580
Conquering C++ This is the first part of C++ (basics of programming and OOP) Contents - PART I Basic C++ 1.) Introduction a.) Evolution of Programming Languages b.) Binary System & Memories c.) Terms used in programming 2.) Simple C++ Programming Your very first C++ program Compilers and how to run a program Data Types of variables o Integer o Floating Types o Double o Character o Boolean type o Data type range o Determining the range o More on Binary numbers Simple C++ programs Identifiers,Keywords and File Nomenclature,Constants,Comments 3.) Operators Arithmetic operator, Escape Sequences Assignment operator, Type Conversion Arithmetic Assignment operators, Relational operators Logical Operators, Unary operator Sizeof operator, Ternary (conditional) operator Operator Precedence and Associativity, Comma Operator, Bitwise Operator Workshop (for chapters 1, 2 and 3) Test Yourself More questions (extra programs) 6.) More Data Types Scope of variables, Storage Classes (auto, static, extern, register) Type Qualifiers (const and volatile), Variable Initialization, Arrays Character Arrays Multi Dimensional Arrays, Initializing Multi Dimensional Arrays, Passing Arrays to functions Structures, Nesting of Structures Enumerated Data Types, Unions, Passing structures to functions Workshop (for chapters 4, 5 and 6) Test Yourself-II More Questions (extra programs) 7.) Pointers Address of operator, Declaration and use of pointer Arithmetic operation on pointers, Pointers and Arrays Pass by value, Pass by reference (C and C++ style) and reference variables More on references Returning pointers from functions Memory Allocation (new and delete) and Memory Heap Pointers to functions, Pointer Declarations Pointers to Structures, Pointer to Pointer (multiple indirection) Pointer to constants and constant pointers, Void pointers Pointers to characters, Pointers Demystified (2-D arrays and pointers) Passing multidimensional arrays to functions, Functions with varying arguments

C++ Basics to Advanced

Embed Size (px)

Citation preview

Page 1: C++ Basics to Advanced

Conquering C++

This is the first part of C++ (basics of programming and OOP)

Contents - PART I Basic C++

1.) Introduction

• a.) Evolution of Programming Languages• b.) Binary System & Memories • c.) Terms used in programming

2.) Simple C++ Programming

• Your very first C++ program • Compilers and how to run a program • Data Types of variables

o Integer o Floating Types o Double o Character o Boolean type o Data type range o Determining the range o More on Binary numbers

• Simple C++ programs • Identifiers,Keywords and File

Nomenclature,Constants,Comments

3.) Operators

• Arithmetic operator, Escape Sequences • Assignment operator, Type Conversion• Arithmetic Assignment operators,

Relational operators • Logical Operators, Unary operator • Sizeof operator, Ternary (conditional)

operator • Operator Precedence and Associativity,

Comma Operator, Bitwise Operator

Workshop (for chapters 1, 2 and 3)

• Test Yourself • More questions

(extra programs)

6.) More Data Types

• Scope of variables, Storage Classes (auto, static, extern, register)

• Type Qualifiers (const and volatile), Variable Initialization, Arrays

• Character Arrays • Multi Dimensional Arrays, Initializing

Multi Dimensional Arrays, Passing Arrays to functions

• Structures, Nesting of Structures • Enumerated Data Types, Unions,

Passing structures to functions

Workshop (for chapters 4, 5 and 6) • Test Yourself-II • More Questions

(extra programs)

7.) Pointers

• Address of operator, Declaration and use of pointer

• Arithmetic operation on pointers, Pointers and Arrays

• Pass by value, Pass by reference (C and C++ style) and reference variables

• More on references • Returning pointers from functions • Memory Allocation (new and delete)

and Memory Heap • Pointers to functions, Pointer

Declarations • Pointers to Structures, Pointer to Pointer

(multiple indirection) • Pointer to constants and constant

pointers, Void pointers • Pointers to characters, Pointers

Demystified (2-D arrays and pointers) • Passing multidimensional arrays to

functions, Functions with varying arguments

Page 2: C++ Basics to Advanced

4.) Controlling Program Flow

• Loops (Iterations) o For loop and its variations o While, Do-while loop

• Decision statement o If...elseif..., Nested If,

Conditional Operator o Switch Case

• Loop Control Statements o Break, Continue, Goto,

Return

5.) Functions

• Declaration, call and definition, Parameters and arguments

• Default Arguments, Returning values from a function, Returning void, The int main( ) function

• An illustration of int main( ) • Types of functions, Using Library

functions (rand, srand and time), Function overloading, An example using functions

• Recursion (calling oneself!), Inline Functions

8.) Classes and Objects

• Object Oriented Programming Concept and OOP Languages

• Class with example • Constructors, Constructor with

Parameters, Overloaded Constructors, Default Constructors and Arrays of Objects

• Scope Resolution Operator, Destructor, Objects and Functions (passing and returning objects), Initialializing an object using an object

• A Practical Use of Classes • Data Encapsulation...Who are we

hiding data from? • More objects and classes, Private

member functions (helper functions)• Friend Function and Friend Classes,

Static Class Members, Constant Objects (and the 'mutable' qualifier)

• Copy Constructor, this pointer, pointer to objects, objects in memory

• Initializer list, explicit keyword, declaration and definition, return value optimization

Workshop (for chapters 7 and 8)

• Test Yourself-III • More Questions (extra

programs)

If you are done with the basics go on to Advanced C++ (PART - II)

Page 3: C++ Basics to Advanced

Programming Languages

The earlier first section was Binary numbers. Some people said that I ought to reduce the math in this tutorial and I felt that perhaps I should start off differently. And so I've altered the sequence of sections in this first unit.

Recap of important computer concepts:

When we talk of computers, we refer to ‘hardware’ and ‘software’. All the physical components like monitor,

keyboard, mouse, modem, printer etc. fall under the hardware category. We need computers to do something

useful for us but computers aren’t smart enough to know what we want them to do (at least not yet!). We need to

explicitly tell the computer our requirements in the form of instructions. A set of instructions for doing

something useful forms a ‘program’. And we refer to these programs as software (i.e. unlike hardware, software

is something that we can’t touch and feel).

Major hardware parts in a computer:

1. Input and Output devices: Computer can receive input from input devices (like a keyboard,

mouse or a scanner). Output devices are used by the computer to send out information to the

user; example: monitor, printer, plotter etc.

2. Arithmetic and Logical Unit (ALU): As the name implies this unit is responsible for all

arithmetic calculations. It is part of the central processing unit.

3. Memory: When our computer wants to work on some information, it will require some place

where it can store data; where it can store the instructions; where it can store intermediate

results of operations etc. Memory serves this purpose and there are different types of memory

(for different requirements):

Primary memory: This memory can be quickly accessed by the computer (in technical jargon we say

that this memory has low access time; i.e. time taken to access data in primary memory is less). Generally,

instructions and data needed by the computer immediately for processing are placed in primary memory. Data in

primary memory is not permanent. Each time we restart the computer, the memory would get refreshed.

Secondary memory/ storage: This is the place where we store all our data. It’s a long-term storage

device (like a hard-disk). Access times are higher but secondary memory is cheaper than primary memory.

3

Introduction

Page 4: C++ Basics to Advanced

4. CPU (Central Processing Unit):

We’ve seen computer parts to get input, store information, display output and also to perform

calculations. But there needs to be someone at a higher level to control the individual units; someone

to decide when to capture the input, when to send output information to the monitor etc. The CPU

takes up this responsibility and is termed the ‘brain’ of the computer. The CPU is also called the

processor (a microprocessor chip).

The language computers understand:

We have a lot of languages in this world but all computers can understand only binary language. The vocabulary

is very simple and small; it consists of only 2 things: 0 and 1. This is all that a computer understands. (Humans

are comfortable with the decimal system - consisting of the numbers 0 to 9). We’ll look at the binary system later

in this chapter.

Remember: A computer finally needs everything in binary language (instructions and data).

Programming Languages:

Programs are written to tell the computer to do something useful for us. It might be as simple a task as

adding two numbers or as complex as transferring data between 2 computers in a network.

There are several reasons why we need programs. Imagine searching through a stack of papers to

search for some telephone bill. Imagine a company maintaining its accounts (expenses and income on

a day-to-day basis) - if this was done using the traditional file and paper method, someone would have

to keep entering all the details in sheets of paper and then stack them into files. If, in future, someone

wants to find out when a particular product was sold they would have to manually read through each

4

Introduction

Page 5: C++ Basics to Advanced

and every paper in each and every file. Computers can be programmed to perform such tasks and

they will do it faster. Basically, the computer is very good in performing repetitive tasks. One point to

note: man created computers and man programs computers to perform certain tasks. A program is a

set of instructions that can be executed by the computer. The computer will obediently follow whatever

the programmer instructs it to do (as long as it understands the instructions). The downside to this is

that the computer does not have any special intelligence; it is only as intelligent as it is programmed to

be. For instance, a robot can be programmed to walk. If it is not programmed to detect obstacles, the

robot will simply bang into obstacles because it does not have the intelligence to recognize and avoid

obstacles. If the programmer provides such provisions, then the robot will be able to handle such

scenarios.

With many software programs already existing, you may wonder why do we need more software; why

not just buy and use what already exists? Software is usually not custom designed for the

requirements of a particular company and you may not even have software for your own special

requirement. For example, dentists never used to have any software specifically for their use but now

you can see a range of dental software (the dentist can maintain details of each of his patients’ tooth

in the computer). Programs needn’t be as complicated as dental software; you may want to have a

little software to calculate your CGPA or to calculate your rank in class. To perform such tasks a

programmer has to write a program (or a set of instructions). These instructions are written in a

specific programming language and the programmer can chose to write the instruction in any one of

the available languages.

Evolution of Programming Languages:

Any programming language can be categorized into one of the following categories:

• High Level • Middle Level • Low Level (machine and assembly languages)

Middle level languages are sometimes clubbed together under the category of high-level languages.

Machine Level Languages:

A computer has a processor and if we want the computer to do something then we need to

direct instructions at the processor. The problem is that computers can understand only binary

language (i.e. the language which comprises of only 1s and 0s). All instructions to the processor

should be in binary and so a programmer can write programs using a series of 1s and 0s. Every

5

Introduction

Page 6: C++ Basics to Advanced

processor will understand only some instructions (this depends on how the processor was designed).

An instruction to increment a variable might be:

110110100011101

Imagine having 100 such instructions in a program! The advantage is that no conversion (or

translation) is needed because the computer can understand the 1s and 0s. This is called machine

level language and this was used at the time computers came into existence.

The drawbacks are quite clear. By using machine language writing programs is very tedious

and also prone to error (even if one bit is wrong, the entire program functionality could get altered).

Trying to identify the error will also be a very tedious job (imagine reading a series of 1’s and 0’s trying

to locate the mistake).

Assembly Languages:

Since it is very difficult for us to write instructions in binary language, assembly languages were

developed. Instead of using a set of 1s and 0s for a particular instruction, the programmer could use

some short abbreviations (called ‘mnemonics’) to write the program (for example: ADD is a mnemonic

to add two numbers). Every processor has an instruction set that describes the various commands

that the processor can understand. A normal instruction set will have instructions like JMP (jump),

ADD (for addition) and so on. A programmer will write the program using these instructions and an

assembler will convert the mnemonics into binary form so that the processor can understand it. The

problem is that assembly level languages are specific to each processor. For example the 8085 (the

simplest microprocessor) has an instruction set different from the 8086 microprocessor. Thus you

would have to rewrite the program for each microprocessor. Another problem with assembly level

programming is that the programmer needs to know details about the processor’s architecture such as

the internal registers where values can be stored, how many internal registers are available for use

etc.

Low level languages are very close to the hardware but difficult for writing programs.

High Level Languages:

Writing larger programs in assembly language is quite difficult and hence, high-level languages were

developed (like BASIC). These languages were not close to the actual computer hardware but were

very close to the English language. They tried to simplify the process of programming. In these

languages the programmer needn’t worry about internal registers and processor architecture;

instructions could be typed in almost normal English. The English-like instructions had to be converted

to machine language using a compiler. One simple example of an English like command in COBOL is:

6

Introduction

Page 7: C++ Basics to Advanced

ADD incentive TO basic GIVING salary.

The instruction is quite self-explanatory (Add ‘incentive’ and ‘basic’ and store the result in ‘salary’).

COBOL is also a high-level language.

As programs grew larger (for example: maintaining a record of all employees, accounts

maintenance etc.), even high level languages had certain drawbacks. These languages are also

referred to as unstructured form of programming. The reason they are unstructured is because when a

large program is written, it becomes very difficult to analyze the program at a later date (by someone

else or even by the original programmer himself). There are many reasons for this; one is the use of

statements like GOTO in high-level languages. In any program some tasks will either be performed

repetitively or the programmer might want the program to execute a different set of instructions if some

condition is satisfied. For example in a division program, in case the denominator is 0 then the

program should not divide the two numbers; instead it should display an error message. Only if the

denominator is not 0 should the program divide the two numbers. This kind of programming is referred

to as program flow control. In a larger program there could be various other possibilities and all this

was dealt with by using the GOTO statement. If you’ve used BASIC you will be aware that for every

instruction that you type, you have to specify a line number (example 10,20,30 and so on). GOTO will

be followed by some line number (indicating the instruction that has to be executed next. Ex: 1020

GOTO 50 means that now you want the program flow to go to line number 50).

The problem with this is that in a large program it will become very difficult for anyone to

understand the program logic. Imagine reading through 100 lines and then finding one GOTO

statement to the 200th line. Then on the 205th line there could be another GOTO statement directing

you to the 150th line. This should make it clear as to what is meant by the term ‘unstructured’. This is a

major problem with high level languages. Another fact is that high level languages are very far away

from the actual hardware. Examples of high-level languages are BASIC, Fortran (Formula

Translation), COBOL (Common Business Oriented Language) and LIST (List processing).

Thus to take advantage of high level and low level languages, middle level languages like C and C++

were developed. They were easy to use and tried to retain the advantages of low level languages (i.e.

being closer to the architecture).

7

Introduction

Page 8: C++ Basics to Advanced

C and C++

Dennis Ritchie developed C and it was quite popular. An interesting feature in C is

the use of functions. The programmer could write a function for checking whether a number

is odd or even and store it in a separate file. In the program, whenever it is needed to check

for even numbers, the programmer could simply call that function instead of rewriting the

whole code. Usually a set of commonly used functions would be stored in a separate file and

that file can be included in the current project by simply using the #include <filename>

syntax. Thus the current program will be able to access all functions available in ‘filename’.

Programs written in C were more structured compared to high level languages and another

feature was the ability to create your own data types like structures. For instance if you want

to create an address book program, you will need to store information like name and

telephone number. The name is a string of characters while the telephone number is an integer

number. Using structures you can combine both into one unit. Similarly there are many more

advantages of using C.

Though C seemed to be ideal, it was not effective when the programs became even more

complex (or larger). One of the problems was the use of many functions (developed by

various users) which led to a clash of variable names. Though C is much more efficient than

BASIC, a new concept called Object Oriented Programming seemed better than C. OOP was

the basis of C++ (which was initially called ‘C with classes’). C++ was developed by Bjarne

Strastroup. In object oriented programming, the programmer can solve problems by breaking

them down into real-life objects (it presented the programmer with an opportunity to mirror

real life). What is an object? This topic is dealt with extensively in the chapter on ‘Objects

and Classes’ but a brief introduction is provided here.

Consider the category of cars. All cars have some common features (for example all cars have

four wheels, an engine, some body colour, seats etc.). Are all cars the same? Of course not. A

Fiat and a Ford aren’t the same but they are called as cars in general. In this example cars will

form a class and Ford (or Fiat) will be an object.

8

Introduction

Page 9: C++ Basics to Advanced

For those people who know C programming, it would be useful to know the differences

between C and C++. Basically C++ includes everything present in C but the use of some C

features is deprecated in C++.

• C does not have classes and objects (C does not support OOP)

• Structures in C cannot have functions.

• C does not have namespaces (namespaces are used to avoid name collisions).

• The I/O functions are entirely different in C and C++ (ex: printf( ), scanf( ) etc. are

part of the C language).

• You cannot overload a function in C (i.e. you cannot have 2 functions with the same

name in C).

• Better dynamic memory management operators are available in C++.

• C does not have reference variables (in C++ reference variables are used in functions).

• In C constants are defined as macros (in C++ we can make use of ‘const’ to declare a

constant).

• Inline functions are not available in C.

Let’s recap the evolution of programming languages: initially programs were written in terms

of 1s and 0s (machine language). The drawback was that the process was very tedious and

highly error-prone. Assembly language was developed to write programs easily (short

abbreviations were used instead of 1s and 0s). To make it even simpler for programmers, high

level languages were developed (instructions were more similar to regular English). As the

complexity of programs increased, these languages were found to be inadequate (because they

were unstructured). C was developed but even that was not capable of dealing with complex

or larger programs. This led to the development of C++.

Note: Sometimes languages are divided into low level and high level only. In such a

classification, C/C++ will come under high level languages.

9

Introduction

Page 10: C++ Basics to Advanced

Why do you need to learn C++?

There are many people who ask the question, "why should I learn C++? What use is it in my

field?" It is a well-known fact that computers are used in all areas today. Programming is

useful wherever computers are used because it provides you the flexibility of creating a

program that suits your requirements. Even research work can be simulated on your computer

if you have programming knowledge. Construction and programming may appear to be miles

apart but even a civil engineer could use C++ programming. Consider the case of constructing

a physical structure (like a pillar) in which the civil engineer has to decide on the diameter of

the rods and the number of rods to be used. There are 2 variables in this case:

1. The number of rods needed (let’s denote it as ‘n’) and

2. The diameter of each rod (let’s call it as ‘d’)

The civil engineer might have to make a decision like: "Is it cost-effective for me to have 10

rods of 5cm diameter or is it better to have 8 rods of 6cm diameter?" This is just one of the

simple questions he may have in his mind. There are a few related questions: "What is the

best combination of number of rods, their diameters and will that combination be able to

handle the maximum stress?"

Usually equations are developed for each of the factors involved. It would be much easier if

the civil engineer could simply run a program and find out what is the best combination

instead of manually trying out random values and arriving at a solution. This is where

programming knowledge would benefit the engineer. Any person can write a good program if

he has adequate knowledge about the domain (domain refers to the area for which the

software is developed. In this case it is construction). Since the civil engineer has the best

domain knowledge he would be able to write a program to suit his requirements if he knew

programming.

Programming is applicable to almost every field- banking (for maintaining all account details

as well as the transactions), educational institutions (for maintaining a database of the

students), supermarkets (used for billing), libraries (to locate and search for books), medicine,

electrical engineering (programs have been developed for simulating circuits) etc.

10

Introduction

Page 11: C++ Basics to Advanced

Binary Numbering System

The following 2 sections on binary numbering system and memory are optional but recommended. If you’ve taken a course in electronics you probably already know about this and can use the material provided here as a refresher. Having knowledge of the binary system and computer memory will help in understanding some features in programming.

The heart of the computer is the microprocessor, which is also referred to as the

processor. The microprocessor is the main part of the computer’s CPU (central processing

unit). A processor consists of millions of electronic switches; a switch can be either in ON

or OFF state. Thus there are only two distinct states possible in these devices. In our real-

world calculations we make use of the decimal system (which has 10 distinct

states/numbers: 0 to 9). Counting up to ten is easy for humans but would be quite difficult

for computers. It is easier to create a device capable of sensing 2 states than one capable of

sensing 10 states (this also helps reduce on errors). Computers make use of the binary

system (i.e. they store data in binary format and also perform calculations in binary format).

The binary system (binary meaning two) has just two numbers: 1 and 0 (which correspond

to the ON and OFF state respectively – this is analogous to a switch which can either be in

ON state or in OFF state).

When we have different systems (binary, decimal etc.), there ought to be a way of converting data from one system to the other. In the decimal system the number 247 stands for 7*100 + 4*101 + 2*102 (add it up and the result will be 247; i.e. in each place we can have one of the 10 digits and to find the actual place value we have to multiply the digit by the corresponding power of 10). For example:

247 = (2 x 102) + (4 x 101) + (7 x 100) = 200 + 40 + 7

1258 = (1 x 103) + (2 x 102) + (5 x 101) + (8 x 100) = 1000 + 200 + 50 + 8

Note: In C++ and most other computer languages, * is used as the multiplication operator.

The same concept holds good for a binary number but since only two states are possible, they should be multiplied by powers of 2.

Remember: A binary digit is called a bit.

11

Introduction

Page 12: C++ Basics to Advanced

So, what is the value of 1101? Multiply each position by its corresponding power of 2 (but remember, you have to start from 20 and not from 21). The value for 1101 is 13 as illustrated in the figure below:

An alternate method to obtain the value is illustrated below (but the underlying concept is the same as above):

It is easy to obtain the values which are written above each bit (27=128, 26=64 and so on).

Write these values on top and then write the binary number within the squares. To find the

equivalent decimal value, add up the values above the square (if the number in the square is

1). If a number is denoted as 1101, then this stands for the lower (or last) four bits of the

binary number (the upper bits are set to 0). Hence 1101 will come under the values 8, 4, 2

and 1. Now, wherever there is a 1, just add the value above it (8+4+1=13). Thus 13 is the

decimal equivalent of 1101 (in binary format). To distinguish between decimal and binary

we usually represent the system used (decimal or binary) by subscripting the base of the

system (10 is the base for the decimal system while 2 is the base for the binary system).

Hence (13)10 = (1101)2

12

Introduction

Page 13: C++ Basics to Advanced

Hence (13)10 = (1101)2

Computers store information in the form of bits and 8 bits make a byte. But memory

capacity is expressed as multiples of 210 bytes (which is equal to 1024 bytes). 1024 bytes is

called a Kilobyte. You may wonder why it is 1024 and not 1000 bytes. The answer lies in

the binary system. Keeping uniformity with the binary system, 210=1024 and not 1000 (the

idea is to maintain conformity with the binary system).

Beware: The bit in position 7 in fig 1.1 is actually the 8th bit of the number (the numbering

of bit starts from 0 and not 1). The bit in the highest position is called as the most

significant bit. In an 8 bit number, the 7th bit position is called the most significant bit

(MSB) and the 0th bit is known as the least significant bit (or LSB). This is because the

MSB in this case has a value of 128 (28) while the LSB has a value of just 1.

13

Introduction

Page 14: C++ Basics to Advanced

Computer Memory

We know that computers can operate only on bits (0s and 1s). Thus any data that has to be

processed by the computer should be converted into 0s and 1s.

Let us suppose that we want to create a text file containing a single word “Hello”. This file has to be

stored physically in the computer’s memory so that we can read the file anytime in the future. For

the time being forget about the file-storage part. Let’s just concentrate on how the word “hello” is

stored in the computer’s memory. Computers can only store binary information; so how will the

computer know which number corresponds to which alphabet? Obviously we cannot map a single

bit to a character. So instead of bits we’ll consider a byte (8 bits). Now we can represent 256

characters. To perform map a character to a byte we’ll need to use some coding mechanism. For

this purpose the ASCII (American Standard Code for Information Interchange) is used. In this

coding system, every alphabet has an equivalent decimal value. When the computer uses ASCII, it

cannot directly use the decimal value and it will convert this into an 8-bit binary number (in other

words, into a byte) and store it in memory.

The following table shows part of the ASCII code.

Character Equivalent decimal value Binary value A 65 0100 0001 B 66 0100 0010 a 97 0110 0001 b 98 0110 0010

In this way each character is mapped to a numeric value. If we type the word hello, then it is converted into bytes (5 bytes- one for each character) based on the ASCII chart and is stored in memory. So ‘hello’ occupies 5 bytes or 40 bits in memory.

Note: It is very important to know the binary system if you want to use the bitwise operators available in C++. The concept of memory is useful while learning pointers.

A question arises, “where are the individual bits stored in memory?” Each individual bit is stored in an electronic device (the electronic device is technically called a flip-flop; which is something like a switch). A single flip-flop can store one bit. Consider the fig. below:

14

Introduction

Page 15: C++ Basics to Advanced

As mentioned earlier we deal in terms of bytes rather than bits. The figure shows a 4-byte memory (which means it can hold 32 bits – each cell can store one bit). All information is stored in memory and the computer needs some method to access this data (i.e. there should be some way of distinguishing between the different memory locations). Memory addresses serve this purpose. Each bit can be individually accessed and has a unique memory address. To access the first byte, the computer will attempt to read the byte stored at memory address 1.

If memories didn’t have addresses then the computer would not know from where it has to read data (or where it has to store data). An analogy to memory address is the postal address system used in real-life. A city will have a number of houses and each house has a unique address. Just imagine the situation if we didn’t have any postal address (we wouldn’t be able to locate any house in the city!). One difference is that a memory address can house only bits and nothing else.

Memory address representations are not as simple as shown above. In the above case we’ve considered a memory that has capacity to store just 4 bytes. Memories usually contain kilobytes or gigabytes of space. As the amount of memory available increases, so does the size of the address (the address number might be in the range of millions). Thus instead of using the decimal system for addresses, the hexadecimal numbering system is used. Hexa means 16 and the distinct numbers in this system are 0 to 9 followed by A, B, C, D, E and F where A= 10 in decimal, B= 11 in decimal and F= 15 in decimal.

Counting in hexadecimal system will be 0…9, A, B…F, 10,11,12,13…19,1A, 1B, 1C…and

so on.

It is quite clear that 10 in hexadecimal does not equal 10 in decimal. Instead, (0F)16 =

(15)10, (10)16 = (16)10 and (11)16 = (17)10

Four bits form a ‘nibble’. Eight bits form a ‘byte’ and four bytes of memory are known as a

‘word’. There is some significance attached to a ‘word’ which we shall deal with later.

15

Introduction

Page 16: C++ Basics to Advanced

Another term related to memory is ‘register’. A register consists of a set of flip-flops (flip-flops are electronic devices that can store one bit) for storing information. An 8-bit register can store 8 bits. Every processor has a set of internal registers (i.e. these registers are present within the processor and not in an external device like a hard disk). These registers (which are limited in number depending on the processor) are used by the processor for performing its calculations and computations. They usually contain the data which the processor is currently using. The size of the register (i.e. whether the registers will be 16-bit, 32-bit or 64-bit registers also depends on the processor). The common computers today use 32-bit registers (or 4-byte registers). The size of a register determines the ‘word-size’ of a computer. A computer is more comfortable (and prefers) working with data that is of the word-size (if the word-size is 4 bytes then the computer will be efficient in computations involving blocks of 4 byte data). You’ll understand this when we get into data types in the subsequent chapters.

The different types of memory are:

1. Secondary storage (for example the hard disk, floppy disks, magnetic disks, CD-ROM) where one can store information for long periods of time (i.e. data is retained in memory irrespective of whether the system is running or not).

2. The RAM (random access memory) is used by the computer to store data needed by programs that are currently running. RAM is used for the main (primary) memory of the computer (all programs which are executed need to be present in the main memory). The RAM will lose whatever is stored in memory once the computer is switched off.

3. ROM (read only memory): This contains instructions for booting up the system and performing other start-up operations. We cannot write to this memory.

4. The internal registers within the processor- these are used by the computer for performing its internal operations. The compiler will decide what has to be stored in which register when it converts our high-level code into low-level language. As such we won’t be able to use these registers in our C++ code (unless we write assembly code).

Remember: Secondary storage (also called auxiliary memory) is not directly accessible by the CPU. But RAM is directly accessible (thus secondary memory is much slower than the primary memory). For a program to execute it needs to be present in the main memory.

Which memory has lowest access time (or which memory can the CPU access quickly)?

The internal registers can be accessed quickly and the secondary storage devices take much longer to access. Computers also make use of a cache-memory. Cache memory is a high-speed memory which stores recently accessed data (cache access is faster than main memory access).

16

Introduction

Page 17: C++ Basics to Advanced

Related concept: In general cache means storing frequently accessed data temporarily in a place where it can be accessed quickly. Web browsers tend to cache web pages you visit frequently on the hard disk. Generally when we type in a website address, the browser needs to query the website and request for the page; which is a time consuming process. When the browser displays this webpage, it internally caches (stores a copy) this webpage on our hard disk also. The next time we time the same website address, the browser will directly read out from the hard disk rather than query the website (reading from hard disk is faster than accessing a web server).

Remember: Computers store information using the binary system, addresses are

represented in the hexadecimal system and in real-life we use the decimal system.

A 3-bit number can be used to form 8 different combinations (including the 000

combination).

Binary Decimal Equivalent

000 0

001 1

010 2

011 3

100 4

101 5

110 6

111 7

17

Introduction

Page 18: C++ Basics to Advanced

If 3 bits are used then the maximum possible decimal number that can be represented is 7 (not 8 because the first number is 0). Similarly if an 8-bit number can be used to represent up to (2^8) different values (0 to 255 in the decimal system).

A binary number can be either signed or unsigned. When a number is unsigned (we don’t

bother about the sign), it means that the number is always positive. If a number is signed,

then it could be positive or negative. +33 is a signed number (with a positive sign). In real

life, we can use + or – to indicate whether a number is positive or negative but in computers

only 1s and 0s can be used. Every decimal number has a binary equivalent. The binary

equivalent for the decimal number 8 is 00001000. If this is a signed number then +8 would

be written as 00001000 and –8 would be denoted by 10001000. Notice the difference

between the two. The 7th bit (or the most significant bit) is set to 1 to indicate that the

number is negative.

Assume the number 127.

For +127 you will write: 01111111

For –127 you will write: 11111111

For 255 you will write: ?

Well, the value for 255 cannot be written using a signed 8-bit number (because the 7th bit is reserved for the sign). If the unsigned representation was used then 255 can be represented as 11111111 (in this case the MSB signifies a value and is not concerned about the sign of the number).

What is the point to note here? By using a signed representation the maximum value that

can be represented is reduced. An 8 bit unsigned binary number can be used to represent

values from 0 to 255 (11111111 will mean 255). On the other hand, if the 8 bit binary

number is a signed number then it can represent from –127 to +127 only (again a total of

255 values but the maximum value that can be represented is only 127).

Beware: Signed representation in binary format will be explained in detail later. Computers store negative numbers in 2s complement rather than storing them directly as shown above.

Remember: In signed numbers the MSB (in the binary representation) is used to indicate

the sign of the number.

18

Introduction

Page 19: C++ Basics to Advanced

Terms used in C++

• Compiler :

Before providing a technical explanation a compiler in layman’s language is a

program which will read through the C++ program code (code refers to the program written

by the programmer) line by line. The compiler will read each and every character that has

been typed in the program code. After reading the entire code, the compiler will convert the

code into machine level format (i.e. 0s and 1s). The compiler is very dedicated and hard

working since it will meticulously read each and every character.

Technically speaking, the microprocessor in a computer can understand only binary

numbers (only 0s and 1s). It is not possible for us to write programs in binary numbers.

Compilers will convert the coding for a program into machine understandable form (i.e. it

will convert the instructions we type into machine understandable form). The instructions

we type (in something similar to English), is known as the source code. The compiler

converts the source code into the object code (which the microprocessor understands and we

cannot understand). There is a detailed discussion on this topic in the chapter “Advanced

Topics”.

If you are new to programming you might wonder as to where you should type your C++

program?

First of all you should have a C++ compiler. There are many compilers available for free on

the Net and they can be downloaded from the Net. After downloading the compiler, create a

new C++ source file and type the C++ code in the compiler. Later on in this book, we shall

see how to run a C++ program in the compiler.

A few compilers available on the Net are :

1. Borland C++ compiler : www.borland.com/bcppbuilder/freecompiler/ 2. Bloodshed Dev C++ compiler : www.bloodshed.net/devcpp.html 3. DJGPP C++ compiler 4. For Linux/Unix the GNU C++ compiler is available in standard installations of the

operating system.

19

Introduction

Page 20: C++ Basics to Advanced

Most compilers nowadays provide an Integrated Development Environment (IDE), i.e. they have an editor, a compiler, a linker and some extra tools (like a debugger). In schools and colleges the most commonly used compiler is Turbo C++. Most of the programs written in this book are compatible with Turbo C++. It is better to use Visual C++ because advanced features like namespaces and exception handling are not available in older versions of Turbo C++. All compilers will support the basic features of C++.

If you are using Linux/Unix then you will be having the GNU C++ compiler in your Linux distribution CD (this compiler is freeware but it doesn’t have a graphical user interface- it can only be used from the command line; refer to the Appendix section).

• Keywords :

Keyword is a word that the compiler already knows, i.e. when the compiler sees a keyword somewhere in the program it knows what to do automatically.

For example, when the compiler encounters the keyword ‘int’, it knows that ‘int’ stands for an integer. Or if the compiler reads a ‘break’, then it knows that it should break out of the current loop.

• Variable and Constant :

As the name suggests, a variable is something whose value can be changed throughout the program. It is not fixed. On the other hand, a constant is one whose value remains the same (constant) throughout the program.

• Operators and Operands:

Operator is something that performs operation on a variable. For example + is an operator that performs the addition operation. The terms on which the operator operates are known as the operands. In the expression

x + y

x and y are known as the operands and + is the operator.

20

Introduction

Page 21: C++ Basics to Advanced

• Expression:

Expression performs an operation and consists of operators and operands (or variables). a>b This is an expression in which a and b are the operands. > is the operator.

• Binary and Unary:

Binary means ‘two’ and ‘unary’ means one. A binary operator operates on two operands (or two values) whereas a unary operator operates on just one operand.

• Definition and Declaration :

In C++ these two terms are used frequently. Declaration of a variable means telling the compiler that this variable is an integer, or this variable is a character. A declaration will inform the compiler about the type of a variable. Defining a variable means allocation of memory space by the compiler for that particular variable.

In the case of variables, a single statement performs both declaration and definition. For example: the statement

int x;

declares and defines ‘x’ as an integer.

Remember that declaration is just declaring the type of something (telling the compiler what data type it belongs to) but definition means allotting space to it. It may seem as if both are one and the same; in many cases they are but in a few places they are not (these cases will be dealt with later).

• Initialization :

Initialization refers to the first (initial) assignment of a value to a variable. x = 50; This initializes the value of x to 50. Always remember that the value to the right of the equal sign will be assigned (or stored) to the value on the left of the equal sign.

For example: a = b;

This means that value of b is assigned to a.

21

Introduction

Page 22: C++ Basics to Advanced

• Parentheses, braces, angle and square brackets :

There are different type of brackets:

• Parentheses are just the normal brackets ( ). • Braces are { }. • Angular brackets are < >. • Square brackets are [ ].

All of these brackets are used in C++ but each one is used for different purposes.

• Character:

Characters include alphabets, numbers and other special symbols as well (like *, /, - etc…).

Usually one tends to think of characters as only alphabets; but remember a character could

be anything (an alphabet, a number, a special symbol on your keyboard or even a blank

white space is a character).

• Syntax :

Every language has its own ‘syntax’. By the term syntax we refer to the

structure of a language and use of correct language. English has its own set of rules while

French has slightly different rules. The vocabularies of both the languages are also different.

Programming is also another language (that’s it is called programming language). Just like

any other language it has its own set of rules that must be complied with. This is known as

the syntax. Only if the programmer adheres to the syntax will the compiler understand the

instructions.

• Errors:

This is a very common term in programming languages. In fact it is very

common to hear someone say, "Syntax Error". Error means a mistake and syntax error

means that there is a mistake in the syntax. The compiler reads the entire program before

execution. While reading the program, if the compiler discovers that it can’t understand

22

Introduction

Page 23: C++ Basics to Advanced

some instruction it will display an error message. Till the error is cleared, the program

cannot be executed. The compiler will give a syntax error if the syntax has not been adhered

to properly. Errors generated by the compiler are known as compile-time errors.

There are other types of errors as well. The compiler is not intelligent to detect all types of

errors. For instance if the programmer make a logical error, the compiler will never know it.

Suppose a program is written for adding two numbers and in the coding, instead of the +

operator, the – operator has been used. The compiler will not recognize any error during

compilation and the program will execute (but the programmer will not get the desired

output). This is a logical error and as a programmer you have to be very careful while

coding. The compiler is not brilliant enough to know what is in the programmer’s mind!

• Executable file and linkers:

Executing refers to the process of running a program. We’ve talked about object file, but

can we execute object files directly? The compiler produces an object code but this object

code generally depends on some other external files. For example: if you are using complex

mathematical functions, you might make use of functions which have been defined by some

other programmer. That programmer would have created a library which you can include in

your source code (instead of creating those functions in your program). A library is a

collection of object files. Thus your program will actually depend on the other object files

for some of the complex mathematical functions which you’ve used. Someone now has to

link up all these object files to produce an executable file. This work is done by the ‘linker’.

Most modern compilers have a linker included in them. Linkers are discussed in detail later.

Remember: Each C++ source code needs to be compiled separately to produce an object code (ten source codes mean that we’ll have ten object codes). Finally the linker has to combine these ten object codes to produce one single executable module.

• Debugging:

is the process of troubleshooting a program (of course it’s done when the program doesn’t work up to your expectation!). Debugging is the process of identifying bugs in the code. A bug is a programming term for an ‘error’.

23

Introduction

Page 24: C++ Basics to Advanced

Some extra details:

An interesting point (about Kilo, Mega etc.):

You might have read that 1Kilobyte = 1024 bytes and perhaps you thought about it as well, “Shouldn’t 1Kilobyte = 1000 bytes?”

Let’s start with our familiar decimal system. In the decimal system, 1 Kilo unit = 1000 units.

For example:

• 1 Kilometer = 1000 meters = 103 meters • 1 Kilogram = 1000 grams = 103 meters • 1 Megawatt = 1000 Kilowatts = 1000000 watts = 106 watts

This is fine as far as the decimal system is concerned. Here the base used is 10 and we express everything in powers of 10.

Computers speak in binary language and the base used here is 2. Let’s extend the concept of ‘kilo’ to the binary system.

Can we represent the value 1000 as a power of 2? Let’s try:

Power Value 20 0 21 2 22 4 23 8 24 16 25 32 26 64 27 128 28 256 29 512 210 1024

Oops! We’ve just exceeded 1000 in 210. In the binary system, the closest we come to 1000 is 1024. Thus 1 Kilobyte = 1024 bytes (but for rough calculations we assume it as 1000 bytes).

24

Introduction

Page 25: C++ Basics to Advanced

Recap of the First Unit:

¤ Computers only understand binary language (1s and 0s).

¤ 8 bits form a byte.

¤ The different types of storage are: internal CPU registers, RAM, ROM and secondary storage.

¤ RAM is used as main memory and data is present only as long as the computer is switched on.

¤ Main memory is directly accessible by the computer whereas secondary memory is not.

¤ During execution programs should be present in the main memory.

¤ Word-size of a machine depends on the size of the processor’s internal registers.

¤ Machine level language is written in 1s and 0s.

¤ Assembly level languages make use of mnemonics (mnemonics are abbreviated English words used to represent instructions).

¤ High level languages like BASIC are in simple English but they are far away from the real hardware of the system.

¤ C has the advantages of both high level and low level languages. But it doesn’t support Object Oriented Programming (OOP).

¤ C++ is actually C with classes (i.e. it supports OOP).

¤ A compiler is required to convert the source code (i.e. the instructions we type in English) into the object code (i.e. the machine language which the computer understands).

¤ 1 kilobyte = 1024 bytes (and not 1000 bytes).

25

Introduction

Page 26: C++ Basics to Advanced

Your very first C++ Program

All the example programs in this book have been tested in Turbo C++ compiler (the programs were also testedin Visual C++ 6.0). They should run on other C++ compilers as well.

Let us start with a basic C++ program. This will give you an idea about the general structure of a C++ program.Let’s write a program in which the user can enter a character and the program will display the character that was typed:

Explanation for the above program:

// My first C++ program: The first line in the above program forms the ‘comments’. In C++, any line that is typed after the two slashes ( // ) is known as comments. The compiler does not read comments. Comments can be used anywhere in a program and you can write anything that you want in the comments. Comments are useful when a programmer (or someone else) reads the source code in the future. Proper comments will make it easier for anyone to understand the program logic. When you write a very long program, you will appreciate the use of comments.

# include <iostream.h : Next comes the pre-processor directive. Preprocessor directives are instructions meant specifically for the preprocessor. Before compiling a program, the preprocessor will scan the program and perform text substitutions in the source code. The preprocessor directive usually comes at the beginning of the program (all directives for the preprocessor start with the # symbol). *.h files are known as header files. There are many other header files like: conio.h, graphics.h etc. There are certain operations that you will frequently use in a C++ program. Defining these operations in each program is a cumbersome process and leads to more lines of code. Thus these standard operations have been written in header files which a programmer can include in his coding (rather than coding everything again). We can also create our own header files (we’ll look at this later). When the preprocessor encounters #include <iostream.h>, it will physically include the iostream header file in your source code (i.e. the entire file called iostream.h file will be pasted in your source code). This is one of the standard header files which you’ll use in almost all of your C++ programs.

int main( ) : Every C++ program has to have one ‘main’ function. Functions will be explained later.

// My first C++ program

# include <iostream.h> int main( ) {

char letter; cout << "Enter any letter" ; cin >>letter; cout << "The letter you entered is : " <<letter; return 0;

}

26

Data Types & Variables

Page 27: C++ Basics to Advanced

Remember that the compiler will execute whatever comes within the ‘main’ function. Hence the instructions that have to be executed should be written within the ‘main’ function. Just as the name implies, ‘main’ is the main part of the C++ program and this is the entry point for a C++ program.

{ : Functions are defined within a block of code. Everything within the opening and closing braces is considered a block of code. ‘main’ is a function and hence we define this function within braces. This is as good as telling the compiler, “The ‘main’ function starts here”.

char letter; : ‘letter’ is the name of a variable. Instead of the name ‘letter’ we could also use any other name. ‘char’ defines ‘letter’ as a character variable. Therefore, the variable ‘letter’ will accept the value of only one character.

cout << "Enter any letter" ; : This is followed by the << operator (known as the insertion operator). Following this operator, anything typed within double quotes will be displayed on the screen. Remember that cout and << go together. ‘cout’ is known to the compiler already (since it is defined in the iostream header file which has been included). So when the compiler comes across cout<<, it knows what to do.

cin >>letter;: cin is the opposite of cout. cin>> is used to obtain the value for a variable from the user. (>> is known as the extraction operator). Input is usually obtained from the keyboard.

return 0; : This statement tells the compiler that the ‘main’ function returns zero to the operating system (return values will be discussed in the chapter on functions).

}: The closing brace is used to tell the compiler that ‘this is the end of the function’. In our case, it is the end of the ‘main’ function and this indicates the end of the program as well.

You might have noticed in the above program that every statement is terminated with a semi-colon (;). That is exactly the use of the semi-colon. It is used to tell the compiler that one instruction line is over. If you don’t put semi-colons in your program, you will get errors while compiling.

A variable is used for temporary storage of some data in a program. In the above example, ‘letter’ was a variable. Every variable belongs to a particular type (referred to as data type). The different data types in C++are discussed later. In the above program:

char letter;

declares ‘letter’ as a character variable (a character is one of the basic data types in C++). When the compiler encounters this statement, it will allocate some memory space for this variable (i.e. any value which is assignedto the variable ‘letter’ will be stored in this allocated memory location). When the required memory space has been allocated we say that the variable has been defined (in this case a single statement will declare and definethe variable ‘letter’).

Some points to remember:

When we want to display something on the screen we will code it as:

cout<<variable-name;

When we want to obtain a value for a variable from the user we will say:

cin>>variable-name;

27

Data Types & Variables

Page 28: C++ Basics to Advanced

Beware of the direction of the >> and << operators. In the statement

cout<<variable;

information flows from the variable into ‘cout’ (which means it goes to the monitor display). When we say

cin>>variable;

information (the value of the variable) flows from ‘cin’ (which would be the keyboard) into the variable (i.e. thevalue is stored in the variable). Information will flow in the direction of the arrows (<< or >>). ‘cout’ is linked to the standard display device (i.e. the monitor) while ‘cin’ is linked to the standard input device (i.e. the keyboard). Hence, you can’t use

cout>>variable;

This will cause an error while compiling. ‘cout’ and ‘cin’ are already pre-defined and so you can use them directly in your programs. 'iostream.h’ is a header file that is used to perform basic input and output operation(or general I/O like using ‘cin’ and ‘cout’).

Remember: C++ is a case-sensitive language, which means that the compiler will not consider ‘letter’, ‘LETTER’ and ‘Letter’ as the same.

28

Data Types & Variables

Page 29: C++ Basics to Advanced

How to run your first program in the compiler?

Saving and compiling the program:

There are many C++ compilers available in the market. Some of them are freeware (meaning thatthey are free to use) while others have to be paid for. Turbo C++ compiler might be the simplest to use (but it isnot freeware). Simply choose "New File" and type out the program coding. Suppose you are using some othercompiler, click on File and choose "New". Some compilers may have the option of creating a C++ source filewhile other compilers may require a new project to be created. Whatever the method you will ultimately cometo the step of creating a C++ source file. After typing the code in the compiler, save the file by giving it somename. The "Save As" option will appear under the "File" menu. Give a name (for example: first). Now the fileis saved as first.cpp. All C++ source files are saved in *.cpp format. Just like *.doc represents a Word documentfile, *.cpp denotes a C++ (C Plus Plus) source file.

In the compiler program there will be an option called ‘Compile’ in the menu bar. Select the compile option and the compiler will do its work. It will compile the program (or in other words, it will read whatever has beentyped) and in case there are any errors, the compiler will point out the line where the error was detected. Checkwhether the program has been typed exactly as given earlier. Even if a semi-colon is missing, it will lead to errors. If the compiler says no errors (or if the message "compiled successfully" appears), then you can go to thenext stage: building the *.exe file.

*.exe file extension stands for executable files. A *.cpp file cannot be run directly on the computer. This has tobe converted into a *.exe file and to do so select the "Make or Build exe" option in the compiler. The file‘first.exe’ will be created. Now the program can be executed from the DOS prompt by typing ‘first’. But instead of running the program every time from DOS, it will be convenient to run the program from the compiler itselfand check the output. In the compiler there will be another option called "Run". Just click on this and theprogram will run from the compiler itself (you needn’t switch back and forth between DOS and the compilerscreen).

The figure should give you a rough idea as to how the executable file is created from the C++ source code.

29

Data Types & Variables

Page 30: C++ Basics to Advanced

The source code is the program that you type. Source code is converted into object code by the compiler. Thelinker will link your object code with other object codes. This process of creating a C++ program is discussed inthe last chapter.

Modification that might be needed in first.cpp (depending on your compiler):

A little problem might be encountered while running your program from the compiler (this problem will exist inTurbo C++ compiler). While running the program from the DOS prompt, this problem will not occur.

What’s the problem? When first.cpp is executed from the compiler the program will ask the user to enter acharacter. Once a character has been entered, the program will return to the compiler screen. You won't see youroutput! It might appear as if there is some problem with the program. What happens is that the program displaysthe character on the screen, immediately terminates the program and returns to the compiler screen. Thishappens so fast that you can’t see the output being displayed.

30

Data Types & Variables

Page 31: C++ Basics to Advanced

Modify your program as shown below (if you are using Turbo C++):

Another header file called conio.h has been added. The function getch( ) is used at the end of theprogram. getch ( ) is defined in the conio.h header file. getch( ) stands for ‘Get Character’. When getch ( ) is executed, the program will wait till the user types a character (any character). Only after a character is typedwill the program move to the next instruction. In the above program, there is no statement other than return 0;after getch ( ). Hence program flow is as follows:

The program asks the user to enter a letter. The user enters a letter. Then the program displays the typed letteron the screen. The program will not quit at this point since there is a statement getch ( ). It will wait till the usertypes another character. When the user presses a character the program will quit.

Type the above program, save and run it from the compiler. Some compilers have named the function getch( )as _getch( ). This function also requires the conio.h header.

Remember: conio.h is not available with all compilers and getch( ) function is not a standard C++ function. Some compilers provide it but most do not. In other compilers if a similar problem is encountered whiledisplaying the result try using

getchar( );

instead of

getch( );

getchar( ) function doesn’t require the conio.h header file. If the compiler does not recognize getchar( ), then instead of adding any of the in-built functions, just add another line as follows:

cin>>letter;

at the end of the program just before return 0;

// Your first program modified: first.cpp

# include <iostream.h> # include <conio.h> int main( ) { char letter; cout<< "Enter any letter" ; cin>>letter; cout<< "The letter you entered is : " <<letter; getch( ); return 0; }

31

Data Types & Variables

Page 32: C++ Basics to Advanced

The program flow will be the same as described earlier.

The latest compilers, like VC++ (Microsoft Visual C++ compiler) do not have any of the above problems even if the program is run from the compiler. VC++ will always ask the user to press a character to terminate the program.

Another alternative is to use a function called ‘system’, which is defined, in the header file: stdlib.h.

system("PAUSE");

can be used to pause the program (i.e. execution of the program will continue only when the user presses a key).

system("CLS");

can be used to clear the display screen. To use these two functions you have to type #include <stdlib.h> headerfile in your source code. The system( ) function actually executes a DOS command. (Try giving the commands ‘cls’ and ‘pause’ in your DOS prompt).

32

Data Types & Variables

Page 33: C++ Basics to Advanced

Data Types

The following topics are covered in this section:

Introduction

Integer

Floating Type

Double

Character

Boolean

Data Type Ranges and determining the ranges

More on Binary Numbers

Every piece of data has to belong to some basic category. Consider a simple example in real life: every number has to beof a particular type. The number 5 is a natural number (or it can be called as a whole number). 6.5 is a real number (it has a decimalpoint). Similarly, in programming we have what are called as data types. When a variable is declared, the programmer has to specifywhich data type it belongs to. Only then will the compiler know how many bytes it should allocate for that particular variable. Or inother words, each data type occupies a different memory size and if a variable is declared as belonging to one particular data type itcannot be assigned a different data type value. In simpler terms, suppose the variable ‘x’ is declared such that it can hold only whole numbers; then it cannot (and should not) be assigned some alphabet.

There are two categories of data types: fundamental data types and user-defined data types. The second category of data types will be dealt with later.

The fundamental (or built-in or primitive) data types are:

Integer Floating Point Character Double Bool

The first three data types: integer, floating point and character are used frequently.

Integer (int):

An integer can contain only digits (numbers) from 0 to 9. Examples of integers are:

0 10 345 6789

33

Data Types & Variables

Page 34: C++ Basics to Advanced

-23 -600

It includes positive and negative numbers but the numbers have to be whole numbers. It does accept the decimal point. Hence the following numbers are not integer data types:

3.5 4.8 0.23

These numbers come under the second category (floating point type) and not under integers. If the program has to accept such valuesfrom the user do not declare the variable as an integer. If a variable is declared as an integer and the user enters a value of 2.3, theprogram will assign 2 as the value for that integer variable. Similarly, if the user enters 3.2, the program will assign 3 to the integervariable.

Remember: Once a variable is declared as an integer, it will only store whole numbers (if the user types a value with the decimal point, the program will ignore everything that is typed after the decimal point).

How to declare a variable as belonging to the type integer? The syntax is:

int variable-name;

Each data type occupies a certain amount of memory space. An integer will occupy 2 bytes of memory (which means 16 bits). Fromthis it is possible to calculate the maximum and minimum values that an integer can store. 2^16 = 65536 (hence 65536 differentcombinations of 16 bits are possible). Divide this by 2 because integers (by default) range from negative to positive values. We have a0 in between and so subtract one from this to get 32,767. Hence an integer can take values from –32,768 up to +32,767 (a total of65536 different values).

A natural question springs to mind, "What would happen if a value greater than 32,767 is entered?" Since this value cannot beaccommodated within the allocated two bytes, the program will alter the value. It’s not exactly altering the value; it will basically change your value into something different. The user might enter 123456 as the integer value but the program will store it as –7623 or something like that. Whenever you use variables ensure that you have declared them as belonging to the correct data type.

This restriction on maximum range might seem to be a problem. In C++ ‘qualifiers’ can be used to vary the range of fundamental data types. Qualifiers are only supplements to the basic data types and they cannot be used separately on their own. They work only with abasic (or fundamental) data type. The 4 qualifiers available in C++ are:

1. Short 2. Long 3. Signed 4. Unsigned

Signed and unsigned integers were discussed in the first chapter. When an integer is specified as signed, then automatically the mostsignificant bit of the number is used as a sign bit (to denote the sign of the number). Hence it can be used if the programmer needspositive and negative number values for the variable. By declaring a variable as an integer, by default you can specify both positiveand negative values. By default an integer is a signed integer. In other words,

int variable-name;

is the same as

signed int variable-name;

In the second form, ‘signed’ is the qualifier and it is used to explicitly state that the variable is a signed integer. For an unsigned integer the syntax will be:

unsigned int variable-name;

34

Data Types & Variables

Page 35: C++ Basics to Advanced

An unsigned integer can hold a value up to 65,535 (a signed integer can hold only up to 32,767). Of course, in an unsigned integer you cannot assign a negative value. The range is from 0 to 65,535. To go beyond 65,535 and make use of both positive and negative values as well, the qualifier long should be used.

long int variable-name;

Long integers occupy 4 bytes of memory (32 bits). Remember, long int actually means signed long int (you can give positive and negative values).

If you specify

unsigned long int variable-name;

you can only assign positive values to the variable. Thus, two qualifiers can be used together with a basic data type.

What about the ‘short’ qualifier? Short integer is the same as a signed integer. It occupies two bytes and has the same range of positive and negative values as the normal integer case.

int x;

is usually the same as

short int x;

Compilers (depending on the operating system) will assume ‘int’ as a ‘long int’ or a ‘short int’. VC++ (since it works in the Windows OS) will default to ‘long int’ if you specify a variable as type ‘int’ (i.e. it will allocate 4 bytes to an ‘int’ variable). Turbo C++ (which is a DOS based compiler) will default to ‘short int’ when you specify a variable as type ‘int’. Thus the statement:

int var;

will allocate ‘var’ 4 bytes if you are using VC++ but the same statement will allocate 2 bytes if you are using Turbo C++ compiler.

Programmers sometimes prefer to explicitly state what type of integer they want to use by making use of the ‘short’ and ‘long’qualifiers. ‘short int’ always occupies only 2 bytes (irrespective of whether the OS is Windows or DOS) while a ‘long int’ always occupies 4 bytes.

Two qualifiers can be used together, but do not try using:

short long int variable-name;

This will cause a compile-time error. So be careful with what qualifiers you use. And remember that the default for int is equivalent to short signed integer.

Floating Types (float):

Floating type data include integers as well as numbers with a decimal point. It can also have an exponent. Exponent means 10 to thepower of some integer value (whole number). 20000 = 2 x 10^4 = 2e4 = 2E4.

If you specify decimal numbers, floating point data type will store up to a precision of 6 digits after the decimal point. Suppose0.1234567 is assigned to a floating-point variable, the actual value stored would be 0.123457 (it will round up to the sixth digit after the decimal place). Valid floating-point numbers are:

0.1276 1.23 1.0 10.2

35

Data Types & Variables

Page 36: C++ Basics to Advanced

2e5 (this will be typed in your code as 2e5)

Do not use an exponent with a decimal point. For example: 2e2.2 is an invalid floating point because the exponent has to be an integer.Floating point numbers use 4 bytes of memory and has a much greater range than integers because of the use of exponents. They canhave values up to 10^38 (in positive and negative direction). The same qualifiers used for an integer can be applied to floating pointnumbers as well. To declare a floating variable, the syntax is:

float variable-name;

Double (double):

This is similar to the floating-point data type but it has an even greater range extending up to 10308. The syntax to declare a variableof type double is:

double variable-name;

Beware: Visual C++ (VC++) usually uses its default as ‘double’ instead of ‘float’. Suppose we type:

float x=31.54;

you will get a warning message saying that a ‘double’ (i.e. 31.54) is being converted into a floating point. It is just to warn you that you are using a ‘float’ and not a ‘double’. (Even if there are warnings, there won’t be any problem in running your program).

Character (char):

A character uses just one byte of memory. It can store any character present on the keyboard (includes alphabets and numbers). It cantake numbers from 0 to 9 only. The following are valid characters:

A B 3 a : ‘ /

If the number 13 is entered as the value for a character, the program will only store 1 (i.e it will store the first character that itencounters and will discard the rest). A character is stored in one byte (as a binary number). Thus whatever the user enters isconverted into a binary number using some character set to perform this conversion. Mostly all computers make use of the ASCII(American Standard Code for Information Interchange). For example, according to the ASCII coding, the letter ‘A’ has a decimal value of 65 and the letter ‘a’ has a value of 97.

There is another form of coding called the EBCDIC (Extended Binary Coded Decimal Information Code) which was developed byIBM and used in IBM computers. However, ASCII remains the most widely used code in all computers. The table at the end of thebook gives a listing of the ASCII values and their equivalent characters. The syntax to declare a variable which can hold a character is:

char variable-name;

Boolean Type (bool)

36

Data Types & Variables

Page 37: C++ Basics to Advanced

This data type will only accept two values: true or false. In C++, ‘true’ and ‘false’ are keywords. Actually a value of true corresponds to 1 (or a non-zero value) and a value of false corresponds to 0.

Remember: The size of data types given above is a general case. Data type sizes depend on the operating system. So it may vary from system to system.

Data Type Ranges

Remember: ‘short int’ always occupies only 2 bytes (irrespective of whether the OS is Windows or DOS) while a ‘long int’ always occupies 4 bytes.

Determining the range:

You might be wondering why the ranges are from -128 to 127 or from -32768 to 32767? Why not from –128 to 128? We’ll take the case of signed characters to understand the range. A character occupies one byte (8 bits). In a signed character the MSB (i.e. the 7th bit is used to denote the sign; if the MSB is 1 then the number is negative else it is positive). The binary number: 0111 1111 represents +127 in decimal value (we’ve seen about conversion from binary to decimal numbers in the first chapter). The number 0000 0000 represents 0 in decimal. But what about 1000 0000? Since the MSB denotes a negative number, does this stand for–0? Since there is no point in having a –0 and a +0, computers will take 1000 0000 as –128. 0000 0000 is taken to be zero. Thus in the negative side we have a least value of –128 possible while in the positive side we can have a maximum of only +127.

#include <iostream.h> int main( ) { bool check; check = true; cout<<check; //output will be 1 (since ‘check’ is true) return 0; }

Name

Bytes

(Compiler dependent)

Description Range (depends on number of bytes)

char 1 Character or 8 bit integer. signed: -128 to 127 unsigned: 0 to 255

bool 1 Boolean type. It takes only two values. true or false

short 2 16 bit integer. signed: -32768 to 32767 unsigned: 0 to 65535

long 4 32 bit integer. signed:-2147483648 to 2147483647

int 2/4

Integer. Length depends on the size of a ‘word’ used by the Operating

system. In MSDOS a word is 2 bytes and so an integer is also 2

bytes.

Depends on whether 2/4 bytes.

float 4 Floating point number. 3.4e + / - 38 (7 digits)

double 8 double precision floating point number. 1.7e + / - 308

37

Data Types & Variables

Page 38: C++ Basics to Advanced

Another point to note is that negative numbers in computers are usually stored in 2’s complement format. For example: 1000 0001 actually stands for –1 but if this number is stored in 2’s complement format then it stands for –127. Similarly 1000 0010 would appear to be –2 but is actually –126. To understand 2’s complement you should know about 1’s complement. Let us say we have a signed binary number: 1000 0001. The 1’s complement of this number is 1111 1110 (i.e. to find the 1’s complement of a binary number just change the 1s to 0s and 0s to 1s but do not change the sign bit). To find the 2’s complement just add 1 to the 1’s complement. Hence the 2’s complement of 1000 0001 is 1111 1111 (or –127). In the same way the 2’s complement of 1111 1111 (-127) is 1000 0001 (or –1). Thus the computer instead of storing 1111 1111 (for –127) will store the 2’s complement of the number (i.e. it will store 1000 0001).

Remember: When converting a number to its 2’s complement leave it’s sign bit unchanged. And numbers are stored in 2’s complement format only if they are negative. Positive numbers are stored in the normal format.

More on Binary Numbers:

Everything in the computer is stored in binary format. Even if we ask the computer to store an octalnumber or a hexadecimal number in a variable, it will still be stored in binary format (we’ll see this later) because computers only understand 0s and 1s. How does a computer perform calculations? Yes, it has to do it inbinary format. Let’s take an example of binary addition:

0 1 1 0 1 0 1 0 (106)10

0 0 1 1 0 1 0 0 (52)10

-----------------------------------------------------------------

1 0 0 1 1 1 1 0

First of all, what 2 decimal numbers are we adding? Convert them to decimal and you’ll get: 106 and 52. When the computer has to add 106 and 52, it would in effect be adding the 2 binary numbers as shown above. Binaryarithmetic is simple:

0 + 0 = 0

1 + 0 = 1

0 + 1 = 1

1 + 1 = 0 and a carry of 1

Use this and try out the addition of 106 and 52. Voila! You’ll get the answer of 158.

Proceeding further, we need to investigate as to how negative numbers are really stored in computers. Wediscussed earlier that if +10 is represented as 0000 1010 then -10 would be represented as 1000 1010 (since the MSB is considered as the sign bit). Right? Let’s check it out.

38

Data Types & Variables

Page 39: C++ Basics to Advanced

If we add -10 to 10 then we should get the answer as zero.

0 0 0 0 1 0 1 0

1 0 0 0 1 0 1 0

-----------------------------------------------------------------

1 0 0 1 0 1 0 0

And the answer is? -20.

So, where did we go wrong? One question you might have is why did we add the sign bit also? The computerisn’t smart enough (or rather it doesn’t have the circuitry) to separate the sign bit from the rest of the number. The 2 numbers that you want to add, are fed as input to an adder circuit which will blindly add up both thenumbers and give the result. To overcome this problem, we can make use of 2s complement.

For example: 1000 0001 actually stands for –1 but if this number is stored in 2’s complement format then it stands for –127. Similarly 1000 0010 would appear to be –2 but is actually –126. To understand 2’s complement you should know about 1’s complement. Let us say we have a signed binary number:

1 0 0 0 0 0 0 1

The 1’s complement of this number is

1 1 1 1 1 1 1 0

To find the 1’s complement of a binary number just change the 1s to 0s and 0s to 1s but do not change the signbit.

Next, to find the 2’s complement just add 1 to the 1’s complement.

1 1 1 1 1 1 1 0

1

-----------------------------------------------------------------

1 1 1 1 1 1 1 1

Hence the 2’s complement of 1000 0001 is 1111 1111 (or –127). In the same way the 2’s complement of 1111 1111 (-127) is 1000 0001 (or –1). Thus the computer instead of storing 1111 1111 (for –127) will store the 2’s complement of the number (i.e. it will store 1000 0001). Why? Let’s go back to our initial problem of adding +10 and -10. Now let’s assume that the computer stores -10 in 2’s complement format. The addition will now be:

39

Data Types & Variables

Page 40: C++ Basics to Advanced

0 0 0 0 1 0 1 0 (+10)

1 1 1 1 0 1 1 0 (-10)

-----------------------------------------------------------------

0 0 0 0 0 0 0 0

Voila! The answer is 0. But you may ask ‘what about the carry of 1?’. Well, since it is an extra bit, it overflows (which means it is lost and we needn’t worry about it). I’m not going to get into more details of 2’s complement since this should be sufficient for learning C++. To learn more on this subject, you can check out books ondigital systems or computer system architecture.

Note: When you perform binary addition, 1 + 1 is equal to 0 and a carry of 1 (because in the binary system we only have 2 states: 1 and 0; so we can’t have 1 + 1 = 2 in binary).

Remember: When converting a number to its 2’s complement its sign bit is unchanged. Numbers are stored in 2’s complement format only if they are negative. Positive numbers are stored in the normal format.

Go back to the Contents Page

Copyright © 2004 Sethu Subramanian All rights reserved.

40

Data Types & Variables

Page 41: C++ Basics to Advanced

Simple C++ Program

A few simple programs are explained in this section to help you get a feel of C++. First, let’s write a program to calculate the area of a circle. The variables needed in this program are the radius of the circle (which can beobtained as input from the user) and the constant ‘pi’ (whose value can be provided in the code itself).

The preprocessor has only one directive and it will include the iostream.h header file into the source code. The compiler will start reading the code from the main ( ) function onwards.

Remember: Whatever is typed within the main ( ) function will be executed. The main ( ) function is used as the entry point for a C++ program.

PI is a variable name and is declared as a float quantity (because the value of PI has a decimal point). At thepoint of declaration, PI is initialized to a value of 3.14. This means that whenever PI is used in the program, thecompiler will use 3.14 instead of PI.

The line:

cout<<"Enter the radius";

will cause

Enter the radius

to be displayed on the screen. This is because "Enter the radius" is typed within double quotes following ‘cout’and the insertion operator. Anything between double quotes, along with cout<< will be displayed on the screenjust as it appears within the double quote.

The value entered by the user will be stored in the variable ‘rad’.

Then the statement "Area of the circle is " will be displayed on the screen. The compiler will calculate the valueof ‘PI * rad * rad’ and display it at the end (* is the multiplication operator in C++).

The output for the above program is:

Enter the radius 9

// To Calculate the Area of a Circle

# include <iostream.h> int main( ) { float PI = 3.14; // variables can be initialized during declaration int rad; cout<< "Enter the radius" ; cin>>rad; cout<< "Area of the circle is "<< PI * rad * rad; return 0; }

41

Data Types & Variables

Page 42: C++ Basics to Advanced

Area of the circle is 254.14

Bold indicates that the user entered the value. In this case 9 was entered as the radius.

Suppose we type

cout<< "rad";

the output will be just the word

rad

The value of the variable ‘rad’ will not be displayed.

Remember: When you want to display some variable’s value on the screen, DO NOT ENCLOSE IT IN DOUBLE QUOTES; just mention the name of the variable after the insertion operator.

Initializing variables:

It is a good idea to initialize variables at the time of declaration. Even if you are unsure of the value you can still initialize it to 0. If you are wondering why, just consider the example below:

#include <iostream.h> int main( ) { int correct, choice; cout<<"\nEnter your guess of the lucky number: "; cin>>choice; if (choice= =correct) { cout<<"\nCongrags. You are correct!"; } else { cout<<"\nSorry. Wrong guess"; } cout<<correct; //uninitialized variable return 0; }

Let’s not get into the details of this program for the time being (we’ll discuss about the ‘if’ statement, escape sequences \n later). You should have got a rough idea as to what the program is about. We ask the user to enter a number and if that number matches the number we’ve decided upon then we display a message saying ‘Congrags’.

The user’s value is stored in the variable ‘choice’ and the actual correct value should have been stored in the variable ‘correct’. Run the program and you’ll get something like the following:

Enter your guess of the lucky number: 5

Sorry. Wrong guess 469772

42

Data Types & Variables

Page 43: C++ Basics to Advanced

The problem is that in the program we haven’t assigned a value to the variable ‘correct’ and neither have we initialized it. Thus this variable has an unknown value (also called garbage value).

There are 2 ways to initialize variables:

int correct = 25;

or

int correct(25);

The second form is called functional notation and we generally don’t use this to initialize in-built data types (like integers, characters, double etc.). This form is used to initialize user defined data types (which we’ll discuss in the chapter on Classes).

Another example program:

The above program is for finding out the ASCII value for any character that you type. How does it work?

Always go line by line and put yourself in the position of the compiler. ‘check’ is declared as a character and ‘i’is declared as an integer. The first value entered is stored in the variable ‘check’. ‘check’ is a character but ‘i’ is an integer variable. How can we equate ‘check’ to ‘i’? Won’t there be some problem?

It is not advisable to equate one data type to another. Many a times this could lead to errors (usually logicalerrors) but in the above program this is done purposely. ‘check’ is a character (one byte) but it is stored as a number in memory (for example: an ‘a’ is not stored as an ‘a’ in memory. It is converted into a decimal number using the ASCII code and then into binary form and stored in memory). A character occupies one byte while aninteger occupies 2 bytes. A character has a range from –127 to 127 while an integer has a much greater range.Thus, whatever value is held by ‘check’ can be held by the integer ‘i’ as well. And this value is actually the ASCII value for the character that the user types. You might wonder, why not use the statement below since‘check’ also has the same value:

cout<<check;

This will display the same character that you entered and not the ASCII value. Why? Because when somethingis stored as a character (though it is stored in integer format), it will go through the same process of ASCIIcoding and find out what is the equivalent character (and display the character). But when something is stored

//Can you guess what this program is for and how it works?

#include<iostream.h> int main( ) { char check; int i; cout<<"Enter the character that you want to convert to ASCII : "; cin>>check; i = check; cout<<"The ASCII value for "<<check<< " is "<<i; return 0; }

43

Data Types & Variables

Page 44: C++ Basics to Advanced

as an integer, the number will be displayed as it is (without going through the ASCII coding process).

Try it: Write a program that will do the reverse process (i.e. type a number and the program has to display the corresponding character).

44

Data Types & Variables

Page 45: C++ Basics to Advanced

Identifiers, File Nomenclature, Keywords, Constants, Comments

The following topics are covered in this section:

Identifiers File Nomenclature Keywords Constants Comments

Identifiers:

Identifiers are very important in C++ and are used in all programs. What is an identifier? In the previous program ‘check’ was declared as a character variable. The identifier in this case is ‘check’. Basically identifiers are names that are given to variables, constants or functions. In the previous program there are two identifiers: ‘check’ and ‘i’.

C++ permits the use of a huge variety of names for identifiers like:

test Test int_test var1 var123 c_b_f _var

Almost any name you can think of can be used as an identifier but there are some restrictions. An identifier should not start with a number. The first character of an identifier should be an alphabet or an underscore ( _ ). After the first character, numbers can be used in the identifier. Remember the following:

Never start an identifier with anything other than a letter or an underscore. It is better to use a letter than starting with underscores. Do not use keywords as identifiers (ex: do not name an identifier as int). Uppercase and Lowercase identifiers are different (Check is different from check). Be careful when using characters that look similar to each other. For example ‘1’ (the number one) and ‘l’ (the lowercase alphabet L) look alike. Use names that are easy to understand (if you are storing the salary of a person, name the variable as ‘salary’ instead of declaring it as ‘x’. Do not use very long names. Do not name many variables with similar names (avoid using identifiers like: count, counter, counting etc.).

45

Data Types & Variables

Page 46: C++ Basics to Advanced

Keywords:

Keywords are reserved words in C++ programming. They should not be used as identifiers and all keywords should be in lower case. The 63 keywords are tabulated below.

File Naming Convention:

A few points regarding file nomenclature are:

In MSDOS a filename cannot have more than 8 characters whereas in Windows there is no such restriction. Use meaningful names (depending on what the program performs). Avoid using underscores in the file names. Be careful when using characters that look similar to each other. For example ‘1’ (the number one) and ‘l’ (the lowercase alphabet L) look alike.

Asm Auto bool break case Catch char

Class Const const_cast continue default Delete do

Double Dynamic_cast else enum explicit Export extern

False Float For friend goto If inline

Int Long mutable namespace new Operator private

Protected Public register reinterpret_cast return Short signed

Sizeof Static static_cast struct switch Template this

Throw True Try typedef typeid typename union

Unsigned Using virtual void volatile wchar_t while

46

Data Types & Variables

Page 47: C++ Basics to Advanced

Constants:

Constants are used in expressions and their values cannot be changed. They are used in the program code without using any variable name to refer to them. For example consider the statements:

int x = 5;

float y = 3.33;

In this, 5 is an integer constant and 3.33 is a floating type constant. You cannot change 5 or 3.33 but you can assign these values to variables (the variables in the above code fragment are ‘x’ and ‘y’).

Constants can be classified based on their data type as follows:

Numeric type constants (includes integers, floating-point etc.) Character Constants String Constants

Numeric Constants:

Numeric constants consist of a series of digits. Integer constants can be written in different number systems: hexadecimal (base 16), octal (base 8) or in decimal (base 10).

A decimal integer constant is any normal whole number (can consist of digits from 0 to 9):

File Type Extension used

C++ source file *.cpp

C++/C header file *.h

C source file *.c

Executable file *.exe

Object Code *.obj

Text file *.txt

Word document *.doc

Image/graphics file *.jpg/ *.gif/ *.bmp

47

Data Types & Variables

Page 48: C++ Basics to Advanced

2,34, 100, 900, 1456 etc.

An octal integer constant can contain digits from 0 to 7 only and it should start with a zero (so that the computer will know it is an octal number). For example:

023, 0567, 0214 etc.

A hexadecimal integer constant should start with 0x or 0X and can consist of digits from 0 to 9 and A to F (uppercase and lowercase letters are allowed). For example:

0x10, 0x1FF etc.

Floating point constants will have the decimal point in the number.

Character and String Constants:

Character constants are single characters enclosed within two single quotes (or between two apostrophes). For example:

‘a’

‘b’

‘x’

‘1’

‘A’

‘*’

A single character enclosed within single quotes is a character constant. All character constants will have an integer value (determined from the ASCII table). A C++ statement:

char ch = ‘B’;

will assign the character constant ‘B’ to the character variable ch.

String constants consist of a series of characters enclosed within double quotes. For example:

"hello"

"This is a string"

"x"

Even "x" is a string because it is enclosed in double quotes. "x" is different from ‘x’ (this is a character constant). The reason is because "x" actually consists of ‘x’ and ‘\0’ (the null character which will be

48

Data Types & Variables

Page 49: C++ Basics to Advanced

explained later).

Constants are also called as ‘literal’. You might come across the terms character literal, integer literal, string literal etc.

Beware: The constants described in this section are different from variables using the ‘const’ keyword (this is discussed in Unit 6).

Comments:

Comments are not meant for the compiler to read. They are meant to make the program easier to understand for humans who might read the coding later. It is a good practice to write comments (even for small programs). What should be written in the comments? First of all, a couple of lines about the aim of the program should be mentioned. You should also mention the name of the programmer and the date when the program has been modified. Comments should be updated every time you modify the code. As the program gets larger, it is advisable to write comments for the various functions that are used in the program. For instance, if a function is used to calculate the sum of numbers, then this could be mentioned in the comments. Basically you are permitted to write anything within comments because it will not affect the program.

There are two methods used to denote comments:

Single line commenting (//) Multiple line commenting (/*…..*/)

For single line comments use double slashes (//) followed by the comments (but it should be on the same line as the double slash). Example:

//this is a single line comment

Multiple line commenting permits you to write line after line of comments. It will be as follows:

/* Multiple line commenting…

…this is also part of the comment…

……comment continued……

*/

Everything in between /* and */ will be commented. By using multiple line commenting the programmer needn’t use double slashes for each and every line.

Recap:

49

Data Types & Variables

Page 50: C++ Basics to Advanced

The file iostream.h provides the general basic input and output functions.‘cout’ is used to display data on the screen while ‘cin’ is used to obtain inputs from the user. Every variable/function in C++ has to have a name. This name is called an identifier. Keywords are specially reserved words in C++. They cannot be used as identifiers. There are two data types in C++: fundamental and user-defined. Variables are named memory locations (their name is provided by the programmer) where values can be stored and changed frequently in the program. Constants are usually used to assign values to the variables. All data types have different sizes and are used for specific purposes. The type of a variable determines what it can store. Comments are written for easy understanding of the program (they are not processed by the compiler).

50

Data Types & Variables

Page 51: C++ Basics to Advanced

Operators - I

C++ Operators - I

The following topics are covered in this section:

• Arithmetic Operators • Escape Sequences

1. Arithmetic Operators

These operators are used to perform basic mathematical operations like addition, subtraction, division and multiplication.

Operator Symbol Operation performed

+ Addition

- Subtraction

* Multiplication

/ Division

% Modulo operator (Remainder after division)

The modulo operator is not used to calculate the percentage. It is used to find the remainder after integer division. For example: 6 % 4 = 2 (because dividing 6 by 4 produces a remainder of 2).

The modulo operator can operate only on integers. Since the modulo operator is used to determine the remainder after division, it is also called the remainder operator. Arithmetic operators are binary operators since they need two quantities (or operands) to perform an operation.

51

Page 52: C++ Basics to Advanced

Operators - I

#include <iostream.h> int main( ) { int num1, num2; cout<<"Enter the two numbers : "; cin>>num1>>num2; cout<<"The product is : "<<num1*num2; cout<<"The sum is : "<<num1+num2; cout<<"The difference is : "<<num1-num2; cout<<"The quotient is : "<<num1/num2; cout<<"The remainder is : "<<num1%num2; return 0; }

In the program, we declare two integers ‘num1’ and ‘num2’ and obtain their values from the user through the statement:

cin>>num1>>num2;

This is a method of obtaining multiple inputs using a single statement. The above statement is equivalent to writing:

cin>>num1;

cin>>num2;

The two numbers, when entered by the user, can be separated by a space or by a new-line (i.e. the first number is typed and then after pressing the ‘enter’ key the second number is typed).

When you run the program you would get the following on your screen:

Enter the two numbers : 8 4

The product is : 32The sum is : 12The difference is : 4The quotient is : 2The remainder is : 0

Something is not right in this output; the results are correct but the display is on a single line. To display the output in an organized manner, the program should print each output on a new line. For this purpose of formatting the output C++ provides us with ‘escape sequence’.

52

Page 53: C++ Basics to Advanced

Operators - I

Escape Sequences/ Backslash Character Constants

If you remember, whatever you type within double quotes following cout<<, will be printed as it is on the screen. There is a problem in case you want to print a new line, or you want to use tabs (because whatever you type within double quotes will be displayed directly on the screen).

To solve this problem, escape sequences were developed. Just as the name implies, these escape sequence characters are used to escape from the normal sequence of events. An escape sequence always begins with a backslash ( \ ). For a new line, the escape sequence is \n (n for new line). If you want to push the tab setting then \t should be used (t for tab).

The modified program for doing simple arithmetic operations is as follows:

#include <iostream.h> int main( ) { int num1, num2; cout<<"Enter the two numbers : "; cin>>num1>>num2; cout<<"\n The product is : "<<num1*num2; cout<<"\n The sum is : "<<num1+num2; cout<<"\n The difference is : "<<num1-num2; cout<<"\n The quotient is : "<<num1/num2; cout<<"\n The remainder is : "<<num1%num2; return 0; }

When you run the program you would get the following on your screen:

Enter the two numbers : 8 4 The product is : 32 The sum is : 12 The difference is : 4 The quotient is : 2 The remainder is : 0

There are a few other useful escape sequence characters as well:

53

Page 54: C++ Basics to Advanced

Operators - I

Escape Sequence Meaning

\n new line

\t horizontal tab

\a bell sound

\\ Backslash

\? question mark (?)

\" double quotation

\’ Apostrophe

\0 Null character (used in strings)

Though escape sequences consist of 2 characters they represent only a single character (for example: \? represents the question mark character). Escape sequences are character constants and are denoted as ‘\\’, ‘\?’, ‘\0’ etc.

Beware: ‘\0’ is not equivalent to zero. ‘\0’ represents a null character (used in strings).

Try it: Use the above escape sequences in a C++ program to see their effects. The \a escape sequence will literally make a sound of a bell.

Remember: Escape sequences always start with a backslash ( \ ) and have to be followed by valid characters (like n, a, t etc…). If you happen to use some other character (like w), the compiler will display a warning message (the program will run and usually the compiler will ignore the character or display the character on screen).

Coming back to the program with arithmetic operators; what would be the output if 5 and 2 are entered as the two numbers? What will the quotient and remainder be? The result will give a quotient of 2 and a remainder of 1. But isn’t 5/2 = 2.5?

The answer to this lies in the declaration of the variables ‘num1’ and ‘num2’. Since these two variables are declared as integers, all the results of arithmetic operations will also be in the integer format. Suppose ‘num1’ and ‘num2’ are declared as floating point data type, then the result of division would be 2.5 instead of 2. In this case, what do you think will be the remainder? Will it be zero always?

Remember: The modulo (%) operator can operate only on two integers and not on any other data type. If the remainder operator is used on floating point numbers, the compiler will produce an error message. The other arithmetic operators can be used on integers as well as floating-point numbers.

54

Page 55: C++ Basics to Advanced

C++ Operators - II

The following topics are covered in this section:

• Assignment Operators • Type Conversion

2. Assignment Operators

The assignment operator assigns the value on its right-hand side to whatever is present on the left-hand side. The ‘equal to’ symbol ( = ) is the C++ assignment operator.

For example, the statement

x = 2;

assigns the value on the right-hand side of the ‘equal to’ sign to the term on the left (i.e. the value 2 is assigned to the variable x). The term which appears on the left-hand side of the assignment operator (usually a variable) is called the ‘target’.

You will always assign a value to some variable but not to a constant.

2 = x; //Error

is a wrong statement because you cannot assign the value of x to 2 (2 is an integer constant and you cannot change its value).

The value on the right side of the assignment operator can be an expression as shown below (or a function that returns a value): x = num1 + num2; y = 5*6;

Compilers often use the terminology ‘lvalue’ (or Lvalue) and ‘rvalue’ especially in error messages. The ‘lvalue’ refers to the operand on the left-hand side of the assignment operator while the ‘rvalue’ refers to the operand on the right-hand side of the assignment operator. The lvalue can be a variable or a pointer but it should not be a constant. The rvalue can be a constant, variable, an expression or a function call.

Beware: Lvalue can be used as rvalue (i.e. on the right-hand side of an assignment) but rvalue cannot be used as Lvalue.

55

Operators

Page 56: C++ Basics to Advanced

C++ permits multiple assignments (i.e. more than one variable can be assigned a value using one statement). For example let x, y and z be integers, then

x = y = z = 3;

is an example of multiple assignment. The process of assignment will be from right to left; 3 is assigned to ‘z’ and the value of ‘z’ (which is now 3) is assigned to ‘y’ and y’s value is assigned to ‘z’. The above multiple assignment is equivalent to the following set of statements:

z = 3; y = z; x = y;

and all the three variables x,y and z are assigned the value of 3.

Is this valid?

x = y+1 = 3;

This sort of a statement is not valid in C++. You cannot use arithmetic operators in between multiple assignments (the compiler will not know how to solve this statement).

Type Conversion

Each data type has a different range because of the different sizes that they occupy. A character is allotted 1 byte while an integer might be allotted 2/4 bytes. What will happen if we assign an integer to a character or a double to an integer? Assigning a variable of one data type to a variable of another data type is known as type conversion. When assigning between numeric types, whatever is on the right hand side of the assignment will be assigned to the variable on the left. If a variable of a smaller type is assigned to a variable of a larger type, no information will be lost on conversion (because a smaller type can be accommodated into a larger type). For example a character (which occupies only 1 byte) can be converted into an integer without any loss of information (because an integer occupies more bytes).

However if you attempt to assign an integer to a character you will lose the higher order bits of the integer (only the lower 8 bits of the integer will be retained in the character).

Let us assume that an integer occupies two bytes and a character occupies one byte.

unsigned int a=510; unsigned char x; x=a;

56

Operators

Page 57: C++ Basics to Advanced

As can be seen from the figure, the upper 8 bits are lost due to the type conversion and the value of the character will be only 254.

The following program illustrates a few type conversions:

#include <iostream.h> int main( )

{ char ch;

double db; float f;

short int i; db=55e4;

ch = i = f = db; cout<<ch<<" , "<<i<<" , "<<f<<" , "<<db;

return 0; }

The output will be:

p , 25712 , 550000 , 550000

Some compilers will produce a warning message

warning: assignment to ‘short int’ from ‘float’

A program with warnings can be executed. A warning is a caution produced by the compiler when it feels that the programmer might have done something by mistake (in this case an assignment from a larger type to a smaller type). To avoid warnings we will need to make use of typecasts (which will be explained later).

Remember: Be very careful if you make use of type conversion because it can lead to loss of information.

57

Operators

Page 58: C++ Basics to Advanced

C++ Operators - III

The following topics are covered in this section:

• Assignment Arithmetic Operators • Relational Operators

3. Assignment Arithmetic Operators/Shorthand Operator (+ =, - =, * =, / =)

Assignment arithmetic operator is a combination of an arithmetic operator and the assignment operator. Consider the example:

X + = 3;

The above statement is the same as:

X = X+3;

i.e. the value of X is incremented (or increased) by 3 and the new incremented value is assigned to X. So if X had a value of 2 before this expression, then X will be 5 after this expression is executed.

Operator Operation performed

a+=b a = a + b

a-=b a = a – b

a*=b a = a * b

a/=b a = a / b

a%=b a = a % b

58

Operators

Page 59: C++ Basics to Advanced

4. Relational Operators ( < , > , = = , ! = , >= , <= )

Relational operators are also binary operators (since they operate on two operands). They are used for comparing two values and the result of the comparison is either true (value 1) or false (value 0).

Some examples are given below:

5>4 will return a value of True (1)

2>3 will return a value of False (0)

In programs that you write, comparisons will usually be made between one variable and a constant or between two variables. For example:

x>y z>10

> means ‘greater than’ while >= stands for ‘greater than or equal to’.

x>=y

will yield a true value even if x = y whereas x>y will yield a value of false when x = y. Be clear as to what relation you want to test when using these operators.

Suppose you want to test whether two variables are equal, you have to make use of the equality operator. The equality operator is denoted by = = (double equal to signs).

Remember: Many beginners in programming use the equality operator and assignment operator interchangeably. The assignment operator is a single ‘equal to’ sign and it is meant only for assigning values to variables. The equality operator (a double ‘equal to’ sign) is used to check whether two values are equal.

Relational Operator Operation Performed Result of Operation

x>y Is x greater than y? True/False

x<y Is x less than y? True/False

x>=y Is x greater than or equal to y? True/False

x<=y Is x less than or equal to y? True/False

x==y Is x equal to y? True/False

x!=y Is x not equal to y? True/False

59

Operators

Page 60: C++ Basics to Advanced

We’ll write a simple program for comparing two numbers and displaying the appropriate result.

#include <iostream.h> int main( ) { float num1, num2; cout<<"Enter the two numbers : "; cin>>num1>>num2; if (num1>num2) { cout<<"The number "<<num1<<" is greater than "<<num2; } if (num1= =num2) { cout<<"The number "<<num1<<" is equal to "<<num2; } if (num1<num2) { cout<<"The number "<<num1<<" is less than "<<num2; } return 0; }

It might appear as if there is a white space between the two ‘equal to’ symbols used in the equality operator but this is not the case. Do not leave a blank space between the two symbols (this will produce a compile error).

We still haven’t covered the topic on ‘if’ conditions but you should be able to understand the working of the above program. The two numbers that are obtained from the user are compared using three ‘if’ conditions. Depending on which condition is satisfied, the corresponding output will be displayed.

An example for the output displayed is:

Enter the two numbers : 5 6

The number 5 is less than 6

Very frequently the above program is written with a small mistake which will lead to severe logical errors. Instead of typing

60

Operators

Page 61: C++ Basics to Advanced

num1= = num2

within the ‘if’ condition, the following mistake is made:

if (num1 = num2) // Logical Error { cout<<"The number "<<num1<<" is equal to "<<num2; }

What do you think will happen? Let us assume that the two numbers are 7 and 5.

The output will be:

Enter the two numbers : 7 5

The number 7 is greater than 5The number 5 is equal to 5

When the following code is encountered:

if (num1 = num2)

the computer assumes that this is an assignment and not a comparison (because the assignment operator has been used). Hence in this line of coding, the value of ‘num2’ will be assigned to ‘num1’ (both ‘num2’ and ‘num1’ are now 5). Now within the ‘if condition’ we have a non-zero positive value (this value is 5 and is equivalent to ‘true’). So, the compiler will execute the cout statement within that ‘if’ condition irrespective of the values of ‘num1’ and ‘num2’. This should give you a good idea as to how a small mistake can completely alter the logic of the program.

The ‘if’ statement will be dealt in Chapter 4, but for the time being just remember that whatever you type within an ‘if’ statement will be executed if the condition is true.

When testing whether a variable is equal to a constant some programmers prefer to use the following method:

if (100 = = x)

instead of the usual:

if (x = = 100)

The advantage of the first method is that even if the programmer commits the mistake:

if (100 = x)

the compiler will produce an error (since you cannot assign a value to a constant).

61

Operators

Page 62: C++ Basics to Advanced

In the following program, we have coded to display the result of num>5. Do you think it is valid?

#include <iostream.h> int main ( ) { int num; cout<< "Enter the number"; cin>>num; cout<<(num>5); //Legal? return 0; }

Always remember that the result of a comparison yields a value of TRUE (1) or FALSE (0). Hence the above program is perfectly correct. In case you enter a value that is greater than 5 you will get the output as 1 else you will get 0. 0 is considered as false and all other values are considered to be true.

62

Operators

Page 63: C++ Basics to Advanced

C++ Operators - IV

The following topics are covered in this section:

• Logical Operators • Unary Operators

5. Logical Operators - AND ( && ) OR ( || ) NOT (!)

These operators are used to combine two or more expressions. The way in which they combine the expression differs depending on the operation used. They are used when we need to test multiple conditions. For example you may write a program that has to check whether the marks scored by a student is greater than 70 and less than 80. If it is so then you will want the program to display a ‘B’ grade. To check whether the average mark is greater than 70 you have to use one expression and to check whether the average is less than 80 you should use another expression. Thus in simple English your statement will be:

If (average mark is greater than 70 AND average mark is less than 80)

Print "B grade"

AND: it combines two conditional expressions and evaluates to true only if both the conditions are true.

First Condition Second condition Result of AND operation

False False False

False True False

True False False

True True True

If the first condition and the second condition are both true then the result of the AND operation will also be true.

Example:

63

Operators

Page 64: C++ Basics to Advanced

// To check whether the given number is even # include <iostream.h> int main ( ) { int num; cout<< "Enter the number"; cin>>num; if ( (num!=0) && ((num%2)= =0) ) // Two conditions have to be true { cout<<"\n Even Number"; } return 0; }

In this program we need to check for two conditions (the number entered should not be zero and the number when divided by 2 should not produce a remainder). Only if both these conditions are satisfied should the program display that the number is even. The AND operator is used to combine the two conditions that are to be tested and if both are true then the message is displayed.

OR: operator combines two conditions and evaluates to true if any one of the conditions is fulfilled (i.e. only one of the conditions need to be true). It is designated by using two parallel bars/pipes ( | | ).

First Condition Second condition Result of OR operation

False False False

False True True

True False True

True True True

The ‘AND’ and ‘OR’ operators can be used on a sequence of conditions (i.e. at a time you can check for multiple conditions). For example the following code is legal:

if ( (x>y) && (y>5) && (z>y) && (x>4) ) { //body of the ‘if’ condition… }

64

Operators

Page 65: C++ Basics to Advanced

In this case only if all the four conditions are true will the body of the ‘if condition’ be executed.

NOT: NOT is a unary operator. Unlike ‘AND’ and ‘OR’, NOT operates only on one operand. The logical value of the operand is reversed. If the operand is true, then after the NOT operation it will be become false. You might be thinking that the NOT operator can operate only on 1 and 0. Actually, any number greater than 0 will be considered as a true value. Hence the following would give:

• !5 will give 0 • !0 will produce 1 • !1 is equal to 0.

Condition Result of NOT operation

False True

True False

Basically, any number other than zero is considered as true. ‘Not’ of any number (other than 0) will give you FALSE (or zero). Check out the following program that illustrates the NOT operator.

#include<iostream.h> int main ( ) { int num, result; cout<< "Enter the number"; cin>>num; result = (!num); cout<<result; return 0; }

65

Operators

Page 66: C++ Basics to Advanced

6. Unary Operators (NOT operator !, increment operator ++, decrement operator --)

Unary operators operate on only one operand, which could be a constant or a variable. We’ve already discussed about the NOT operator. Another simple unary operator is the unary minus operator. This will act on only one operand and will change the sign of the number it operates on. For example:

• -(5) = -5 • -(-5) = +5

The ++ and – operator are very important if you are taking a course in C++ (teachers usually love this topic and they are bound to ask some questions on unary operators).

++ is known as the increment operator and it can be used in two ways.

• As a prefix : i.e. when the operator precedes the variable (as in ++ i, where i is the integer variable)

If we write:

i1 = 10; i2 = ++ i1;

This is the same as:

i1 = 10; i1 = i1 + 1; i2 = i1;

At the end of these steps the value of both i1 and i2 will be 11. When used as a prefix, the variable is first incremented and later assigned.

• As a suffix: i.e. when the variable precedes the operator (as in i ++).

If we write:

i1 = 10; i2 = i1 ++ ;

This is the same as writing:

i1 = 10; i2 = i1; i1 = i1 + 1;

66

Operators

Page 67: C++ Basics to Advanced

At the end of these steps the value of i2 will be 10 and that of i1 will be 11. When used as a postfix, the variable value is first assigned and later incremented.

The decrement operator works in the same way. The only difference is that it decreases the value of the operand by one. Again the decrement operator also can be used in two ways: as a prefix or as a suffix (or postfix).

Suppose we have the following coding in a program, what will be the value of ‘sum’ at the end of each statement?

int sum=5; //‘sum’ is 5 cout<<sum++; // ‘sum’ is 5. Only in the next reference to ‘sum’ will it be 6 cout<<" "<<sum; //‘sum’ is now 6

Thus in the case of the postfix operator the value is assigned first and increment is carried out in the next step. If you make use of the prefix operator then:

int sum=5; //‘sum’ is 5 cout<<++sum; // ‘sum’ is immediately 6 cout<<" "<<sum; //‘sum’ is 6

Remember: When used as a prefix ( ++i ) compiler will first increment and then assign. When used as a suffix, assignment is done first and then incrementing is performed.

Beware: The ++ and – operators cannot be used on expressions (i.e. ++ (y + 1); is an incorrect statement and will lead to a compiler error).

67

Operators

Page 68: C++ Basics to Advanced

More on Operators (V)

The following topics are covered in this section:

• Sizeof Operator • Conditional Operator • Operator Precedence • Associativity • Comma Operators • Bitwise Operators

SizeOf Operator (sizeof)

This is again another unary operator. Just as the name implies, this operator returns the length (in bytes) of the variable that is mentioned in the parentheses. For example the size of a character is 1 (i.e. 1 byte).

// Using sizeof operator #include <iostream.h> int main ( ) { int a; cout<<sizeof (a); // Since a is an integer, the output will be 4 cout<<sizeof(char); // Output will be 1 return 0; }

It is not a must to mention a variable within the parentheses; you could also mention one of the in-built data types. Some operating systems allocate a different memory space for data types and it is better to use the ‘sizeof’ operator to determine the size of data types instead of assuming that all systems use the same number of bytes.

Conditional Operator (?:)

The conditional operator is a ternary operator (i.e. it operates on 3 operands at a time). We’ll take a closer look at it in the next chapter. For the time being just remember that it needs three operands and it is denoted by: ? :

68

Operators

Page 69: C++ Basics to Advanced

Operator Precedence

When a statement has more than one operator, the operator precedence determines as to which operator is given priority. For example if you have a * and a +, the compiler has to decide whether it will multiply and add or add and multiply. This will depend on the operator precedence.

The parentheses have the highest precedence. For instance if you write:

(5+3)*5

the result will be (8)*5 or 40.

If you write the same without using parentheses as

5+3*5

the result will be 20 (because * has a greater priority than +). The order for operator precedence is listed below (starting from the highest priority):

Parentheses ( ) Unary Operators ! Unary minus - Multiplication * Division / Modulo % Addition + Subtraction - Relational Operators < <= > >= Equality operator == Inequality operator != Logical Operator && || Conditional operator ?: Assignment = Arithmetic assignment *=, /= , %= , += , -=

69

Operators

Page 70: C++ Basics to Advanced

Beware: It is better to write long expressions using parentheses otherwise it could lead to a lot of confusion and also to potential logical errors.

Associativity

If two operators have a different priority level then their execution order depends on the operator precedence. What will happen if two operators have the same priority level?

When 2 operators in an expression have the same priority, the expression is evaluated using their associativity. Consider the statement:

net = basic + allowance – tax;

Both + and – have the same priority level. Almost all of the operators except the assignment (and arithmetic assignment) operators have associativity from left to right. The assignment operator has associativity from right to left. Since the + and – binary operators have an associativity of left to right the expression for ‘net’ is the same as:

net = (basic + allowance) – tax;

When you perform multiple assignments:

x = y = 0;

the associativity of the assignment operator (=) is taken into account and thus the order of evaluation will be:

y = 0 (which will give ‘y’ a value of 0) followed by x = y (which will assign 0 to ‘x’).

70

Operators

Page 71: C++ Basics to Advanced

Comma Operator

The comma operator can accept two expressions on either side of the comma. When executed, the left side expression is first evaluated followed by the right side expression. Ultimately it is the expression on the right side that will be the value of the entire expression.

int x,y; cout<<(x = 1, y = 5); // 5 will be displayed

First the ‘x’ will be assigned 1 and then ‘y’ is assigned a value of 5. The comma operator is equivalent to saying "do this task and do this also". In this case the compiler will do:

x = 1 and then y = 5

The rightmost expression is

y = 5

and hence the value of the entire expression

(x=1,y=5)

is 5. Be careful while assigning the value of a comma separated expression to a variable. The comma operator has lower operator precedence than the assignment operator. If we type:

y = (x = 1 , x = 5 , x + 10);

x will have a value of 5 while y will be 15. If we type:

y = x = 1, x = 5 , x+10;

the value of ‘y’ will be 1 and that of ‘x’ will be 5. This is because the compiler will perform the following operations:

y = x = 1; // y and x are set to 1

x = 5; //x value is 5

x + 10; //the value of the entire comma separated expression is 15

The comma operator will be used usually in ‘for’ loops.

71

Operators

Page 72: C++ Basics to Advanced

Bitwise Operator:

The bitwise operators available in C++ are tabulated below:

Bitwise Operator Symbol Function

& AND individual bits

| OR individual bits

^ EXOR individual bits

~ NOT of individual bits (or complement operator)

>> Right-shift operator

<< Left-shift operator

These operators will operate on individual bits of the operand (i.e. on the binary representation of data). These operators are dealt with in detail in Chapter 13.

Remember: Do not confuse the && and & operators or the || and | operators (logical operators are entirely different from bitwise operators).

Recap

• Arithmetic operators are used to perform mathematical operations. • The modulo/remainder operator is applicable only on integer operands. • Escape sequences are character constants that are used to format the output

displayed on the screen. • = is the assignment operator and the Rvalue is assigned to the target (or the

Lvalue). • Information will not be lost when converting from a smaller data type to a larger

data type. • Shorthand operators are a combination of one of the arithmetic operators and the

assignment operator. • Relational operators are used to compare two values or expressions. They will

return a value of true or false. • Logical operators are used to combine two or more expressions. • The operator precedence determines which operator has a higher priority. • When 2 operators have the same priority the expression is evaluated based on

their associativity. • Bitwise operators are used to operate on individual bits.

72

Operators

Page 73: C++ Basics to Advanced

The basics (chapters 1 to 3)

1.Q) What do you think is the output???

int main( ) { int a=3,b; b=a++ + a++; cout<<endl<<"b value = "<<b; a = 3; b=++a + ++a; cout<<endl<<"b value = "<<b; return 0; }

2.Q) The following coding:

a=3; int c = ++a; int d = ++a; int b = c + d; a=3; b = ++a + ++a;

What are the values for 'b' in both cases?

3.Q) In the following piece of coding what do you think is the value of j?

char i=127; int j; i=i+1; j=i; cout<<j;

4.Q) ‘cout’ and ‘cin’ are keywords?

5a.) For each of the questions below assume:

int i = 2; int j = 5; int k;

Find out the value of ‘k’ (not all will have values because some are illegal statements).

k= (j++);

5b.)

k= 2++;

5c.)

k= i++ + ++j;

5d.)

k = i+++++j;

5e.)

k= i = (j = 1);

5f.)

1=2;

73

Page 74: C++ Basics to Advanced

Answers and Explanations to the Quiz-I

1. (b) Depends on compiler 2. (c) 9 (sure), 10 (mostly) 3. (d) -128 4. (b) False

5a. (a) 5 5b. (d) Illegal 5c. (c) 8 5d. (d) Illegal 5e. (a) 1 5f. (c) Illegal

Q.) What do you think is the output???

int main( ) { int a=3,b; b=a++ + a++; cout<<endl<<"b value = "<<b; a = 3; b=++a + ++a; cout<<endl<<"b value = "<<b; return 0; }

A. The output will (probably) be:

b value = 6 b value = 10

When we say a++, the computer will not increment the variable value first. Thus it will only add 3 + 3 and b will become 6. Whereas in ++a, we will have to increment the variable first.

Note: We've said probably because you may find the result of 7,10 on some systems. It all depends on the compiler. Thanks to a teacher Malin for pointing out the discrepancy.

On a SPARC machine, the compiler gave the result of 7,10. Why? Some compilers, add up the original value of 'a' and then increment 'a' twice. Equivalent low level code for b = a++ + a++ on some compilers is: -Add 'a' with 'a' and store in a temporary area. -Move value from temporary area to 'b'.

74

Page 75: C++ Basics to Advanced

-Increment value of 'a' twice. This would yield the result as 6. On the SPARC system, the equivalent low-level code created was: -Store 'a' in temporary area. -Increment 'a' (now 'a' is 4). -Add temporary area value with 'a' and store in another temporary area. -Move value from second temporary area to 'b'. -Increment 'a'. Here the value for 'b' will be 7! The next question explores this issue further.

Q.) The following coding:

a=3; int c = ++a; int d = ++a; int b = c + d;

gives b the value of 9 but the following code:

a=3; b = ++a + ++a;

Gives b a value of 10. Why?

A. There are some tricky points to note in these scenarios.

a=3; b = ++a + ++a;

What do you think the compiler will do? As you know, a unary operator will have a higher precedence than a binary operator. The compiler evaluates the expression from left to right. First it increments ‘a’ to 4. Binary operator will operate on two variables. Thus it needs to first find out what is the value of the leftmost ++a + ++a. It will increment ‘a’ once again (from 4 to 5). The common assumption would be that the resultant should be: 5+4 but actually it will be 5+5 (in the first step, it increments ‘a’ from 3 to 4 and then for the second half of the expression it increments ‘a’ from 4 to 5. ‘a’ is the same variable thus the value of ‘a’ is now 5 for both cases). The expression becomes:

b = 5 + 5; or it would be b = 10;

75

Page 76: C++ Basics to Advanced

The overall sequence of events will be:

b = ++a + ++a; b = 4 + ++a; b = 5 + 5; (4 becomes 5 because ‘a’ is now 5) b = 10;

If we extend the problem as:

b=3; b = ++a + ++a + ++a;

and try it on different compilers; you might end up with different results. Compilers will convert these C++ statements we write into machine code.

CASE I.) Some compilers do the following:

When there is a prefix operator in a statement, apply the prefix operator first throughout the statement and then perform other operations.

i.e. in our example: Increment 'a' three times and then then use this value of 'a' in the statement:

b = 6 + 6 + 6; //and b will be 18.

CASE II.) But on some compilers you'll get the result as 16. Why? When they encounter:

a=3; b = ++a + ++a + ++a;

they'll do the following:

b = 4 + ++a + ++a; b = 5 + 5 + ++a; //when the second instance becomes 5 the first also will be 5 (because it is the same variable) b = 10 + ++a; b = 10 + 6; b = 16;

In these compilers (the second case which we saw just now), the placement of parentheses can change the output. Consider the statement:

a = 3; b = ++a + ++a + ++a + ++a;

will give ‘b’ a value of 23. But b=(++a + ++a) + (++a + ++a); will give ‘b’ a value of 24.

76

Page 77: C++ Basics to Advanced

The latest compilers seem to work as explained in our second case. But it is better to avoid writing such code (it gets really confusing and a later time no one will be able to predict the output). Generally in tests the question would be:

b = ++a + ++a;

in which case both types of the compiler will produce the same result.

Tip: Best is to avoid testing candidates/ students with such questions and if you happen to be the candidate and are asked this in an interview you should be able to convince the interviewer that this question has no one right answer. If it's a written test then best of luck!

The conclusion from this is to avoid using the increment operator twice on the variable in an expression.

And the point to remember regarding ++ is: When the prefix operator is used in a larger expression it will return the incremented value while the postfix operator will return the original value of the variable (as was before incrementing).

Q.) In the following piece of coding what do you think is the value of j?

char i=127; int j; i=i+1; j=i; cout<<j;

A.) If you predict 128, then you’re wrong. Note that ‘i’ and ‘j’ are signed quantities. A character has a maximum of 1 byte and thus the maximum positive value it can store is +127. When you increment ‘i’ beyond +127, it will become –128. If you increment it further it will go on as –127,-126 etc. The reason for this was discussed in the 2nd chapter onn data types.

Q.) Are ‘cout’ and ‘cin’ keywords?

A. No. ‘cout’ and ‘cin’ are pre-defined objects. They are not keywords. They are defined in the iostream header file.

77

Page 78: C++ Basics to Advanced

Q.) For each of the subdivisions assume:

int i = 2; int j = 5; int k;

Find out the value of ‘k’ (not all will have values because some are illegal statements).

a. k= (j++); b. k= 2++; c. k= i++ + ++j; d. k = i+++++j; e. k= i = (j = 1); f. 1=2;

A. I'll leave this to the reader!!!

78

Page 79: C++ Basics to Advanced

More questions on the basics (chapters 1 to 3)

Q.) Can we write a program as the one below:

int main( ) { int x;cout<<"\nEnter the value of x:";cin>>x;cout<<"You entered:"<<x; return 0; }

A.) Yes. The program is valid since the compiler breaks up C++ statements on the basis of the semicolon and not on the basis of the new-line. The above program is equivalent to writing:

int main( ) { int x; cout<<"\nEnter the value of x:"; cin>>x; cout<<"You entered:"<<x; return 0; }

Q.) What are tokens? What’s lexical analysis?

What is the output of:

int i=5,j=2; cout<<endl<<(i---j); i=5,j=2; cout<<endl<<(i-- -j); i=5,j=2; cout<<endl<<(i- --j);

A.) Every C++ statement we write can be broken down into tokens (also called symbols). The compiler goes through a number of steps before finally converting high-level language into object code and one of these steps is ‘lexical analysis’ (this stage follows the preprocessing stage). In this stage (lexical analysis) the compiler identifies all the tokens used in the program. Operators, identifiers, keywords etc. are all tokens (white space is not a token but white space helps separate tokens). Every C++ statement is a combination of tokens. For example, in the statement:

a=b+c;

the symbols are: a,=,b,+,c

79

Page 80: C++ Basics to Advanced

a, b, c are identifiers and +, = are the operators.

A question arises, how does the compiler decide how long a token is? How does the compiler know when to consider one character for a token and when to consider more? To simplify this situation, the compiler tries to extract the largest possible chunk in a statement to form a token (in other words it’ll try to extract the largest token while scanning from left to right). In our statement:

a=b+c;

the compiler cannot consider a= as a single token (because it knows that = is the assignment operator). So it picks ‘a’ as a symbol and so on. This might not seem to be a significant point but it’ll help us tackle the second question:

int i=5,j=2;

cout<<endl<<(i---j);

We’ve used three consecutive –'s in i---j. You might be tempted into thinking this would cause a compile error but it won’t because of the way lexical analysis is performed. Remember: always extract largest possible token. In i---j, the compiler will find i-- as the largest token. i--- can’t be considered because we don’t have any such operator and i alone would be the smallest possible token. Another possibility is to chose i- but the compiler is designed to take the largest token and that is: i--.

Next it is left with -j which is equivalent to the unary - operator.

Thus the expression reduces to:

i-- -j;

which is as good as 5-2 (because in i-- we’ve used the postfix decrement operator).

In the next two examples:

i-- -j

and

i- --j

since there is a blank space (white space), the compiler doesn’t have an option of deciding upon the symbol. The resultant output for the code fragment will be:

3 3 4

80

Page 81: C++ Basics to Advanced

Note: In the previous question the following was termed illegal:

k = i+++++j;

In this case, the compiler would break up the statement as:

k = (i++)++ + j;

Upto k = i++ everything is fine, but after that it can’t form a legal token and thus produces a compile time error. It will form:

(i++)++

which is not valid.

It is better to avoid coding statements like i---j even though they produce the desired result (such code is neither pleasing to the eye nor the mind!).

Q.) Deduce the output in both of the following cases:

a.) int x=5; if ( (x= =5) || (x=7) ) { cout<<"True"; } cout<<x;

What do you think is the value of ‘x’?

A.) Since ‘x’ is 5, the first condition is satisfied. When we use an OR operator, the program will not evaluate the second condition if the first is true. Thus the value of ‘x’ will be 5 and the output will be: True5

b.) int x=5; if ( (x= =5) && (x=7) ) { cout<<"True"; } cout<<x;

What do you think is the value of ‘x’?

A.) Since ‘x’ is 5, the first condition is satisfied. When we use an AND operator, the program will evaluate the second condition if the first is true. In the second condition we have assigned a value of 7 to x. Thus the value of ‘x’ will become 7 and the program will output: True7

81

Page 82: C++ Basics to Advanced

Q.) Will the following program (not having an iostream.h) compile and execute? Explain with reasons.

int main( ) { return 0; }

A.) iostream.h is needed when we use cin, cout, <<, >> and similar objects (which we use in most of our programs). Otherwise you can do without iostream.h and the above program will compile and execute successfully.

Interview/Viva/ Test questions (along with the logical questions solved earlier):

1. What is the difference between a compiler and an interpreter? 2. Why does 1Kb = 1024 bytes and not 1000? 3. Distinguish between machine language, assembly language and middle level

languages? 4. How do the post increment and pre increment operator function? Are they the same? 5. How many bits are present in a nibble, a byte and a word? 6. Why do we need to give iostream.h? 7. Is the statement: int x; a declaration or a definition?

Other Questions and Programs:

Q.) What is an interpreter?

Interpreters are programs which will interpret the source code line-by-line and execute them. Compilers, on the other hand, read through the entire source code and convert it into an object code (they do not execute the code). The UNIX/Linux shell is a command interpreter (it executes your instructions on a line-by-line basis). The DOS command.com is an interpreter. Interpreters do not produce object code (which means that each time you run a program in an interpreter, the interpreter will repeatedly check the syntax etc. before executing the code). There are some languages which are called interpreted languages (i.e. these languages are always interpreted). C/C++ is not an interpreted language.

Q.) What is a linker and a loader?

Generally when writing a program, you will make use of different libraries and other source files. On compiling each source file we obtain an object code. All the object codes need to be put together to form an executable (because one code might call a function written in another code and only the linker will be able to resolve this). This is the basic function of a linker.

82

Page 83: C++ Basics to Advanced

A loader is generally a program which loads the program (the executable) into main memory. All our programs and applications reside on a secondary storage device (hard disk) but the processor requires programs to be present in the main memory (RAM)- access time for main memory is much lower than secondary storage (i.e the CPU can access main memory quicker than secondary memory). The loader takes the program from secondary and loads it into main memory.

You will be clear about a linker when we discuss about multiple file programming.

Q.) Distinguish between machine language, assembly language and middle level languages?

Q.) Why do we need compilers?

Q.) What is the difference between a constant and a variable?

Q.) Write a C++ program to input the name and age of the user and then display a sentence on the screen along with these two data.

Q.) Write a program to convert a given amount from US dollars into Indian Rupees (assume the conversion rate as USD 1 = Rs.45). Also display a statement.

Q.) Write a program that will calculate the compound and simple interest on a principal amount for a given number of years (also obtain the rate of interest from the user).

Q.) Write a program to help a shopkeeper calculate the percentage profit he makes when he sells an item (obtain the buying price and selling price as inputs from the shopkeeper).

Q.) Write a program to solve quadratic equations of the form:

ax2 + bx + c = 0

Obtain the values of a, b and c from the user and find out the values for x.

83

Page 84: C++ Basics to Advanced

For Loops in Depth

The following topics are covered in this section:

• Introduction • For Loop Statement • More of For loops • Body of For loops • Nesting For loops

Statement and Expression

A statement in C++ refers to a part of the code that is terminated in a semicolon. It is the smallest part of the program that can be executed. For example:

sum = a + b;

is a statement. A C++ program will consist of a set of statements.

An expression is a grouping of variables, constants, function calls and operators (arithmetic, logical, relational) to return a value. In the above example, a + b is an expression.

While dealing with control mechanisms you will come across blocks of instructions. A block of instruction refers to a set of statements grouped together within a block. The block is identified by using the curly braces ‘{‘ to start the block and ‘}’ to end of the block.

Program flow and control:

What is program flow? Whenever we write a program we have to decide on the sequence of operations that the program has to perform. Generally this is represented in the form of an algorithm. Writing algorithms for simple programs might seem trivial but when you write complex programs, the algorithm helps reduce programming errors. Let’s take a look at a simple algorithm to calculate the average weight of 3 persons:

Step 1: Start the program.

Step 2: Obtain the weight of all 3 persons (in weight1, weight2, weight3).

Step 3: Calculate the average weight as average = (weight1 + weight2 + weight3)/3

Step 4: Display the result

84

Program Control Flow

Page 85: C++ Basics to Advanced

Step 5: Stop the program

By looking at the algorithm a programmer can easily write the entire program. When you have a complex program, if you have written an algorithm then you can eliminate potential logical errors in this stage itself.

In the algorithm we define the way in which the program control should flow. First the program should get the 3 inputs from the user, then it should calculate the value for average and finally it should display the result. Thus we can say that the program flow has been clearly defined. But program flow needn’t always be so simple. You might need to take some decisions and alter program flow.

Control refers to that part of the program which is currently being executed. Let’s write an algorithm to divide 2 numbers obtained from the user.

Step 1: Start the program.

Step 2: Obtain the 2 numbers (num and den) from the user.

Step 3: Check if den is zero. If it is then go to step 4 else go to step 5.

Step 4: Display “Denominator is zero. Cannot perform division”. Go to step 7.

Step 5: Calculate the quotient by dividing num by den.

Step 6: Display the result.

Step 7: Stop the program.

In this example, there are 2 routes the program can take. If the user enters the denominator as 0 then the error should be produced else normal division should be performed.

The pictorial representation of an algorithm is called a flowchart.

Loops

Suppose you want to add the numbers from 1 to 10, what would you do? Of course you could write a long statement that would calculate 1+2+3+4+…+10. What if 10 were changed to 100 or 1000? Whenever there are statements to be repeated you can make use of loops (in fact you should make use of loops). When using loops, some condition should be specified so that the loop will terminate (otherwise the set of statements within the loop will keep executing infinitely).

85

Program Control Flow

Page 86: C++ Basics to Advanced

For loop statement

For loop is used for performing a fixed number of iterations (or repetitions).

The syntax is:

for (initialize-variables; condition-to-test ; assign-new-values-to-variables) { //statements to be repeated (or the body of the loop) }

The ‘for’ loop has three expressions that have to specified.

Initialize variables: This expression is executed only once (when the program flow enters the loop for the first time). It is usually used to assign the loop variable an initial value.

Condition to test: involves relational operators. It is executed each time before the body of the loop is executed. Only if the condition is true will the loop body be executed. If it is false then the loop is terminated and the control passes to the statement following the ‘for’ loop body.

Assign new value to variable: It is executed at the end of every loop after the loop body. Usually it is used for assigning a new value to the loop variable.

Example:

// To find square of numbers from 1 to 10

#include <iostream.h> int main( ) { int var; for (var = 1 ; var<=10; var++) { //beginning of ‘for block’ cout<< "Square of "<<var<< "is" <<var * var; } return 0; // Statement after the ‘for’ loop body. }

In this program, ‘var’ is the loop variable. The loop variable controls the loop. We have initialized this variable to1. The condition that is to be tested before executing the loop each time is:

Is ‘var’ less than or equal to 10 (i.e. var<=10)?

86

Program Control Flow

Page 87: C++ Basics to Advanced

Thus each time var satisfies the above condition the loop body will be executed. ‘var’ is re-initialized with a value at the end of every loop using the expression:

var++

(i.e. incrementing the value of var by 1 each time the body of the loop is executed).

So, to put the program flow in sequence, it would be as follows:

Initially ‘var’ is assigned a value of 1. The condition is tested. Since 1 is less than 10, the body of the loop is executed. In the body of the loop we display the square of var (in this case it is the square of 1). With this, the loop has completed one cycle. In programming terms this is called as ‘one iteration’. The variable ‘var’ is then assigned a new value depending on what is specified in the ‘for loop’. In this program we have written:

var++

Thus ‘var’ is incremented by one. Now, the new ‘var’ value is 2. Again 2 is less than 10 and so the body is again executed with the ‘var’ value as 2. This will be the second iteration.

This process is repeated until ‘var’ becomes 10. When ‘var’ is 10, the condition is again tested. Since 10 is equal to 10 the condition will return a true value (meaning that the condition is satisfied). The loop body is executed again. For the next iteration, ‘var’ is 11. But since 11 is greater than 10 the condition to test returns a false value and the loop body terminates. Program flow goes to the line after the ‘for loop’ body. In this case, it happens to be return 0 (which signals the end of the program).

Beware:

for (var = 1 ; var< =10; var++) //Will lead to an error { cout<< "Square of "<<var<< "is" <<var * var; }

When you make use of relational operators be careful that you do not leave a whitespace between < and =. You should write the coding as:

<= and not as < =

87

Program Control Flow

Page 88: C++ Basics to Advanced

More on ‘for’ Loops

We've seen the usual ‘for loop’ but there are some interesting modifications to the ‘for loop’. The normal syntax for the ‘for loop’ is:

for (initialize-variables; condition-to-test ; assign-new-values-to-variables) { //statements to be repeated (or the body of the loop) }

Have you wondered what will happen if we don't specify one of the three parts of the ‘for loop’?

#include <iostream.h> int main( ) { int i = 2; for ( ; i<8 ; i++ ) // No initialization of loop variable! { cout<<i; } return 0; }

The ‘for loop’ does not have any initialization expression. Will it run? Yes it will because 'i' has been initialized outside the ‘for’ loop. This is perfectly valid and the output would be:

234567

But if you don't specify i = 2 and try to compile the program, the program would still run (but you won't know what value of ‘i’ has been assumed by the compiler). Thus the initialization expression can be left out in a ‘for’ loop if you have initialized the variable with a value before entering the ‘for loop’.

Basically, it is not a must that we have to specify all the 3 parts in a ‘for’ loop. Check out the following program:

#include <iostream.h> int main( ) { int i; for ( i = 1; i != 2 ; i++ ) { cout<<i; } return 0; }

88

Program Control Flow

Page 89: C++ Basics to Advanced

What do you think the output will be?

The output will be: 1

‘i’ starts from 1, when it goes to the value 2 the condition i != 2 becomes false (because ‘i’ is now equal to 2). Whenever the condition becomes false the ‘for’ loop will immediately terminate.

Suppose instead of

for ( i = 1; i != 2 ; i++ )

we had coded as

for ( i = 2; i != 2 ; i++ )

what will happen? Simple, the ‘for’ loop will be terminated instantaneously; in other words the program will not enter the ‘for’ loop even once because the condition becomes false in the first test itself.

Another variation of the for loop is shown below:

for ( ; ; ) { //body of loop will run infinitely without an end unless a break condition is used }

This is called an infinite loop. If you're wondering why one would want an infinite loop; well, you can use it to create time delays or you could specify some condition within the loop to cause it to break out of the loop. Breaking out from a for loop (or an infinite for loop) can be accomplished by using a ‘break’ statement (this will be discussed later).

So far we’ve seen ‘for loops’ using only one loop variable. But you can have ‘for loops’ with two loop variables as well.

#include <iostream.h> int main( ) { int x,y; for (x=1,y=1; x<10,y<5 ; x++,y++) { cout<<"\n"<<x<<y; } return 0; }

89

Program Control Flow

Page 90: C++ Basics to Advanced

Here we’ve made use of two ‘for’ loop variables (x and y). Both are initialized to 1. There are two conditions specified:

x<10, y<5

This actually means that x should be less than 10 and y should be less than 5. If both are satisfied then the loop body will be executed. If even one isn’t satisfied the loop will terminate.

The output will be:

11

22

33

44

Remember: You have to make use of comma (,) to say that you want to initialize x to 1 and y to 1. It is a comma and not a semi-colon (many beginners use the comma and semi-colon interchangeably). Semi colon is used in the ‘for’ loop to separate each of the expressions (to separate the 3 parts of a ‘for’ statement: the initialize, test and re-initialize parts).

You might wonder whether you could use more than two loop variables? The answer is yes. You can use more loop variables but usually you won’t need to use more than 2.

Empty ‘for’ loops: If the ‘for’ loop has no body to execute then it is called an empty ‘for’ loop. For example:

for (int j=0; j<100; j++);

The ‘for’ loop has been terminated by the semi-colon. This ‘for’ loop has no body and it will simply keep incrementing the value of ‘j’ from 0 to 100. This can be used to produce any required delay (because the loop has to be repeated 100 times). Delays can be useful in I/O operations or even while displaying (if the output is appearing too fast).

Body of the ‘for’ loop

The body of the ‘for’ loop is the statement that immediately follows the ‘for’ statement if the body of the loop hasn’t been enclosed within braces. For example:

for ( j = 0; j<10; j++) cout<< "hi"; cout<< j ;

90

Program Control Flow

Page 91: C++ Basics to Advanced

The compiler will only consider the statement:

cout<< "hi";

as belonging to the ‘for’ loop body. Hence the above code fragment would print "hi" ten times and the value of ‘j’ will only be printed once. The above set of statements is equivalent to:

for ( j = 0; j<10; j++) { cout<< "hi"; } cout<< j ;

It is good programming practice to clearly specify the body of loops using braces (the same holds good for ‘while’ loops and ‘if’ statements).

Nesting ‘for’ Loops

The term nesting is frequently used in programming languages. Nesting means one within the other. For example: when we say nesting of ‘for loops’ it means that there is one ‘for loop’ within the body of another ‘for loop’.

for (…..;….;…..) { //body of 1st for loop (called the outer ‘for loop’) for (….; …..; …..) { //body of 2nd for loop (called the inner loop) } }

You might wonder as to how the program executes in the case of two ‘for loops’. Let’s assume that the outer ‘for loop’ has a loop variable ‘j’ that takes values from 1 to 10 in increments of one. Let the second loop variable be ‘k’ assuming values from 1 to 20.

Thus initially j=1, and the program will encounter the second ‘for loop’. Now k=1 and the body of the second (inner) loop is executed. Then k is incremented to 2 (while j is still at 1) and the inner loop is executed again. Next k=3 while j is still 1 and the inner loop is executed once more. When the ‘k’ value reaches 20, the inner loop terminates and only then will ‘j’ be incremented to 2. Again for a ‘j’ value of 2, the inner loop will be repeated 20 times (since k will take values from 1 to 20) and so on.

How many iterations are performed totally? A total of 10 * 20 (=200) iterations are performed using these two ‘for loops’.

91

Program Control Flow

Page 92: C++ Basics to Advanced

While Loop

The following topics are covered in this section:

• While Loop • Do...While Loop

While loop

We saw that the ‘for’ loop has 3 parts in its syntax (initialization, testing and then re-initializing the loop variable). The ‘while’ loop has only one expression; the test condition. The ‘while’ loop keeps on repeating the loop body as long as the loop condition is true.

The syntax is: while (test-condition) { // repeat the statements as long as condition is true }

Before illustrating the ‘while’ loop using an example, you should know an important fact about relational operators and characters. You might recall that a character can hold a single character. We have seen comparison of numbers using relational operators but what about comparing characters? There are many instances when you will want to check whether the user entered a particular character. For example: you may want to check whether the user entered a y (for yes) or an n (for no). Depending on what was entered your program should continue on two different lines. If ‘letter’ is a character variable, we will obtain the input from the user using the statement:

cin>>letter;

If you want to check whether the user has entered a ‘y’ or an ‘n’ you can do the following:

if (letter = = ‘y’) { //body of if statement )

You should not compare characters as shown below:

if (letter = = y) //WRONG

This method of comparing a character variable with a character constant is WRONG. It will yield an error. The character constant should always be enclosed in single quotes.

Beware: Beginners often forget the single quotes while using character constants.

92

Program Control Flow

Page 93: C++ Basics to Advanced

//To find the square of a given number # include <iostream.h> int main( ) { char reply; int num, square; cout<< "Do you want to find the square of a number(y/n)? "; cin>>reply; while (reply = = 'y') //No blank-space between the two equal signs. See note. { cout<<"\nEnter the number : "; cin>>num; square = num*num; cout<< "The square is : " <<square; cout<< "\nDo you want to square another number (y/n)? "; cin>>reply; } return 0; }

Note: A blank space has been left between the two ‘equal to’ symbols just to make it clear that there are two equal signs. Do not leave a space between the two = symbols when you write your program.

At the start of the program, if the user types a ‘y’ the program will execute the loop body. At the end of the while loop, the program will prompt the user to enter the value for ‘reply’. If the user again types ‘y’, then the program will execute the while body a second time. Hence, at the end of each loop the program obtains the value for ‘reply’ and checks whether the test condition is true. As long as the test condition is true, the while loop is executed over and over again.

The difference between the ‘while’ loop and the ‘for’ loop is that the ‘while’ loop does not have a fixed number of repetitions of the loop body. As long as condition is true it keeps executing the loop body. Usually the ‘while’ loop is used when the programmer is not sure about the number of iterations that need to be performed. The ‘for loop’ is used when the programmer knows how many iterations are to be performed. Of course, the use of both can be interchanged as well. In fact, a ‘for loop’ can be modeled into a ‘while loop’. The equivalent ‘while loop’ for a ‘for loop’ will be as below:

Initialize a variable value; while (condition involving the variable) { //body of the loop //assign a new value to the loop variable; }

93

Program Control Flow

Page 94: C++ Basics to Advanced

If we use the coding:

while(1= = 1) { //body of loop }

the while loop becomes an infinite loop (because 1 is always equal to 1).

Do…While loop

The ‘do…while’ loop is a modification of the ‘while’ loop. If you noticed in the earlier program for the ‘while’ loop, initially we have to ask the user whether he/she wants to find the square of a number. Only if the user types a ‘y’ will the program enter into the ‘while’ loop. Hence in the program we have to ask the user twice whether he/she wants to square a number (once outside the loop and once inside the loop). The ‘do while’ loop, eliminates this repetition.

When you make use of the ‘do-while’ loop, the body of the loop will be executed at least once. After the first iteration, if the loop condition holds true then the loop is repeated again. Thus the first iteration is compulsory in the ‘do…while’ loop.

The program to find the square of a number can be re-written as follows:

#include <iostream.h> int main( ) { char reply; int num, square; do { cout<<"\nEnter the number : "; cin>>num; square = num*num; cout<< "The square is : " <<square; cout<< "\nDo you want to square another number (y/n)? "; cin>>reply; }while (reply = = 'y'); return 0; }

In this program, the body of the loop will be executed once for sure. After the first iteration, the user has the option of terminating the program or continuing to use the program for squaring another number.

Beware: when using the ‘do…while’ loop, make sure that you put a semicolon at the end of the while statement: while (reply = = 'y');

94

Program Control Flow

Page 95: C++ Basics to Advanced

Decision Statements (IF)

So far we have seen statements that help you in repeating a particular task as long as you desire. Another important form of program flow control is to be able to make a decision as to how you want the program to continue. Statements of this kind are referred to as decision statements (sometimes also called selection statements). The following topics are covered in this section:

• If else • Nested If • Conditional Operator

If…Else If…else…

‘if-else’ is one of the decision statements available in C++. This enables the programmer to provide different paths for the program flow depending on certain conditions. For example: consider a program for dividing two numbers. Division by zero will lead to an error. To avoid this from happening, after obtaining the two numbers from the user the programmer will want to ensure that the denominator is not zero. Hence there needs to be two different program flow options. If the denominator is zero a message saying, "Division not possible" should be displayed. Otherwise the program should carry out the division and display the results. In simpler terms this can be stated as:

If the denominator is zero, then display a message and do not divide

else perform division and display the output.

Syntax:

if (condition) { //statements to be executed; } else { //statements to be executed; }

The program flow is as follows: If the condition being tested within the ‘if’ statement is true, then the body of ‘if’ will be executed otherwise the body of ‘else’ will be executed. Thus the program takes a decision as to what it should do next.

95

Program Control Flow

Page 96: C++ Basics to Advanced

You can also make use of the ‘if’ statement alone (‘else’ is not compulsory) or you can even make use of a series of ‘if...else’ statements as follows:

if (condition1) { //body } else if (condition2) { //body } else { //body }

Example:

// To find the greater among two numbers #include <iostream.h> int main( ) { int a, b; cout<< "Enter the two numbers you want to compare : "; cin>>a>>b; if (a = =b) { cout << "The two numbers are equal"; } else if (a>b) { cout <<a<< " is greater than "<<b; } else { cout << b<< " is greater than "<<a; } return 0; }

In the above program, we have used a series of ‘if…else’ statements. The first condition is:

if (a = =b)

so the program will check whether ‘a’ is equal to ‘b’. If it is equal then the body of the ‘if’ statement will be executed. Once the statement is executed, the compiler will not bother about

96

Program Control Flow

Page 97: C++ Basics to Advanced

the next ‘else if’ and ‘else’ statements because one condition has been satisfied. Program flow will then go to return 0;

If the first ‘if’ condition is not satisfied, the program control will go to the following ‘else if’ statement: else if (a>b)

Even if this is not satisfied then only will it go to the next statement, which is an ‘else’ statement. Since ‘a’ was not greater than ‘b’, and equality was also tested earlier the only possibility is that ‘b’ is greater than ‘a’. Thus if the first two conditions have failed then the program flow will go to the body of the ‘else’ block.

Remember: Using the ‘if…else..if’ format, only one of the bodies will be executed (not all). If one condition is satisfied the rest of the ‘else..if…else’ is ignored.

When we use the ‘if..else if’ construct, if one of the conditions is satisfied that corresponding body will be executed and the rest will be ignored. But what would happen if we use a series of ‘if’ statements alone?

#include <iostream.h> int main( ) { char letter; cout<<"\n Enter the alphabet 'a' or 'A': "; cin>>letter; if (letter= ='a') { cout<<"\n You entered an 'a'"; } if (letter= ='A') { cout<<"\n You entered an 'A'"; } return 0; }

In the above program, the compiler will check for ‘a’ first and then it will check for ‘A’ also. Even if the first condition is satisfied, it will still check for the second ‘if’ condition. In such cases it would be better to use the following:

if (letter= ='a') { cout<<"\n You entered an 'a'"; } else if (letter= ='A') { cout<<"\n You entered an 'A'"; }

97

Program Control Flow

Page 98: C++ Basics to Advanced

Now if the user enters an ‘a’ then the program will not enter the ‘else if’ statement to check whether the letter is an ‘A’.

Remember: It is better to make use of ‘if-else-if’ instead of a series of ‘if’ statements because in that way your program need not check all the conditions unnecessarily.

And whenever you use a series of ‘if-else-if’ statements, test the condition that is most likely to be true first (so that the program need not waste time in checking more conditions).

Nested If

You can have an ‘if’ statement within another ‘if’ statement. This is known as nested ‘if’. The program flow will enter into the inner ‘if’ condition only if the outer ‘if’ condition is satisfied. In general form nested ‘if’ will be of the form:

if (condition1) { //code to be executed if condition1 is true if (condition2) { //code to be executed if condition2 is true } }

There can be more than one ‘if’ condition (i.e. you can nest as many ‘if’ statements as you want). An example code fragment is given below (‘month’ and ‘year’ are integer variables whose values are obtained from the user):

if (month= =2) { cout<< "The month is February"; if ( (year%4) = = 0) { cout<< "This month has 29 days."; } else { cout<< "This month has 28 days."; } }

In the above code fragment, only if the variable ‘month’ is equal to 2 will the program enter into the ‘if’ block. Within this block it will print:

The month is February

98

Program Control Flow

Page 99: C++ Basics to Advanced

Then it will check as to whether the given year is a leap year or not. If it is a leap year then it will display that the month has 29 days else it will say the month has 28 days.

Empty ‘if’ statement:

We have seen the use of empty ‘for’ statements but empty ‘if’ statements might not be useful. In fact empty ‘if’ statements are usually logical error (because the programmer places the semi colon by mistake at the end of the ‘if’ statement).

Example:

if (num1<num2); {cout<< "num1 is less than num2";}

Since the ‘if’ statement has been terminated the display will be produced irrespective of the values of ‘num1’ and ‘num2’.

Checking multiple conditions:

In mathematics you will encounter expressions such as: 30<y<40. This actually means that the value of ‘y’ is greater than 30 and less than 40. In C++ you cannot use such an expression directly. You cannot write:

if (30<y<40)

because you cannot cascade relational operators. You have to mention the conditions separately and combine them using the logical operators.

if ( (30<y) && (y<40) )

Conditional Operator (?:)

The conditional operator is a ternary operator and this operator is an alternative to ‘if…else’ statements. The syntax is as follows:

test-condition ? value1 : value2;

The test condition will be evaluated and if it is true then ‘value1’ will be the value for the expression. If the condition is false then ‘value 2’ is the value for the entire expression. For example consider the coding:

a=5; b=6; y = a>b?10:20;

99

Program Control Flow

Page 100: C++ Basics to Advanced

The value of y will be 20 because ‘a’ is not greater than ‘b’. This sort of expression can be written using the ‘if’ statement as:

a=5; b=6; if (a>=b) { y = 10; } else { y = 20; }

Beware: It’s just that you could reduce the amount of coding by using the ternary operator. Be careful that you don’t confuse the logic while trying to reduce the length of the code!

100

Program Control Flow

Page 101: C++ Basics to Advanced

Decision Statements (Switch Case)

There are many instances wherein you may want to test for a series of conditions one after the other. For example: Suppose you obtain the input of month from the user in as a number and you want to display the corresponding name of the month; what would you do? You could write a series of twelve ‘if….else if….else if…else if…’ statements.

The ‘switch…case’ format provides a convenient alternative to using multiple ‘if-else’ statements when you are testing for a single variable alone. The switch statement successively tests the value of a variable against a list of integer or character constants. If a match is found then the statement associated to that particular case will be executed.

First of all, before entering the switch case statement, we have to obtain the value of the switch variable from the user. The switch variable refers to the variable whose value you want to check. In the case of converting numbers into corresponding months, the switch variable will be the month number.

Syntax:

switch (variable/expression that evaluates to an integer) { case char/integer-constant : { //body } case char/integer-constant : { //body } }

101

Program Control Flow

Page 102: C++ Basics to Advanced

For example:

# include <iostream.h> int main ( ) { int month; cout<< "Enter the month of the year: "; cin>>month; switch (month) // month is the switch variable { case 1 : // if month is 1 then the statements are executed { cout<<"The month is January"; break; } case 2 : { cout<<"The month is February"; break; } //write case statements for 3 to 10 just as shown above case 11 : { cout<<"The month is November"; break; } case 12 : { cout<<"The month is December"; break; } default : // If value of day is something other than 1 to 7 { cout<<"You entered an invalid number"; break; } } return 0; }

As you can see, the compiler gets the value of the variable ‘month’ from the user. This is called the ‘switch variable.’ If the value of ‘month’ is 1, then the compiler performs what is specified under case 1. If the user enters 2, then the output will be February and so on. Since a year has only 12 months, if the user types 0 or 14 then the program should display that the user has typed the wrong number. The ‘default’ statement is used for this purpose. The ‘default’ statement

102

Program Control Flow

Page 103: C++ Basics to Advanced

provides the program with an option to do something in case the switch variable does not match any of the case constants.

In this program, ‘month’ is called the switch variable and the integer constants from 1 to 12 are called case constants (i.e. they are the values for which the switch variable is tested).

Break: It causes an exit from the switch construct (body). For instance, after printing that "The month is January" you don’t want the compiler to go into the other cases. Hence you ask it to break out of the ‘switch…case’ body. If there is no break for each case then the program will perform all the remaining cases as well.

Try it: Remove all the break statements from the above program and execute your program. If you now type a value of 1, the program will print:

The month is January

The month is February

…and so on…

Beware: A mistake that beginners commit is that they tend to forget the ‘break’ statement. Always use ‘break’ after each case and also make sure that you have a ‘default’ option in your ‘switch…case’ body.

The ‘default’ case is executed only if the user enters a value other than the case constants. It does not matter whether you place the default case at the starting of the switch-case body or at the end (but usually programmers prefer to place the default case at the end of the switch-case construct).

Suppose you want to test the switch variable against a set of character constants then ensure that you enclose your case constants within single quotes.

Suppose the switch variable ‘month’ is a character then the program would be:

switch (month) { case 'a' : //‘a’ is within single quotes because it is a character constant { //body of the case break; } //the remaining cases }

Beware: There can be an expression in the switch part but it should evaluate to an integer. The switch variable and the case constants should be integers (or characters). You should not use other data types.

103

Program Control Flow

Page 104: C++ Basics to Advanced

Controlling flow within a loop statement

We’ve seen a few ways of looping within a program. There are a few occasions when you might want to break out of a loop when some particular condition occurs. Or in other words you may not want to go through the all the iterations within a ‘for loop’. Or you may want to break out of the loop for just one particular value of the loop variable. C++ provides a mechanism to break from loops using the ‘break’ and ‘continue’ statements. These are also called as ‘jump statements’. The following topics are covered in this section:

• Break • Continue • Go To • Return • Apply what you've learnt • Recap of the entire Unit

Break

We’ve already seen the use of the ‘break’ statement in the ‘switch…case’ construct. A ‘break’ statement can also be used to terminate (or break out) from a loop (like the ‘for’ and ‘while’ loops). Suppose you are using nested loops, then a ‘break’ specified in the body of the inner loop will lead to breaking out from the inner loop alone.

#include<iostream.h> #include<stdlib.h> int main ( ) { int x, i; for (i = 0; i<15; i++) { x = rand( ); if (x>500) { cout<<"\n Iteration number "<<i<<" random value is : "<<x; cout<<"\n\n exceeded 100 in iteration number "<<i; cout<<"\n Breaking out from loop"; break; } cout<<"\n Iteration number "<<i<<" Random value is : "<<x; } return 0; }

104

Program Control Flow

Page 105: C++ Basics to Advanced

First of all, in the above program the library function rand( ) has been used. This function is defined in the header file stdlib.h. rand ( ) is used for generating random numbers.

Can you find out what’s the logic of the above program? The ‘for’ loop is supposed to run 15 times. But within the ‘for’ loop we’ve specified a condition wherein if the random value generated exceeds 500 the program will break out of the ‘for’ loop.

The output for the program will be:

Iteration number 0 Random value is : 41

Iteration number 1 random value is : 18467

Exceeded 100 in iteration number 1

Breaking out from loop

Continue

Sometimes you may not want to break out of a loop permanently. You may want to abort one particular iteration and then continue with the remaining iterations. For this purpose, the ‘continue’ statement is used. Let’s suppose that you have a ‘for’ loop with a loop variable named ‘j’ and you want the loop to run 10 times except for the value of j=5. In other words, you want the loop to run with all ‘j’ values except the value of 5. Hence, when the compiler enters the loop with the ‘j’ value of 5, the loop should terminate temporarily and then continue with the next ‘j’ value.

Consider a simple example:

int main ( ) { int j; for (j=1; j<8; j++) { if (j= =5) { continue; } cout<<"\n This is Round "<<j; } return 0; }

The output would be:

105

Program Control Flow

Page 106: C++ Basics to Advanced

This is Round 1 This is Round 2 This is Round 3 This is Round 4 This is Round 6 This is Round 7

Notice that "This is Round 5" is not displayed because that iteration was not performed.

Remember: ‘Break’ is used to terminate permanently from a loop whereas ‘continue’ is used to terminate only a particular iteration.

Go To

To use the ‘goto’ statement you should use ‘labels’. We’ll start with an example:

#include <iostream.h> int main ( ) { int i; i = 1; LOOP : if (i<10) { cout<<i; i=i+1; goto LOOP; } return 0; }

The above program will display the following:

123456789

LOOP is a label. When the compiler encounters goto LOOP, it will go to the label named LOOP and continue executing from there. The logic of the program should be easy to comprehend. The above program can be easily written using a ‘for’ loop.

A few beginners use the ‘goto’ statement excessively instead of using the other looping options available. Of course there’s nothing wrong with goto but it could lead to confusion when you start using many ‘goto’s in your program. It is advisable and good programming practice to avoid the use of ‘goto’ statements (unless it is really needed).

Beware of jumping initializations using GOTO:

106

Program Control Flow

Page 107: C++ Basics to Advanced

Check out the following code, which makes use of the goto statement:

#include <iostream.h> int main( ) { char ans; cout<<"Enter y/n : "; cin>>ans; if (ans=='n') { goto done; } int x=5; //ERROR done: cout<<x; return 0; }

In C++, a variable can be declared and initialized anywhere in the C++ code. You can even declare and initialize a variable just before the statement return 0;. But the above program will lead to an error in compilation. Why? In C++, the goto statement should not jump (or skip) a declaration and initialization.

So, if you replace int x=5; by

int x; x=5;

the compiler will accept the program.

Try it: Try the above program with the modification and give an input of ‘n’ and see what happens. The result will be some weird answer and NOT 5 because the goto statement will bypass the initialization of the variable ‘x’ and hence you get an ambiguous answer. Be very careful when using ‘goto’ in your program.

Return

The ‘return’ statement will be covered while discussing about functions. When using any looping technique within a function, the return statement can be used to break out from the loop and return control to the caller. We shall look at this later. For the time being just remember that the ‘return’ statement can be used to break out from a loop.

107

Program Control Flow

Page 108: C++ Basics to Advanced

Apply the techniques

Let’s write a program that covers most of the techniques that we’ve learnt.

Problem: Write a program in C++ to obtain a date from the user. The user will enter the date in the format: dd/mm/yyyy. Our program should print the corresponding month and also the number of days in that particular month. The program should also inform the user as to whether the year is a leap year or not. For example if the user enters: 11 10 1980 then the display should be:

October 11,1980.This month has 31 days. This is a leap year.

Solution:

1. First obtain the inputs in three different variables (i.e. the day, month and year). 2. Write a ‘switch case’ statement to check for the month. 3. Within the case statements print the month and also set some other variable to the

number of days present in that month. 4. Outside the ‘switch case’ block, print the date. 5. Using an ‘if’ condition check whether the year is divisible by 4 (which means it is a

leap year).

#include <iostream.h> int main( ) { int d,m,y,days; cout<<"\nEnter the Date (DD MM YYYY): "; cin>>d>>m>>y; switch(m) //To print the month { case 1: cout<<"\nJanuary"; days=31; break; case 2: cout<<"\nFebruary"; break; case 3: cout<<"\nMarch"; days=31; break; //…write the remaining cases case 11: cout<<"\nNovember"; days=30; break; case 12:

108

Program Control Flow

Page 109: C++ Basics to Advanced

cout<<"\nDecember"; days=31; break; default: cout<<"\nInvalid month"; return 0; //quit the program! break; } cout<<" "<<d<<","<<y<<"."; //display the date. if (m==2) //February could be 28/29 days { if(y%4==0) { days=29; } else { days=28; } } cout<<"This month has "<<days<<" days."; //display number of days in the month if (y%4==0) //check whether it is a leap year { cout<<" This is a leap year."; } else { cout<<" This is not a leap year."; } return 0; }

A leap year is actually a year that is divisible by 4 and not by 100 or a year that is divisible by 400. You can add these two conditions in the above program. More questions are given in the exercise section for this chapter.

109

Program Control Flow

Page 110: C++ Basics to Advanced

Recap

• A C++ statement is an executable line of coding and it is terminated in a semi-colon. • For performing any repetitive task, looping techniques are used. • The various looping options available are ‘for’, ‘while’ and ‘do-while’ loop. • The ‘for’ loop is used when the number of iterations are known. • The ‘do-while’ loop will execute the loop body once and only then will it check for

the test condition. • For taking decisions the programmer can use: ‘if-else’ statements, ‘switch-case’

statements. • It is possible to break out from a loop using either the ‘break’ or ‘continue’ statements.

The ‘goto’ statement is less frequently used in C++ programming. • ‘break’ statement is used to terminate a loop while the ‘continue’ statement is used to

terminate specific iterations. • The loop statements as well as the decision statements can be nested.

110

Program Control Flow

Page 111: C++ Basics to Advanced

Functions Intro

The following topics are covered in this section:

• How to use functions • Parameters and Arguments

What is a function?

A function is a group of statements within a single unit. Basically, it is a block of executable statements grouped together which will be executed when the function is called. They are the building blocks of a program. You may wonder what’s the use of a function is? Or why not write the entire program within the main function itself?

Suppose in your program you have to repeat a certain set of statements at different instances, it wouldn’t be a good idea to keep repeating the same statements throughout the entire program. It will be ideal to place these statements within a function and use a single statement to call the function wherever needed (this approach makes it simpler to understand the program logic). Functions provide means for breaking down a large program into smaller modules. Using functions will also help in debugging programs (identifying and correcting errors).

The general syntax for a function declaration is:

return-data-type function-name (parameters);

The return data type specifies what sort of data the function will return to the calling process. For example if the return data type is ‘int’, then the function will return an integer value to the calling process. If the return data type is void, this means that the function does not return any value. What is a calling process? Every function has to be called by someone. A function will execute only when it is called. In C++, functions can be called by the main ( ) function or by some other function that you have defined. The main ( ) function is the part of your program that will execute and usually you will call the other functions from the main ( ) function.

You may wonder, the main ( ) itself is a function, then who calls the main ( ) function? The main ( ) function is the main part of your program and it is called by the operating system (since your OS will run the program).

There are three components for a function, namely:

1. Function Declaration or Prototyping 2. Function call and 3. Function definition.

111

Functions

Page 112: C++ Basics to Advanced

Function Declaration (Prototyping):

Declaration means informing the compiler that a function, with this name and which looks like this, will appear later on in the program. It is a statement that specifies the function name, parameter data types and return data type. Function declaration is always done before (and outside) the main ( ) function block. The syntax for declaring a function is:

return-data-type function-name (data-type-of-parameters); Note that the function declaration should have a statement terminator.

Function Definition:

Once the compiler knows that you have declared a function, it then needs the definition for that function. The function definition contains the code to be executed whenever the function is called. The definition tells the compiler what it has to do when it encounters that function call.

The first line of the function definition is the same as the declaration and is called the ‘declarator’ (it is also called the function header). Don’t confuse declaration with declarator. The function header is very similar, in appearance, to the function declaration (or prototype) except that it does not have the statement terminator. The function header must agree with the prototype (i.e. it must have the same function name and the same data types of parameters in the same order and the also the same return data-type).

The general syntax of the three components of a function are as follows:

// Function declaration

return-data-type function-name (data type of parameter1,data type of parameter2) ;

int main ( ) { function-name (arguments); //Function call within main( ) }

// Function definition (outside main( ) )

return-data-type function-name (arguments) // Declarator/ Header { // Function Code or Body of function }

Parameters and arguments will be dealt with later in this chapter. Let us consider an example to illustrate the three basic components of a function.

112

Functions

Page 113: C++ Basics to Advanced

// To add two numbers #include <iostream.h> void add ( ); // Function Declaration int main( ) { add( ); // Function Call return 0; }

void add( ) // Function header { int num1,num2,sum; // Body of the function cout<< "Input the two numbers : "; cin>>num1; cin>>num2; sum = num1 + num2; cout<<"The sum is "<<sum; }

As you can see, we have first declared that there is a function named ‘add’ with the return data type as ‘void’ and with no parameters. When ‘void’ is used as the return data type it means that the function will not return anything to the caller.

Remember: The compiler will always execute what you type in the main ( ) function. You can execute other functions by calling them from within the main ( ) function.

We call the function add( ) from within the main ( ) function. When the compiler encounters the function call statement, it recollects that you had already declared a function by the same name and argument type. So it goes on reading your program assuming that you have defined the function later in the program. In the definition we have written a set of statements to find the sum of two numbers. The compiler will now know what to execute when it encounters the add ( ) function. After displaying the result (due to the presence of ‘cout’ in the ‘add’ function), the program flow will go back to the statement following the function call in the caller. In this case the caller is main ( ) and the next line is

return 0;

113

Functions

Page 114: C++ Basics to Advanced

There are some finer details that you should note. When a function calls another function, the caller does not terminate. Instead it waits for the called function to return back control. The caller sleeps till it receives control again. There is some overhead involved in this process because the program needs to store extra information to know exactly where to return to. The program also needs to keep track of variables used in the calling function because once control is returned back to the caller, these variables can be used. This might seem confusing but just remember that extra operations (overhead) is involved in calling functions. You’ll understand this better once you learn recursion. Coming back to our example, the function declaration will tell the compiler that a function by that name is coming up later on in the program. There is a way to avoid writing the function declaration; i.e. there is no need to declare the function. In the above program the function definition comes after the function call. If you do not want to declare the function, the function definition must come before (precede) the function call. Let us consider the same program to add two numbers using a function called add with no arguments.

Example 2:

// When function definition precedes function call #include <iostream.h> void add( ) { int num1,num2,sum; cout<< "Input the two numbers : "; cin>>num1; cin>>num2; sum = num1 + num2; cout<<"The sum is "<<sum; }

114

Functions

Page 115: C++ Basics to Advanced

int main ( ) { add( ); return 0; }

As you can see, in the above program there is only one small change. The function add ( ) hasn’t been declared because it has been defined before the function call. The compiler will initially itself read the function definition and hence it knows two things before entering the main ( ) function – it knows that a function with the name ‘add’ will come up later in the program and it also knows what to do when it encounters the add ( ) function (because we’ve already defined the function).

Parameters and Arguments

Argument is a piece of data passed from the program to the function. Arguments allow a function to operate with different values. The variables used within the function to hold argument values are called parameters. The data type of parameters in the function declaration and data type of arguments in the function call should be the same.

Consider the following program to explain arguments. Again consider the same program to add two given numbers but using function arguments.

#include <iostream.h> void add (int var1, int var2) // Parameters var1 and var2 { int var3; var3 = var1 + var2; cout<<"The sum of the two numbers is "<<var3; } int main( ) { int num1, num2; cout<< "Input the two numbers "; cin>>num1; cin>>num2; add(num1,num2); //Arguments num1 and num2 return 0; }

Two arguments have been passed to add ( ) from the main ( ) function. Observe the function ‘declarator/header’. The parameter specified is ‘int var1’ and ‘int var2’. This means that in the function named ‘add’, ‘var1’ and ‘var2’ are integers that are used within the body of the function. Within the function body we have declared a third integer called ‘var3’. var1 and var2 are added and stored in var3 using the statement :

115

Functions

Page 116: C++ Basics to Advanced

var3=var1 + var2;

Now the question might arise, what are the values for var1 and var2? In the main ( ) function the value for two integers ‘num1’ and ‘num2’ are obtained from the user. After obtaining the values, the following statement is encountered:

add(num1,num2);

This statement is the function call. Two arguments namely ‘num1’ and ‘num2’ are passed through this function call. Since the function was declared and defined as having two parameters (and since the data types of the parameters and the arguments are the same), the program will execute the code of the add ( ) function with the value of ‘var1’ being that of ‘num1’ and value of ‘var2’ being equal to ‘num2’. If the data types or number of arguments passed do not match with the parameters, the compiler will produce an error message.

Effectively, var1=num1 and var2=num2. The body of the function ‘add’ is now executed with the values of ‘var1’ and ‘var2’. Thus the arguments are passed from the program (which is the main ( ) function) to the parameters of the function ‘add’.

The variables used to hold the argument values are known as parameters. The function declarator in the function definition specifies both data type and name of parameters. In our example, the parameters were ‘num1’ and ‘num2’. Their data types were int (integer).

Remember: The values of ‘num1’ and ‘num2’ are passed to ‘var1’ and ‘var2’. The function is operating only on ‘var1’ and ‘var2’ (it does not operate on ‘num1’ and ‘num2’).

If we had declared the add ( ) function in the program, it would have been as follows:

void add (int, int);

In the function declaration you don’t have to specify the parameter names (just specifying the data types of the parameter is sufficient).

Remember: You pass arguments to parameters.

116

Functions

Page 117: C++ Basics to Advanced

A closer look at Functions

The following topics are covered in this section:

• Default Arguments • Return Values • Returning void • int main and void main ( ) • exit( ) • using return to break out from loops

Default Arguments

While using functions with parameters, you can specify default argument values (i.e. in case arguments are not passed to the function then the function will assign the default values to the parameters).

#include <iostream.h> void add (int var1=5, int var2=10) // default values 5 and 10 {

o int var3; var3 = var1 + var2; cout<<endl<<"The sum of the two numbers is "<<var3;

} int main( ) {

o add( ); //default arguments 5 and 10 used add(1,2); // arguments are now 1 and 2 add(1); // arguments are 1 and 10 return 0;

}

The output will be:

The sum of the two numbers is 15 The sum of the two numbers is 3 The sum of the two numbers is 11

117

Functions

Page 118: C++ Basics to Advanced

If the programmer specifies arguments then the default arguments are not used. Some points to remember are:

• If you are declaring a function, it is sufficient to specify the default arguments in the function declaration (function prototype) alone.

• The default arguments should be the rightmost in the parameter list. You cannot use:

void add (int var1=5, int var2)

• Default arguments are used when the programmer feels that those values will be frequently passed to the function.

Return Values

So far we have only seen functions with ‘void’ as the return data type. In this section you will learn about how to return values from functions. ‘void’ means that no value is returned by the function.

Return value, as the name suggests, is used to return some value from the function to the caller process. When a function completes its execution it can return a single value to the caller. ‘return’ serves two purposes:

• It gives back the program control to the caller. • It can also return a value to the caller.

The syntax is:

return variable-name;

This statement is specified at the end of the function body before the closing brace. When a function returns a value, the data type of the return value must be specified in the function header as well as in the function declaration.

Consider the following program that uses a function to return a value:

// To add two numbers using return value

#include <iostream.h> int add (int , int); // Declaration int main ( ) { int sum, var1, var2; cout<< "Enter the two numbers"; cin>>var1>>var2;

118

Functions

Page 119: C++ Basics to Advanced

sum = add(var1,var2); //Function call cout<< "The sum of the two numbers is "<<sum; return 0; } int add(int num1, int num2) // Function Definition { return num1 + num2; }

In the above program the statement of interest to us is

sum = add(int num1, int num2);

The right side of the equal sign is actually the function call. This is being assigned to a variable called ‘sum’. Hence, the function ‘add’ has to be executed and some value should be returned. This returned value is assigned to ‘sum’.

In the function header instead of ‘void’ we have mentioned ‘int’ as the return data type. This means that the function ‘add’ will return (or give back) an integer quantity to the calling program (in this case the caller is the main ( ) function). Therefore, the sum of ‘num1’ and ‘num2’ will be returned to main ( ). You have to mention the return data type in the function declaration also.

Remember: The data type specified in the function declaration should match the return data type in the function definition. Make sure that the data type of the variable you are returning to (in the above case this is the variable ‘sum’) also belongs to the same type as the return data.

What would happen if you return a value from a function but don’t use it? Will it cause an error? What if you don’t assign the returned value to any variable? Is it a problem? It is entirely your choice as to whether you want to use a returned value. If you don’t assign the returned value to the variable then the compiler simply ignores the return value. It will not be stored anywhere unless you assign it to some variable.

Returning Void

Once a function returns a value, the function will terminate and program flow will be transferred back to the caller. When a function has a return data type of ‘void’ we needn’t specify the return statement at the end of the function body. The function will terminate at the end of the function body (i.e. when it encounters the closing brace of the function body). If you would like to use a return value in for a function that has void as the return data type, then you can type:

return;

119

Functions

Page 120: C++ Basics to Advanced

No value is returned in this case to the caller but program flow is handed over back to the caller program when the ‘return;’ statement is encountered. The following program illustrates this concept of returning void from a function:

#include <iostream.h> void convert(int dollar) { if (dollar<0) { cout<<"\nCan't convert negative amount!"; return; } cout<<"\nThe equivalent money in Rupees is : "<<46*dollar; } int main( ) { int amount; cout<<"\nEnter the amount in dollars you want to convert: "; cin>>amount; convert(amount); return 0; }

The output when you type a positive value is:

Enter the amount in dollars you want to convert: 5

The equivalent money in Rupees is : 230

The output when you enter a negative value is:

Enter the amount in dollars you want to convert: -1

Can't convert negative amount!

As can be seen from the output in the second case, once the return statement is encountered the program will stop executing the function (thus it does not calculate the value of 46*dollar if the value of dollar is negative).

120

Functions

Page 121: C++ Basics to Advanced

void main ( ) and int main ( )

The main( ) function can be written in one of two ways. Either you can use:

void main ( )

or you can use:

int main ( ) { //code return 0; }

int main ( ) returns an integer value while void main ( ) doesn’t return anything. Consider the following program:

# include<iostream.h> int main ( ) {

int test; cout<<"Enter a value :"; cin>>test; if (test= =1) { cout<<"You typed 1"; return 1; } cout<<"This line is displayed if you typed a value other than 1"; return 0;

} What do you think happens when the user types 1 as the value of ‘test’?

Since ‘test’ is equal to 1, the compiler will go into the body of if. It will display "You typed 1" on the screen. Then the compiler will return a value of 1. Generally a return value of a non-zero number is used when errors are encountered. You can't use this return value anywhere else in your program (Why? Because the caller for the main ( ) function is your operating system). In other functions (functions other than ‘main’ ), you can make use of the return value. After returning the value, the program exits. This means that the compiler will not even read the remaining few lines and hence nothing else will display on the screen. The compiler will quit once any integer has been returned by the main ( ) function.

If you type anything other than 1 for test, the compiler will skip the ‘if’ body, display the last statement and then return 0.

121

Functions

Page 122: C++ Basics to Advanced

Better to use int main ( )?

We could now delve one step further into the topic. Which one is better? Or which one should we use and why? Void is generally used to declare functions that do not return values.

When you use int main ( ), the main function returns an integer to the operating system (since the OS is what calls the program and in turn the main function). Returning a value from main ( ) is like an exit function. The value is returned to the operating system and the program terminates. What will the OS do with your returned value? Actually, the OS never wants to know what you return. The program which started your program might need to know. In any OS, a program when running is called a "process". When booting up, the operating system starts the first process (called "init" in UNIX systems). Thereafter the first process starts other processes such as a shell (shell is just a program, which reads commands from the user and converts it to system calls). So when you write a program and execute it in the shell, the shell starts your program. So now, the shell is the parent process of your program (and your program is the child of the shell). Now, in the same way, suppose you want one of your programs to load another program to do a particular job and you want to know just whether the job was successful or not, then the OS gets the exit code of the child process and gives it to the parent process; just like returning from a function. So it is a standard to give the exit code as '0' for a success and any non-zero integer for an error. When programming in C/C++ you can give this exit code when you return from the main function. So you've to declare main as 'int main( )' to do that. If you declare it as 'void main( )' C++ wont allow you to set a value to be returned to your parent program. So the variable which should contain the return code will be filled by nothing, which means that memory can be in any state (unpredictable). Hence you have no control on what value your parent process gets when your child program exits. This is not just for UNIX, it holds good for MSDOS and Windows too. In the UNIXes the shell is usually 'sh' or 'bash'. In MSDOS the shell is called 'command.com'. In Windows the shell is 'explorer.exe'.

Hence it's always better to use int main ( ) along with a return value at the end of the main ( ) function. A return value of zero indicates a normally terminated program. A non-zero value is used in case of errors. Of course this does not mean that a program written with void main ( ) won’t work; but it is better to avoid writing such programs.

A practical example over here (along with some basics of batch programming).

122

Functions

Page 123: C++ Basics to Advanced

exit()

There is a function called exit ( ) which can be used to terminate (or break out from) the program. The function is:

void exit (int r);

where ‘r’ is an integer value returned to the OS. Usually a value of 0 is used to indicate normal termination while a non-zero number is used in the case of abnormal termination (due to errors). Thus, the syntax (or function call) will be:

exit (0);

or

exit (1);

Instead of using integers we can also use two predefined macros: EXIT_SUCCESS (which is equivalent to 0) or EXIT_FAILURE (which is a non zero number).

The exit ( ) function is declared in the stdlib.h library file. Hence you should type:

#include <stdlib.h>

in case you want to use this function.

Using ‘return’ to break out from loops

The ‘return’ statement will return program flow control to the caller and this feature can be used to break out from loops. An example program is given below:

#include <iostream.h> int display( ) {

int i; for (i=0;i<10;i++) { if (i!=5) { cout<<"\n"<<i; } else { return 1; //if ‘i’ is 5, break from loop through return

123

Functions

Page 124: C++ Basics to Advanced

} } return 0;

} int main( ) { display( ); cout<<"\n\nYou have returned to the main function."; return 0; }

The output of the above program will be:

0 1 2 3 4 You have returned to the main function.

As can be seen above, the return statement has acted like a ‘break’ statement to return control flow to the main ( ) function.

124

Functions

Page 125: C++ Basics to Advanced

Example of int main()

If you feel lost with the theory, let’s try out something practical to prove the point. The example has been simplified to illustrate the use of int main( ). We’ll write a program ‘prog1’ in which we’ll ask the user to enter an integer. Depending on the value entered, we’ll set the return value of main( ). We’ll use this return code in DOS to conditionally execute another program called ‘prog2’.

The programs are pretty simple and they would appear as:

//prog1.cpp – This program asks the user for an input and returns a value of 0 or //5.

#include<iostream.h>

int main( ) { int choice; cout<<"\nWelcome to prog1"; cout<<"\nEnter 1 to skip 2nd program:"; cin>>choice; if(choice==1) { return 5; } else { return 0; } }

Our second program just displays a statement to indicate that we are executing the second program.

//prog2.cpp – This program simply displays a statement on the screen

#include<iostream.h>

int main( ) { cout<<"\nCongrags.You are in the second program!"; return 0; }

Build the two executable files: prog1.exe and prog2.exe.

125

Functions

Page 126: C++ Basics to Advanced

Note: If you are using VC++, the exe files will be created in a folder called “Debug” within your project. Turbo C++ will create the exe files in the same folder itself.

So, now we have 2 programs but how do we conditionally execute the 2nd one depending on the return code of the first?

The answer lies in batch programming (if you are using DOS) or in shell programming (if you are using Unix/Linux). We’ll deal with batch programming. This is basically the creation of *.bat files (so far we’ve been creating *.exe files). I won’t go into this topic deeply but I’ll cover a bit of it so that you can appreciate return codes from programs. Before starting to write a *.bat file, you can copy the 2 exe files we created (prog1.exe and prog2.exe) into the same folder (I’ve copied them into my C:\).

I assume that you have used a bit of MS-DOS (at least you should be familiar with the command prompt, changing directories etc.).

Command prompt: To get to this from Windows go to START->RUN and type “command” in the pop-up box. You’ll be taken to the DOS prompt (this is the place from where you can give commands to DOS). The prompt on my system is:

C:\MYWIN\Desktop>

Now type cd\ to go to C:\

C:\>

Let’s create a batch file named combo.bat. To do this simply type:

C:\>edit combo.bat

You’ll be taken to an MS-DOS text editor (similar to Notepad in Windows). Type the following in the file, save it and return back to DOS.

@ECHO OFF

ECHO **** WELCOME TO BATCH PROGRAMMING ****

ECHO Executing the prog1

prog1

IF errorlevel 5 GOTO end

prog2

:end

ECHO End of batch file

126

Functions

Page 127: C++ Basics to Advanced

Perhaps everything seems weird?

Remember: MS-DOS command.com is not case-sensitive (i.e. typing DIR or dir is the same).

The first 3 lines of our batch file are used for the purpose of displaying something on the screen (equivalent to cout<< in C++). On the 4th line we say:

prog1

This is equivalent to executing the program ‘prog1.exe’ on the command prompt. Batch files are used basically to execute a set of instructions/programs in a particular sequence. If every time you log into the system, you want to perform 10 commands, then each time you’ll have to keep typing the 10 commands on your prompt. You can save time (and also needn’t worry about remembering the sequence of commands) if you write those 10 command in a batch file. Now, each time you log into the system, you just need to type the name of the batch file and voila! (all your 10 commands will be executed faithfully).

Coming back to our program, prog1 in the batch file will execute our first program. You’ll see the display:

Welcome to prog1

Enter 1 to skip 2nd program: 1

Let’s assume the user types 1. According to our program:

if(choice==1) { return 5;| }

Now prog1 returns a value of 5 to the caller. The caller is our batch program combo.bat. The next line of the batch program checks for this return code.

IF errorlevel 5 GOTO end

The IF condition becomes true if the previous step had a return value of 5 or greater. In our case the previous step was the execution of ‘prog1’ and this returned a value of 5. Thus the condition is true and the combo.bat will go to the section labeled ‘end’. Here we just display a statement saying that it’s the end of the batch program (echo is used to display on the screen).

If the user had entered some other value then the batch file would have executed prog2 since the return value would have been less than 5 (in our case it was 0) and so the IF condition would be false.

The output if you entered 1 would be:

127

Functions

Page 128: C++ Basics to Advanced

C:\>combo

**** WELCOME TO BATCH PROGRAMMING ****

Executing the prog1

Welcome to prog1

Enter 1 to skip 2nd program:1

End of batch file

C:\>

The output if you entered some other number would be:

C:\>combo

**** WELCOME TO BATCH PROGRAMMING ****

Executing the prog1

Welcome to prog1

Enter 1 to skip 2nd program:3

Congrags.You are in the second program!End of batch file

C:\>

Just follow the above steps, create the batch file and try it out on your system. To execute batch files you don’t need to compile the file (i.e. combo.bat can be directly executed). This is because the command prompt is an interpreter (it executes commands one at a time and does not require compilation as we do for C++ programs).

In Unix/Linux, this is called shell programming (and instead of batch files we call them shell scripts). In large applications, you would need to execute a series of programs everyday and these are written in shell scripts. The execution of programs would depend on the return code of the previous program (if the previous program failed because of some error then you might not want to continue execution of the remaining programs). By now you should have understood about the difference between void main( ) and int main( ).

Note: If you are using Unix/Linux then refer to the Appendix for the above section.

128

Functions

Page 129: C++ Basics to Advanced

A closer look at Functions

The following topics are covered in this section:

• Types of Functions • Function Overloading • Apply what you've learnt

Types of Functions

There are two types of functions namely:

1. Library Functions

• The declaration of library function is in the header file specified at the beginning of the program.

• The definition is in a library file that is automatically linked to the program • Declaration and definition are not required. We will only call the function. Example :

rand( ), clrscr ( ), exit( ) etc.

2. User Defined Functions

• Declaration and definition are part of the source file (*.cpp file). • Function definition and declaration have to be written by the programmer. • Example of user defined functions is the add ( ) function that we used in the previous

section.

There are many functions which are provided by the compiler. These functions that come with the compiler are called as library functions. The prototype for the library functions will be present in some header file. To use a library function, you only need to include the header file (where the function prototype exists) and should know the name of the function.

It is common for programmers to write coding for functions and then use them later in some other program when needed. In fact in C programming, this was quite common. You can do the same in C++ as well. You could define a set of general-purpose functions in a header file and include the header file in the programs where you want to use those defined functions (by specifying a function call statement). But using functions in this way can lead to some problems, which will be discussed later (the main problem is when there is a clash of function names). In C++ we make use of ‘classes’ and reuse classes, rather than functions directly.

129

Functions

Page 130: C++ Basics to Advanced

Using library functions: rand( ), srand ( ), time( )

There may be instances wherein you will want to generate numbers randomly. For example if you are simulating a game played with dice then you should be able to produce numbers between 1 to 6 randomly (corresponding to the 6 faces of a dice). Or if you want to simulate the tossing of a coin you should be able to retain the randomness of the event (i.e. these are events which you can’t predict. It could be a head or a tail).

We can make use of the rand ( )library function to perform such tasks. The rand ( ) function is defined in the stdlib.h library. Let us write a program to toss a coin and ask the user for his/her choice. If the user’s choice and the result of the toss are the same then the user wins the game.

#include <iostream.h> #include <stdlib.h> int main( ) { int move; char choice; do { cout<<"Enter 1 for head and 0 for tail: "; cin>>move; if (rand( )%2= =move) { cout<<"You win."; } else { cout<<"You lose."; } cout<<"\n\nDo you want to play again? "; cin>>choice; }while(choice= ='y'); return 0; }

The rand ( ) function will generate integers upto a maximum of 32767. But in the case of tossing a coin we have only two possibilities (a head or a tail). To scale down 32767 to two cases we divide the rand ( ) value by 2 and use the remainder (the remainder will either be 1 or 0). Thus we assume that 1 is head and 0 means tail in the above program. The output for the above program will be:

Enter 1 for head and 0 for tail: 1 You win. Do you want to play again? y Enter 1 for head and 0 for tail: 1 You win. Do you want to play again? y

130

Functions

Page 131: C++ Basics to Advanced

Enter 1 for head and 0 for tail: 1 You lose. Do you want to play again? n

If you run the program again a second time the result will be:

Enter 1 for head and 0 for tail: 1 You win. Do you want to play again? y Enter 1 for head and 0 for tail: 1 You win. Do you want to play again? y Enter 1 for head and 0 for tail: 1 You lose.

As you might have noticed the sequence is the same (i.e. each time the program is run the same set of random numbers are produced). This is because the random number is generated in a sequence and unless you ask the computer to start the sequence from a different position it will always keep starting at the same place. We have another function called srand( ) than can be used to alter the start of the sequence. To do this just add the satement:

srand( time(0) );

before the ‘do’ statement. The function time ( ) is defined in ‘time.h’ header file. The output will now be:

Enter 1 for head and 0 for tail: 1 You win. Do you want to play again? y Enter 1 for head and 0 for tail: 1 You lose.

If you run the program there is a good chance of getting a different result for the same inputs. This is because we are setting the random generator differently each time the program is run. The function time (0) will give the present system time in seconds. Each time you run this the time (0) value will be different.

Suppose you want to simulate the throw of a dice then you should scale down the outcomes of the rand( ) function to 6. For this (instead of rand( )%2, you could use the statement:

( ( rand( ) % 6 ) + 1 )

We have to add 1 because rand ( )%6 will produce values between 0 and 5 but we need values from 1 to 6.

131

Functions

Page 132: C++ Basics to Advanced

Function Overloading

Can two functions have the same name?

More than one function can have the same name but they should have different number of parameters or the types of the parameters should be different. This is known as function overloading. The name of a function along with its parameter data types forms the function signature. The signature helps differentiate between two functions. In function overloading the signatures of the functions with the same name will be different.

In general, overloading is the process of assigning several meanings to a function (or an operator as in operator overloading, which we will discuss in a later chapter). Consider the example given below:

// A PROGRAM TO ILLUSTRATE FUNCTION OVERLOADING

void display( ); // Function declaration – This function has no arguments void display (char); // One argument void display (char, int); // Two arguments

int main( ) { display ( ); display (‘=’); display (‘+’, 30); return 0; }

void display( ) { cout<< "Hi!"; }

void display (char ch) { cout <<ch; }

void display (char ch, int n) { int i; for ( i = 0, i < n, i ++ ) cout<<ch; }

In the above program, at the starting itself, the compiler will know that there are three functions with the same name of ‘display’. But the compiler is also clever enough to see that

132

Functions

Page 133: C++ Basics to Advanced

the parameters of each of the three functions are different. Once it notices that they are different, the compiler will consider the three functions to be different.

Whenever the ‘display’ function is called, the correct function will be called depending on the arguments that you have used.

Remember: The return value of a function is not part of the function signature. Do not try to overload based on return values (this will produce a compile error). You may wonder why this is the case? The return value of a function need not be stored (i.e. even if a function returns a value you need not use that value in your program). If you used 2 functions called int add( ) and double add( ) and if you never assigned the return value to any variable the program will have no way of determining which function to call.

Apply what you’ve learnt

Let’s write a C++ program to create a little mathematical program. The program should have a menu display providing the user with three options. There should be an option to generate the Fibonacci series, an option to find the factorial of a number and an option to exit the program. All of this has to be done using functions. (The entire program could be written within the main ( ) function without using functions but by using functions you will be able to create a clean and structured program. For performing each of the tasks we will write individual functions and you will find that it is much easier to follow the logic of the program by writing using functions).

By the way, the Fibonacci series goes as follows:

1,1,2,3,5,8,13,21,34,55,89 and so on.

(just add two numbers and you will get the next number in the series).

The factorial of a number, say 5 is:

5*4*3*2*1

Factorial of 6 would be:

6*5*4*3*2*1

//A program using functions

#include <iostream.h> #include <stdlib.h> //to make use of the system("CLS"); function int menu( ); //provide a menu for the user void fibo( ); //function for generating fibonacci series void fact( ); // to find the factorial of a number

133

Functions

Page 134: C++ Basics to Advanced

int main ( ) {

int ch; while (1= =1) //an infinite loop which can be broken { system("CLS"); //to clear the screen before the menu is displayed ch=menu( ); switch (ch) { case 1: fibo( ); break; case 2: fact( ); break; case 3: cout<<"\n Program Terminated."; break; default: cout<<"\n Invalid choice."; break; } if (ch= =3) { break; //break from while loop } } return 0;

}

//All the function definitions below:

int menu( ) {

int choice; cout<<"\n\n\n Welcome to my Program"; cout<<"\n 1.) Generate a Fibonacci Series."; cout<<"\n 2.) Find the factorial of a number."; cout<<"\n 3.) Exit."; cout<<"\n\n Enter your choice : "; cin>>choice; return choice;

}

void fibo( ) {

134

Functions

Page 135: C++ Basics to Advanced

int max, sum, a1, a2; cout<<"\n\nHow many terms do you want in the series? "; cin>>max; a1=1; a2=1; cout<<"1,1"; for (int i=2;i<max;i++) { sum=a1+a2; a2=a1; a1=sum; cout<<","<<sum; }

}

void fact( ) {

int num,i; long result; result=1; cout<<"\n\n Enter the number for which you want to find factorial of : "; cin>>num; for (i=num;i>0;i--) { result = result*i; } cout<<"\n\n The factorial of "<<num<<" is "<<result;

}

You can add more options and develop it into a useful mathematical program. This program was just meant to illustrate how the use of functions can aid in the understanding of the program logic.

135

Functions

Page 136: C++ Basics to Advanced

A closer look at Functions

The following topics are covered in this section:

• Recursion • An elaborated example using recursion • Inline Functions • Recap

Recursion

Recursion comes from the word ‘to recur’ which means happening repeatedly over and over again. Recursion is also referred to as circular definition.

In programming we have recursive functions and they are functions that keep repeating themselves. How do they repeat themselves? Well, actually the function keeps calling itself till some condition is satisfied. It might seem really funny to think of a function calling itself but this is exactly what happens. The best example for recursion is to calculate factorials. In the last program we wrote a function to find the factorial of a number using the ‘for’ loop. We’ll now see how to do the same using recursion. Remember to read the program just like the compiler would read it; otherwise you’re bound to get confused.

#include <iostream.h> int fact(int n) { int result; if (n= =1) { return 1; } else { result = n * fact(n-1); return result; } }

int main( ) { int num; cout<<"\nFor what function do you want to find the factorial : "; cin>>num; cout<<"\n\n The result is : "<<fact(num); return 0; }

136

Functions

Page 137: C++ Basics to Advanced

First of all, the function fact ( ) is the recursive function here. Why? Check out the function body and you’ll see that this function calls itself. The basic idea is that if you want to find the factorial of 3, the answer is actually equal to 3*factorial (2). The factorial of 2 will be: 2*factorial (1) and factorial of 1 will be 1.

Let us interpret the logic of the program. For understanding purpose, let’s assume that we want to find the factorial of 4.

• Now num=4 and n=4. The compiler enters into the recursive function and calculates value of ‘result’.

• result = 4* fact (3);

what does fact(3) mean? ‘fact’ is the same function and hence the function is being called with a different argument value. The value returned by the function is ‘result’ (which is 4*fact(3)). But there is another function called in this returned value. So the program tries to calculate fact(3). When calculating fact(3), it again calculates the value of ‘result’. Now,

• result=3*fact(2);

This value is returned and your actual expression in the computer would be 4*3*fact(2). What next?

• Calculate fact(2) and this leads to:

result=2*fact(1);

• The expression in the computer will now be 4*3*2*fact(1). The program calculates fact(1). When the value of ‘n’ is 1, we have specified an ‘if’ condition that says: return 1;

• 1 is returned for fact (1) and the entire expression becomes: 4*3*2*1 and the compiler produces the result by multiplying the numbers.

If you don’t give the ‘if’ condition the recursive nature will never stop and it will lead to problems.

Remember: When using recursive functions make sure that there is a way for the function to stop itself from infinitely repeating itself.

Can it repeat itself infinitely? You do not want a function to keep calling itself infinitely and this is why we should provide some means of breaking out from recursion. When using recursion there are chances for stack overflow. Stack is a special portion of memory where the computer stores temporary values. For example when a computer encounters a function call, it has to store the current location before going to the function. When the function is executed, the program will return back to the original position and continue the program execution. In recursive functions, there are chances for the stack getting used up quickly. That’s what is stack overflow.

137

Functions

Page 138: C++ Basics to Advanced

Anyway, there isn’t much advantage of using recursion but there may be some cases where you feel that recursion might make the coding easier. A programmer usually uses recursion only if it mirrors the natural methodology or if the iteration method is complex. But be very careful while using recursion. The problem with recursion is that it involves a series of function calls and this can consume extra memory space.

Remember: Any C++ function can be made recursive. What about main( )? It is also a C++ function but main ( ) is the one function which CANNOT be made recursive.

Recursion in string reversal

It is important to understand how the stack works during recursion. The best example to illustrate this is the problem of reversing a given string. The problem would be easy to solve if we knew the length of the string beforehand. Or we could declare a huge array and hope that we don’t get a string longer than the array. Our problem is stated as follows:

"Write a C++ program to reverse a string entered by the user. The string will be terminated when the user presses the Enter key. You should not restrict the input string length."

To solve this let’s take a recursive approach:

//********************************************************* //* Program to illustrate string reversal using recursion * //*********************************************************

#include <iostream> using namespace std;

void backwards( ) { char ch; ch=getchar( ); if (ch!='\n') { backwards( ); } cout<<ch; }

int main( ) { backwards( ); return 0; }

The output would be:

138

Functions

Page 139: C++ Basics to Advanced

trial

lairt

Though the code is really small it might seem confusing at first. First of all, the getchar( ) function will extract only one character at a time. If you want to retrieve 5 characters then you’d have to use getchar( ) 5 times (in our program we do this using recursion). To explain the working, let’s take an example of what happens when we run the program.

The main( ) function calls the function backward( ). In this we have a local variable named ‘ch’. This is now allocated memory space in the stack. Let’s consider our stack as shown below (‘ch’ still doesn’t have a value):

The next line says:

ch = getchar( );

On encountering this line the program waits till the user presses an Enter (getchar( ) will retrieve a character only when the user presses the Enter key). The user now types the word ‘trial’ and presses Enter.

‘ch’ will now have the value ‘t’ (1st letter of the input string).

Next line says:

if (ch!='\n') { backwards( ); }

Our ‘ch’ value is ‘t’. So the condition is true and backwards( ) is called again. What happens in this second call?

Remember: When a function call occurs the compiler stores some information in memory so that it can return back to the caller. If function A calls B, then once B is completed the program flow should return to the exact point in function A and continue executing the rest of function A.

139

Functions

Page 140: C++ Basics to Advanced

In our case the backward function is not complete (it will be complete when backward( ) returns control to the caller- in our case the caller is main( ) ). So, again we go through the same process. A character named ‘ch’ is declared in the function. Thus memory space is allocated for this variable in the stack.

Remember: Each time a local function is called the variables created within that function are available only within that function.

So now our stack will look as below:

Why is the second ‘ch’ on top of the first? A stack functions like that. Each time you add something new onto the stack, it will be added to the top of the pile (just like a stack of plates). The first ‘ch’ and second ‘ch’ are not the same. The top ‘ch’ corresponds to the variable created during the second call of backward( ) while the lower one corresponds to the variable created in the first call of backward( ). Let’s denote them as ch1 and ch2 (so that you don’t get confused as to which ch we are referring to).

Again we have a getchar( ) function. This will now get the second letter of the input string (i.e. ‘r’). Now ‘ch’ has a value of ‘r’ and it is not equal to \n. The stack would like this:

Again the function backward( ) is called. This process goes on and you will finally reach the following stack structure:

140

Functions

Page 141: C++ Basics to Advanced

This ‘ch’ will have a value of ‘\n’. The if condition will fail and we’ll enter the else loop which says:

cout<<ch;

Since ‘ch’ is ‘\n’ the program will output a newline character onto the screen. What happens next?

We still have 5 functions that have not executed their entire block of code (before each backward( ) could complete we called another backward( )- so now the program has to keep going backwards). After printing the newline character on the screen, the current backward( ) function ends (it reaches the terminating } ). So now control goes back to its caller. The caller was the 5th execution of the function backwards( ). This has a line of code that says:

cout<<ch;

In this case, ‘ch’ refers to its local variable (this ‘ch’ was pushed onto the stack – we refer to it as ch5 in our illustration). So this will cause the program to print ‘l’ on the screen. This function ends here and control returns to the caller of this function and so on. Thus you get the string in reverse order.

Remember: Once a function terminates (i.e. returns control to the caller, the local variables used by that function are popped out of the stack- i.e. they are lost once the function returns control to the caller).

Inline Functions

There may be instances when you want to repeat a process more than once in a program. You may think of going for the concept of functions. It’s fine as long as your function is a bit lengthy. Suppose there are only one or two lines you want to repeat? Is it worthwhile going for a function?

When a program executes a function call, it has to save the present memory address and then go to a new memory address to access the function. After executing the function it has to return back to the memory address from where it branched off. All of this is referred to as ‘overhead’ and by branching (or jumping) from the main program to sub-routines involves a lot of overhead. This means it will take some time and will also use some memory space.

If a function is very small, then it might not be advisable to make use of so much of overhead (i.e. there is no need for using another function).

Instead, we can make use of the inline functions. Inline functions will simply substitute the function definition wherever there is a function call. This is more like substitution.

Example:

141

Functions

Page 142: C++ Basics to Advanced

// Convert Dollars to Rupees # include <iostream.h> inline double dollartors (double d) // The function is inline. { return 47*d; // Inline functions usually contain only one/two body lines }

int main ( ) { double dollar; cout << "How many dollars :"; cin>>dollar; cout<< "Rupees : "<<dollartors(dollar); //Function call return 0; }

As you can see above, the inline function is very similar to a normal function. One difference is that you have to mention the keyword ‘inline’ before the function name in the declaration. When the compiler comes to the function call, it knows that ‘dollartors’ is an inline function. So the compiler will insert the body of the function in place of the function call. The compiler will simply copy and paste the function body wherever there is a function call. After making the substitutions wherever the inline function has been called (in our program it is done in only one place), the program is executed.

Thus when the program is executed, the amount of overhead involved is reduced since the equivalent coding will be:

int main ( ) { double dollar; cout << "How many dollars :"; cin>>dollar; cout<< "Rupees : "<< 47*d; //Inline function body substituted return 0; }

The advantage is that the program needn’t save its present memory address, go to the function’s memory address, execute the function and return back to the original address. Instead the function code is brought into the main program and it is executed just like a normal C++ statement. Thus processing time can be reduced significantly.

Remember: It is useful to make a function inline if its body consists of just one or two lines.

142

Functions

Page 143: C++ Basics to Advanced

Recap

• A function is a group of statements written for a specific purpose and grouped within a single block.

• A function has to be declared if it is called before being defined. • Arguments are passed to a function’s parameters. • The data type of the arguments should match the data type of the parameters. • If a function is declared as returning a value to the caller then it should return the

corresponding data type value (unless it returns void). • The return statement is used to return a value to the caller as well as give back

program control to the caller. • The main ( ) function returns value to the OS. • Overloaded functions should have the same name but different parameters. • Return data type cannot be used as a basis for function overloading. • Inline functions are used when the body of the function is very small (one or two

lines). • Recursive functions are functions that call themselves. Some provision has to be

provided for them to break out of recursivity. • Recursive functions have more overheads and are generally not used.

Functions 143

Page 144: C++ Basics to Advanced

More On Data Types and Variables

The following topics are covered in this section:

• Scope of Variables • Storage Classes

Scope of variables

Scope refers to the region where something is valid or the region where something can exist. Variables in C++ have a defined scope, which depends on the way they are declared. There are three places where a variable can be declared: as local variables, formal parameters and global variables.

Remember: In C we can declare variables only in the starting of the function. In C++, we can declare variables anywhere within the program.

Local Variables

Variables declared within a function are called local variables (sometimes called automatic variables). These variables can be used only within the block (or function) in which they are declared. A block starts with an opening curly brace and ends in a closing curly brace. A local variable is created upon entry into the block and destroyed when the program exits that block. If you create a variable in the main ( ) function then it can be used only within the main ( ) function. That variable cannot be accessed by some other function that you may have created. For example:

void test ( ) { // Start of block int q; q = 2; } // End of block void test2 ( ) // Start of another block { int q; q = 5; }

The two q's declared in the two functions (test and test2) have no relationship with each other. Each q is known only within its own block (since it is a local variable). The main advantage is that a local variable cannot be accidentally altered from outside the block.

144

More on data types & variables

Page 145: C++ Basics to Advanced

Try it: Compile the following piece of code in your compiler and check the results

int main( ) { int outer; { int inner; cout<<"enter the outer variable value : "; cin>>outer; } cout<<"\n Enter inner variable value : "; cin>>inner; return 0; }

What do you think will happen? The above program will lead to a compile-time error. Variables are visible only within the block of code where they are declared (unless they are global variables). The coding enclosed within the two braces is called as a ‘block’ of code. Thus:

{ int inner; cout<<"enter the outer variable value : "; cin>>outer; }

is a block of code within which we have declared an integer variable ‘inner’. This variable ‘inner’ is not visible outside of this block. Thus the statement:

cin>>inner;

will lead to an error because ‘inner’ is only known inside this block and not outside the block.

Note the difference between the two codes given below:

int main( ) { int i=5; { int i; //This ‘i' is only visible within this block. i=6; } cout<<i; //Value will be 5 (NOT 6) return 0; }

145

More on data types & variables

Page 146: C++ Basics to Advanced

Compare this coding with the one below:

int main( ) { int i=5; { i=6; } cout<<i; //Value of ‘i' is 6 (NOT 5) return 0; }

What’s the difference between the two codes? In the first one we have declared a new integer ‘i' within the inner block. Now this inner variable is different from the outer block variable.

In the second case, we are modifying the value of the outer block variable (in this case there is only one variable – the outer block variable).

Remember: An inner block can access the variables that are present in the outer block. But the reverse is not possible.

Formal Parameters

As we have seen, if a function is to use arguments then it must declare variables that will accept the values of the arguments. These variables are called parameters or formal parameters. For example:

int remainder (int a, int b) // In this function, the parameters are ‘a’ and ‘b’. { return a % b; }

They are actually similar to local variables and the parameters are visible only within the function block.

Global Variables These variables are known throughout the program. They can be used by any part of the program and are not limited in scope like local variables. They are declared outside the main ( ) function.

#include <iostream.h> int count; // count is a global variable void increment( ) { count=count+2; }

146

More on data types & variables

Page 147: C++ Basics to Advanced

int main ( ) { count=1;

• increment( ); //count is now 3 count=count+1; //count is now 4

}

The above program is not complete but you can see that two functions (increment and main) can access the same variable ‘count’.

count=1;

The increment ( ) function increases the same count value by 2. Now count is 3 and finally the main ( ) function increases count by 1. The final count value is 4. This is a case of more than two variables accessing the same global variable.

Remember: Global variables will take up more memory because the compiler has to always keep it in memory. Avoid using too many global variables. Use it only if the variable is going to be used by a number of functions.

What would happen if a program uses an identifier as both local and global variable name? Consider the program below:

#include<iostream.h> int var=55; //Global variable int main( ) { int var=20; //Local variable with same name cout<<"\nGlobal variable value is : "<<::var; var=var+1; cout<<"\nLocal variable value is : "<<var; return 0; }

The output of the program will be:

Global variable value is : 55

Local variable value is : 21

Within the main ( ) function whenever we refer to ‘var’, it will mean the local variable only. Suppose you want to call the global variable, then you have to make use of the scope resolution operator (::). This operator is explained later but for the time being just remember that ‘::var’ will refer to the global variable.

147

More on data types & variables

Page 148: C++ Basics to Advanced

Beware: It is not good programming practice to use the same variable name for global and local varaibles (it can lead to a lot of confusions and programming errors).

Storage Classes

Storage classes are qualifiers that you can use to tell the compiler how you want the variable to be stored in memory and how you want to access it.

The four storage classes are:

1. Auto (automatic) 2. Static 3. Extern 4. Register

These four terms are keywords in C++.

Auto

All local variables are ‘auto’ by default (auto meaning automatic). Whenever you declare a variable in a function, it is already ‘auto’. You needn’t explicitly specify this qualifier. For example:

int var;

will be the same as:

auto int var;

Suppose we have a function named counter ( ) with a local variable ‘count’

void counter ( ) { int count=0; cout<<count; }

Each time this function is called the value of ‘count’ will be initialized to 0.

148

More on data types & variables

Page 149: C++ Basics to Advanced

Static

There may be occasions when you want to retain a particular variable’s value each time the function is called. When you use auto variables, you will lose the value of the variable. If you declare a variable as a static variable then it will retain its value.

int counter( ) { static int count=0; count=count+1; return count; } int main( ) { for (int i=1;i<10;i++) { cout<<counter(); } return 0; }

The output will be:

123456789

The function counter ( ) is actually being called 9 times by the function main ( ) and each time the ‘count’ value is retained. If the variable ‘count’ were not declared as a static variable, the output would be: 000000000

You might be thinking that the line:

static int count=0;

will initialize the variable ‘count’ each time to zero but this does not happen. It is used to initialize ‘count’ to 0 only once (i.e. at the first call).

Extern variable

Extern or external variable is useful in multiple file C++ programming. So far we have only seen single file programming (i.e. you write the entire program in one *.cpp file, compile and run it). As your program complexity increases you will find it better to divide your program into different files. In such instances, extern variables are useful. This will be dealt with in the topic of "Multiple File programming".

149

More on data types & variables

Page 150: C++ Basics to Advanced

Register variable

When you specify a variable as ‘register’ the compiler might place the variable in one of its hardware registers. The word ‘might’ is very important because you cannot be sure that the compiler has placed the variable in its register.

Remember: In C programming you shouldn’t try to find the address of a register variable.

Why do we need register variables? Placing a variable in the register will help in speeding up the computational processes that involve the register variable. Since the computer processor has only a fixed set of available hardware registers it cannot place all your variables in its registers. Depending on availability the compiler will decide whether to make a variable a register variable or not.

Usually, variables are stored in the computer’s memory (as explained earlier, memory will have addresses for accessing the location and values are stored in these locations). But a register variable is stored in one of the registers of the CPU (the central processing unit). Every microprocessor has a set of internal registers available that it can use for its operations (and accessing these registers is faster than accessing external memory).

The syntax is:

register data-type variable-name;

By the way, it isn’t a rule that only integers can be made register variables. Any data type variable can be made a register variable.

Beware: Don’t expect that specifying all your variables as register type will speed up the program. There is a specific limit and beyond that the compiler will treat your register variables as normal variables. Though you can use it on any variable data type, the register variable might work effectively only in the case of an integer or a character. Use register variables for variables that are accessed frequently in your program.

150

More on data types & variables

Page 151: C++ Basics to Advanced

More On Data Types and Variables

The following topics are covered in this section:

• Type Qualifiers • Arrays • Apply what you've learnt

Type Qualifiers

Variables can be given a qualifier (something that precedes their data type) to specify how they will behave in the program. The two qualifiers that can be used are:

• const (constant) • volatile

They are sometimes referred to as type-qualifiers (since they specify how the program can modify these variables).

Constants

Constants are fixed and will never change (i.e. their value will never change). There might be occasions when the programmer will want to make use of variables whose value should never be modified within the program. When we use variables (As we have done so far in this book), we can change the value of the variable whenever we want to. But once a variable is declared to be a constant (using the keyword ‘const’), it’s value cannot be altered in the program. For example:

const float PI=3.14;

will make PI a constant with the value of 3.14. A constant should always be initialized with a value (you cannot simply declare const float PI; A value has to be assigned to it as well).

By the way, what do you call ‘pi’ in the above example? Is it called a constant variable? Some people use the term ‘constant variable’ (perhaps as a way of distinguishing them from the constants that we saw in the second chapter). Actually this term is an oxymoron. It is advisable to use the term variable when you refer to something whose value can be changed while the term constant is used whenever the value cannot be altered. So ‘PI’ is a constant.

Remember: Programmers usually use uppercase identifiers (example: PI, MAX etc.) for constants while lowercase identifiers (like x, marks, age etc.) are used for variables.

There is another way to declare constants (this method was used in C programming). Instead of the ‘const’ keyword, we can make use of macros.

151

More on data types & variables

Page 152: C++ Basics to Advanced

#include <iostream.h> #define PI 3.14 //Macro definition int main( ) { cout<<PI; return 0; }

The output would be:

3.14

# indicates that the statement is a preprocessor directive. This topic will be explained in detail later in the book. In the above program PI is called a ‘macro’. Wherever the term PI is encountered, the number 3.14 will be substituted in its place.

Remember: C++ is a case-sensitive language, which means that the compiler will not consider PI and ‘pi’ as one and the same.

Beware: You shouldn’t use the equal to sign in the preprocessor directive statement and neither should you use the semicolon terminator at the end.

Volatile

The volatile keyword is the opposite of ‘const’. The volatile keyword informs the compiler that the variable can be modified by methods that are unseen (or external). For instance, this external method could be an interrupt-related method. If we don’t specify the variable as volatile, then the compiler might end up denying modification of the variable by such an external method. To declare a variable as volatile, just precede the declaration by the keyword ‘volatile’.

The syntax is:

volatile data-type variable-name;

Variable Initialization

Assigning values to a variable at the time of declaration is known as variable initialization.

For example:

int x = 10;

double db=2e4;

152

More on data types & variables

Page 153: C++ Basics to Advanced

If global or static variables are not initialized they are automatically set to zero. If a local variable is declared and not assigned any value then it will contain an unknown value (i.e. you can’t say for sure what is the value of the variable. This is sometimes termed as ‘garbage’ value).

For example:

int main( ) { int local; cout<<local; //value is unpredictable. It can have any value. return 0; }

Remember: Local variables will contain an unknown value till they are assigned a value.

There is another way to initialize variables (instead of using the = operator). The following initialization is also valid in C++:

int count(5);

This will initialize the variable ‘count’ to 5. It is the same as:

int count=5;

Arrays

If you want to use a variable you should declare the variable first. What if you need to obtain the marks of a student in 20 subjects? Should you make use of 20 variables? You could write the code as:

int marks1, marks2, marks3…;

This would make the process of declaring variables and obtaining inputs very tedious. Arrays provide an easier way of working with many variables, which are logically related. An array consists of a set of variables of the same data type. Each variable of an array is called its element and each of the elements can be accessed using subscript numbers (or index numbers).

The difference between an array and a structure is that array consists of a set of variables of the same data type. A structure, on the other hand, can consist of different data types. You'll learn about structures later on.

The first step if you want to use an array is declaration of the array.

153

More on data types & variables

Page 154: C++ Basics to Advanced

data-type variable-name [size-of-array];

For example:

int marks[5];

This means that you have declared an array of integer type. The variable name for the array is ‘marks’ and size of the array is 5. Hence you have actually declared five integer elements namely: marks[0] , marks[1] , marks[2] , marks[3] and marks[4].

You may have a question at this point: What about marks[5]?

The computer counts from zero onwards. The size of the array we declared is 5. So if you start counting from zero then the fifth element is marks[4] not marks[5].

If an integer occupies 2 bytes, then the ‘marks’ array declared above will occupy a total of 10 bytes continuously. Each individual element can store an integer value. The figure below illustrates this concept:

Remember: Start counting from zero. Many beginners do not pay attention to this important fact.

154

More on data types & variables

Page 155: C++ Basics to Advanced

Some points about arrays:

1. Array is a collection of variables (of the same data type). 2. Each individual element is denoted by its index number. 3. Array elements are placed consecutively in memory (in other words array elements

occupy contiguous memory locations; i.e. they are placed adjacent to one another). 4. The size of the array has to be specified while declaring the array (because the compiler

has to allocate memory for the individual elements).

Accessing a particular element:

If you want to display the values of an array, you might think that this will work:

cout<<marks; //WRONG

This is wrong! It seems as if this is fine but this will not work. There is no way that you can display the entire contents of an array by simply specifying its common name. You can print the individual elements as below:

cout<<marks[0];

cout<<marks[1];

Similarly if you want to assign a value to one of the elements, you would write the following code:

marks[0] = 56;

marks[4] = 90;

Just type the variable name followed by the element number within the square brackets. Be careful with the type of bracket; it’s a square bracket and not the curly braces.

Then you might be wondering what’s the big deal of using arrays? Since to access individual elements only the index number has to be changed, we can easily display the values stored in an array by using a ‘for’ loop. Another advantage is that the name of the array is the same. Otherwise you would have to use different identifiers.

155

More on data types & variables

Page 156: C++ Basics to Advanced

Initializing an Array:

Suppose you want to initialize the array element values at the beginning itself (during the declaration) then you could make use of a comma-separated list within braces as shown below:

int marks[5] = {56, 75, 80, 59, 90};

This statement tells the compiler that the value of the first element of the array (or marks[0] ) is 56, the second element ( marks[1] ) is 75 and so on. The initialization of an array is equivalent to:

marks[0]=56; marks[1]=75; marks[2]=80; marks[3]=59; marks[4]=90;

While initializing an array the number of elements initialized must be equal to the size of the array. In the above example we have 5 elements and all 5 elements have been initialized in the statement:

int marks[5] = {56, 75, 80, 59, 90};

Instead of this we could also initialize using the statement:

int marks[ ] = {56, 75, 80, 59, 90};

The compiler will automatically set the size of the array as 5 (because 5 elements have been initialized).

Remember: For the initialization you have to use curly braces and for the index numbers you have to use square brackets.

More on data types & variables 156

Page 157: C++ Basics to Advanced

Memory usage for an array:

If you have an array of 10 short integers, then the array will occupy a total of 2*10 bytes (20 bytes). An array of 10 long integers would occupy 4*10 bytes (40 bytes).

double temp[5];

cout<<sizeof(temp);

will produce an output of 40 (because each double variable will occupy 8 bytes). But what if we tried:

double temp[5];

cout<<sizeof(temp[5]);

This would return the size of the 5th element; which means that the output will be 8.

More on data types & variables 157

Page 158: C++ Basics to Advanced

Apply what you’ve learnt

Let’s write a program that utilizes the concept of arrays. We’ll write a program to arrange a set of given numbers in ascending order and display the result. The basic steps involved will be: Obtain the size of the array (i.e. how many numbers the user wants tosort, 2 numbers or 3 or 4 or 8 numbers etc.) Obtain the value of each element of the array. (Hint: make use of a for loop) Compare the values of each element with the others and sort the numbers.

//To arrange a given set of numbers in ascending order and display the result

#include <iostream.h> int main ( ) {

int test[20]; //we assume that the maximum limit is 20 numbers. int size,i,j; int temp; cout<<"How many numbers do you want to compare : "; cin>>size; cout<<"Enter the numbers you want to check : "; for(i = 0; i<size; i ++) //obtain the numbers { cin>>test[ i ]; } for(i = 0; i<size; i++) //2 loops for sorting the numbers {

for (j = 0; j<size; j++) {

if ( test[i]>test[j] ) { temp = test[ i ]; test[ i ] = test[ j ]; test[ j ] = temp; }

} } cout<<"The numbers in ascending order are : "; for (i = 0; i<size; i ++) // loop used for displaying sorted values { cout<<endl<<test[ i ]; } return 0;

}

As can be seen from the program, it is much easier to obtain inputs for array elements as well as displaying the values using loops. By the way, the above method of sorting is simple to implement but quite slow. There are other methods like the quicksort method, which will consume less time.

158

More on data types & variables

Page 159: C++ Basics to Advanced

Character Arrays

You can have an array of any data type: integer, float, double etc. You can even have character arrays which can store strings. A string is an array of characters terminated by the null character (‘\0’). For example, if we store the word "hello" in a character array, the individual characters of "hello" will be stored and the last character of the array will be a null character (‘\0’). It is the null character that denotes the end of the string (this is very important when doing string operations –without the null character we will not be able to identify the end of the string in memory). The character data type can store only a single character but a character array can take in a consecutive series of characters depending on the size of the character array.

Example:

char word[6]={'h','e','l','l','o','\0'};

You have to provide space to accommodate the null character. You can also assign a string constant directly as below:

char word[6]="Hello";

"Hello" is called a string constant and the compiler will automatically add the null character to the array.

The following declaration:

char word[6]="Helloo"; //ERROR

is incorrect and will produce a compile error because there is no space for adding the null character. But if you don’t specify the size of the array then it is acceptable:

char word[ ]="Helloo";

In this case while compiling the compiler will set the size of the array and provide space for the null character also.

Beware: You can assign a string literal to a character array only at the time of declaration. You cannot use:

159

More on data types & variables

Page 160: C++ Basics to Advanced

word[6]="Helloo"; //WRONG

Another Example:

char name[10];

The above declaration declares an array of character type that can store 10 characters. The name of the array is ‘name’.

// A program for getting the name of a person and displaying the name # include <iostream.h> int main ( ) { char name[15]; cout<< "Enter your name : "; cin>> name; cout<< "Your name is "<<name; return 0; }

Remember: Whenever you create character array type strings then ensure that you have space to accommodate the null character.

Beware of Character Arrays (and the compiler in general):

Try out the following program in your compiler. The results may surprise you.

#include <iostream.h> int main ( ) { char name[20]; cout<<"Enter your full name : "; cin>>name; cout<<"You entered your full name as : "<<name; return 0; }

Run it and type your full name. What do you think the compiler will display?

Let's suppose the user types his full name as John Davis. ‘name’ is a character array that can store a maximum of 20 characters. John Davis has 10 characters (including the space between first and last name). So this is less than 20 characters. It seems like there is no problem but if you observe the output, it would be as follows:

You entered your full name as: John

160

More on data types & variables

Page 161: C++ Basics to Advanced

The Davis is left out. Why? After the word ‘John’ a blank space has been left. When a blank space is encountered in the input (John Davis), the program will not read the line further on. Hence only the word John is stored in the array name.

So be careful. This isn't only for character arrays. This applies even when you want to get input for a number. If the user types 1 followed by a space and then 2, the value of the integer variable will be 1. You can try out variations in your compiler, so that you know exactly how this works.

You might be wondering whether there is a way to store the blank spaces? Yes, there is but you shouldn’t make use of the cin method of obtaining input from the user. The cin will stop when it sees a blank space but there are other ways which will store the blank space (those methods will terminate when they encounter a new line; a new line means that the user pressed the ‘enter’ key).

For the time being if you want to get the full name of a person just make use of two character arrays; one for the first name and one for the last name.

If you are really keen on using a single character array for storing white spaces as well, then you could make use of the gets( ) function.

int main( ) { char name[80]; cout<<endl<<"Enter your full name: "; gets(name); cout<<name; return 0; }

In this program, you can enter a name with blank spaces and that will also be stored in the array ‘name’. The gets( ) function will terminate a character array input when the enter key is pressed (in other words, when it encounters a new-line character it will terminate).

Beware: gets( ) does not perform any array limit checking (also known as boundary checking). In case the array size is defined to be 5 and if the user types 10 characters, then it will cause an error.

161

More on data types & variables

Page 162: C++ Basics to Advanced

Alternative Methods to store blank spaces and new line:

The function gets( ) does not perform any array dimension check and hence it doesn’t restrict the user in the number of characters entered. The result is that your program will terminate due to some run-time errors if the array size is exceeded. To avoid this problem there is another alternative function that you can use. This function is a member function and can be called using ‘cin’. (You’ll learn more about objects and member functions later). The function is called get( ).

#include <iostream.h> int main( ) { char name[10]; cout<<"Enter the name: "; cin.get(name,10); cout<<endl<<name; return 0; }

The output will be:

Enter the name: Ian Smith Ian Smith

As you can see, the blank space is stored in the character array ‘name’. Let’s try a longer name:

Enter the name: Gregory Ian Gregory I

"Gregory Ian" has more than 10 characters in it. The get( ) function will terminate the array after 9 characters (since the 10th character has to be the null character). Thus in this case we haven’t run into any run-time errors even though the user entered more characters.

The get( ) function has two parameters (the syntax has been simplified for easy understanding):

get(char-array, length-of-string)

You might not want a character array to be terminated by a new line (you may want to preserve the new line as well). In this case you can make use of:

get(char-array, length-of-string, ‘character-for-termination’)

You can tell the compiler as to when you want the array to be terminated (i.e. you can specify which character will terminate the array), by specifying the character in the field ‘character-for-termination’.

162

More on data types & variables

Page 163: C++ Basics to Advanced

See the example below:

#include <iostream.h> int main( ) { char name[80]; cout<<"Enter the name: "; cin.get(name,80,'*'); cout<<endl<<"The name stored is: "<<endl<<name; return 0; }

The output will be:

Enter the name: John Ian Smith Alex Shone*Luis The name stored is: John Ian Smith Alex Shone

If you observe the output, you will notice that the newline character has been preserved. The input was terminated once the program encountered an ‘*’ (which was specified as the character-for-termination).

163

More on data types & variables

Page 164: C++ Basics to Advanced

Arrays Continued

The following topics are covered in this section:

• Multi Dimension Arrays • Initializing MultiDimensional Arrays • Passing Arrays to functions • Test yourself

Multidimensional Arrays

So far we've only dealt with one-dimensional arrays. int marks[10]; is a one-dimensional array. It is called one-dimensional because it has only one value inside the square brackets. Or in other words, this array will have only a single row of elements. C++ has no limit on the number of dimensions. You can have 2 or 3 or more dimension arrays. Multi-dimensional arrays are actually arrays of arrays. But we shall take a look at two-dimensional arrays since they will be used frequently. Consider a two dimensional array as a tabular column with rows and columns. int marks[3][4]; declares a two dimensional array with 3 rows and 4 columns. Columns are vertical and rows are horizontal.

marks[0][0] marks[0][1] marks[0][2] marks[0][3]

marks[1][0] marks[1][1] marks[1][2] marks[1][3]

marks[2][0] marks[2][1] marks[2][2] marks[2][3]

As you can see marks[0][0] is the first square. The compiler always starts from zero and not one. Hence there are three rows and four columns in the array ‘marks’ as shown in the table above. There are a total of 12 elements.

The element in row 0 and column 0 can be referred to as the ‘0 x 0’ element. The element marks[0][1] will thus be called as ‘0 x 1’ element. Always remember that the row number comes first. This is not a universal method of referring to array elements. It is used here to make it easier to refer to the elements instead of using the element names.

Obtaining inputs for multi-dimensional arrays:

To get the values for an array you have two ways. You have to keep asking the user statement by statement to enter a value. Or you could take advantage of the ‘for’ loop (or any of the other looping mechanisms). We have already seen this in the sorting example involving a one-dimension array. The method to obtain the values of elements of a 2 x 2 matrix is similar to that for a one-dimensional array:

164

More on data types & variables

Page 165: C++ Basics to Advanced

int i, j, a[2][2]; for (i = 0 ; i<2 ; i ++) { for (j = 0 ; j<2 ; j++)

{ cout<< "Enter the "<< i + 1<< " x "<< j + 1 <<"element of the matrix : "; cin>> a[ i ][ j ]; }

} The outer ‘for’ loop starts with a value of 0. i = 0

Corresponding to i=0 we will have two values for j (0 and 1). Hence with this you can get the values for a[0][0] and a[0][1]. This corresponds to the first row of the matrix. For the second row, the ‘i’ loop will execute a second time with a value of 1. Hence you can get the values for a[1][0] and a[1][1].

In the cout statement we have mentioned ‘i + 1’ and ‘j + 1’. This is just for the purpose of display. Remember that the compiler will start numbering from zero. The first element for the compiler will be the 0 x 0 element. For the user it is better if you refer to the first element as 1 x 1 rather than referring to it as 0 x 0.

Similarly, two ‘for’ loops can be used to display the values of a two-dimensional array.

Initializing multi-dimensional arrays:

Initializing a 2-D array is similar to that of a 1-D array.

int marks[2][3]={ 40,50,60, 70,80,90};

The above notation is used for readability. You might as well initialize the array as:

int marks[2][3]={ 40,50,60, 70,80,90};

Thus if you use the following initialization (hoping that marks[0][2] will be 0):

int marks[2][3]={ 40,50, 70,80,90};

the compiler will treat it as:

int marks[2][3]={ 40,50,70,80,90};

165

More on data types & variables

Page 166: C++ Basics to Advanced

and marks[0][2] will be 70 while marks[1][2] will be 0. Only trailing elements will be automatically initialized to 0. There is a better way to initialize multi-dimensional arrays. The following initialization:

int marks[2][3]={ {40,50}, {70,80,90} };

actually produces the result we were looking for earlier. Now, marks[0][2] is zero. The additional pair of parentheses makes a big difference. Readability is improved further and now the compiler will set the trailing elements (which haven’t been initialized) in each row to 0. In our case, only the 3rd element of the first row is missing and hence this is initialized to zero.

You might recollect that in one dimensional arrays we could specify:

int a[] = {1,2,3};

and the compiler would translate this into:

int a[3] = {1,2,3};

The question arises as to whether we can extend this to 2-D arrays as well:

int marks[ ][ ]={ {40,50}, {70,80,90} };

This will give a compile-time error. The compiler wouldn’t know how many columns you want to specify for the array (you’ll understand this concept when we deal with pointers and 2-D arrays in the next chapter). But for the time being remember that you can forget the 1st dimension but shouldn’t leave out the subsequent ones.

Let’s go one step further. What is a 3-D array?

int a[5];

int ab[2][5];

int abc[3][2][5];

‘ab’ is a 2-D array which consists of 2 one dimensional arrays (each of which can hold 5 elements). ‘abc’ is a 3-D array which consists of 3 two dimensional arrays (each of the 2-D arrays contains a 1-D array which can hold 5 elements). The concept can be extended to higher dimension arrays as well (but generally we wouldn’t use more than 3 dimensions).

166

More on data types & variables

Page 167: C++ Basics to Advanced

How do we initialize a 3-D array? int abc[3][3][2]={ { {40,50}, {10,70}, {20,30} },

{ {45,55}, {15,75}, {25,35} } };

The parentheses make the initialization pretty clear. Of course you can remove all the braces but the declaration wouldn’t be easy to understand. Again in the case of a 3-D arrays, you can drop the first dimension but should mention the other 2.

The following declaration is legal:

int abc[ ][3][2]={ { {40,50}, {10,70}, {20,30} }, { {45,55}, {15,75}, {25,35} } };

You’ll have to take care of the braces. In the above example:

abc[0][0][0] = 40 abc[0][0][1] = 50 abc[0][1][0] = 10 abc[0][1][1] = 70 abc[0][2][0] = 20 abc[0][2][1] = 30

What happens in the following declaration?

int abc[ ][3][2]={ {40,50}, {10,70}, {20,30},

167

More on data types & variables

Page 168: C++ Basics to Advanced

{45,55}, {15,75}, {25,35} };

All that we’ve done is removed the braces which were used to denote that the 1st dimension was 2. But the compiler isn’t smart enough to know what’s on our mind and now it would create the array as: int abc[5][3][2].

abc[0][0][0] = 40 abc[0][0][1] = 50 abc[0][1][0] = 0 abc[0][1][1] = 0 abc[0][2][0] = 0 abc[0][2][1] = 0 abc[1][0][0] = 10 abc[1][0][1] = 70 abc[1][1][0] = 0 abc[1][1][1] = 0 abc[1][2][0] = 0 abc[1][2][1] = 0 and so on.

Moral of the story is that you should do your best to make things explicit when dealing with computers rather than assume that the computer would think the way you’re thinking.

Passing an Array to a Function

This topic is dealt with in depth when discussing about pointers. A simple method of passing arrays to functions is illustrated below: #include <iostream.h>

void disp(int a[ ] ) { for (int i=0;i<3;i++) { cout<<endl<<a[i]; } } int main( ) { int marks[3]={60,70,80}; disp(marks); return 0; }

The output is: 60 70 80 Instead of: void disp(int a[ ] ) you could also have used: void disp(int a[3] ) This will also work. Even if you type: void disp(int a[5] )

168

More on data types & variables

Page 169: C++ Basics to Advanced

it will still work. We shall see more about arrays in the chapter on pointers (because arrays and pointers are closely related).

Remember: The compiler will not allocate memory or create an array called a[5] when it is present in the function header. This is just to tell the compiler that an array with a maximum of 5 elements will be passed to the function. Of course you could specify it as a[2], because in effect only the address of the array will be passed. More about this in the section on pointers.

Test Yourself on Multi-Dimensional Arrays

It is a very common program in schools and colleges. Most of the students tend to mug up the coding and reproduce it at the time of the examination. Of course most of them succeed in doing so (the brain is the best memory device in the world!) but some tend to forget one line of coding and end up with weird outputs. Never mug up programs. Understand the logic of the program and then try it yourself. After all, if your logic is correct then there is no chance that the computer will produce wrong results (it will be as good as you program it to be). Let’s write a program to add two matrices (matrices are two dimensional arrays used in mathematics). The various steps involved are:

Ask the user for the order of the matrix (i.e. the number of rows and columns) To add 2 matrices, they should be of the same size. Get the input for each element of the matrix. First get elements of first matrix and then the second. Add the two matrices (i.e. add corresponding elements) and store the values in a third matrix. Display all 3 matrices.

//To obtain two matrices and two find their sum #include<iostream.h> #include<iomanip.h> //needed for using the manipulator setw ( ) int main( ) {

int i , j , r1 , r2 , c1 , c2 , a[20][20] , b[20][20] , c[20][20]; cout<<"Enter the number of rows and columns of first matrix :"; cin>>r1>>c1; cout<<"Enter the number of rows and columns of second matrix :"; cin>>r2>>c2; // If the matrix orders are not equal then addition is not possible if ( (r1! = r2) || (c1!=c2) ) { cout<<endl<<"Matrix addition is not possible"; return 0; } // If orders are equal then get input for two matrices ‘a’ and ‘b’ for (i =0; i< r1; i ++) { for (j = 0; j<c1; j ++) { cout<<"Enter the "<< i <<" x "<< j <<" element of first matrix: "; cin>>a[ i ][ j ];

169

More on data types & variables

Page 170: C++ Basics to Advanced

} } for (i = 0; i<r2 ; i ++) { for (j =0; j<c2; j ++) { cout<<"Enter the "<<i<<"x"<<j<<" element of second matrix: "; cin>>b[ i ] [ j ]; } } cout<<endl; // Displaying matrix ‘a’ for (i =0; i<r1; i++) { cout<<endl; for (j =0; j<c1; j++) { cout<<setw(9)<<a[ i ][ j ]; } } cout<<"\t"<<"+"; cout<<endl; // Displaying matrix ‘b’ for (i = 0; i<r1; i ++) { cout<<endl; for (j =0; j<c1; j++) { cout<<setw(9)<<b[ i ] [ j ]; } } cout<<endl; cout<<"\t \t \t = "; cout<<endl; // Calculating and displaying the result which is stored in matrix ‘c’ for (i =0; i<r1; i++) { cout<<endl; for (j =0; j<c1; j++) { c[ i ][ j ] = a[ i ][ j ] + b[ i ][ j ]; cout<<setw(9)<<c[ i ][ j ]; } } return 0;

} setw ( ) will be explained later on. It is a manipulator that is used to format the way the output is displayed.

170

More on data types & variables

Page 171: C++ Basics to Advanced

Structures

The following topics are covered in this section:

• Structures • Nesting of Structures

Structures are user defined data types. Structures were used in C and they consist of different data types that are related logically in some way. In other words, a structure groups together different data types under one name. The data items that come under a structure are called structure elements or members (or fields). A structure basically puts different data types into one package.

Even for structures you have a declaration and a definition. Declaration means telling the compiler what type a particular variable is. Definition means that the compiler allots memory space to the variable that was declared.

In the case of structures, a structure declaration forms a template. When a structure variable is defined the compiler automatically allocates sufficient memory to accommodate all of its elements. You can define as many structure variables as you want.

The structure syntax is:

struct tag/structure-name { data-type variable-name;

data-type variable-name;

//more declarations } structure-variables; // Structure definition

‘struct’ is a keyword and it has to be used to declare a structure. Following the keyword, we type the name of the structure (this is also known as the tag-name). Then we have an open brace, followed by the different data types that you want to put in the structure. Finally we add a closing brace and can give the names of the structure variables. This is known as the structure definition.

First of all you should know the use of structures before proceeding further. Suppose you own a small shop. For every set of purchases by a customer, you give the customer a bill. This bill has the bill number and the total amount of purchase mentioned in it. The point to be noted is that, every bill will have a bill number and a particular amount mentioned in it.

171

More on data types & variables

Page 172: C++ Basics to Advanced

Now suppose you want to keep a record of all the bills you have issued through your computer, you will have to store the following in your computer: the bill number and the amount for each bill issued. Bill numbers will be integers whereas the amount to be paid will be a float quantity. Hence, you have two different data types but they are logically related (since every bill will consist of the two data types). It is here that the concept of structures will be found useful.

struct bill { int billnumber; float amount; } one,two;

In the above example, we declare a structure by the name ‘bill’. This contains data items ‘billnumber’ (which is an integer) and ‘amount’ (which is a float quantity). The braces are closed and then we define the variables of the structure, i.e. ‘one’ and ‘two’. This means that ‘one’ and ‘two’ belong to the structure ‘bill’. Both ‘one’ and ‘two’ have their own individual ‘billnumber’ and ‘amount’. Therefore, you can give the bill number and amount for bill ‘one’. Similarly, you can also give the details for ‘two’.

Remember that at the end of the structure declaration and definition, we have to terminate it using the statement terminator.

‘one’ and ‘two’ are known as structure variables. ‘billnumber’ and ‘amount’ are known as the structure elements. For normal built in data types we would declare a variable as: int x;

Where x is the variable and int specifies the type of a variable. Similarly in structures, the variable is declared as belonging to the data type that you have created (which in turn is a combination of different built-in data types).

struct bill { int billnumber; float amount; } one,two;

Instead of this we can write it as follows:

struct bill { int billnumber; float amount; };

172

More on data types & variables

Page 173: C++ Basics to Advanced

// End of struct declaration–memory not allocated since struct variable not defined

int main ( ) { bill one, two; //Structure variable definition return 0; }

Both the methods are equivalent and are acceptable.

Accessing and Initializing Structure Elements:

Every structure variable that you define will have its own individual elements. So how do we access these individual elements of a structure? To access a particular structure element we make use of the dot operator.

The syntax is:

structure-variable . structure-element

For example:

one.billnumber

is the syntax for accessing the bill number of structure variable one.

Now that you know how to access a structure element you can initialize it as well. Initializing means giving a value to the element.

Example:

one.billnumber = 214; one.amount = 1032.25;

Bill ‘one’ has a bill number 214 and the ‘amount’ of the bill is 1032.25 One structure variable can be assigned to another only when they are of the same structure type.

two = one;

is a valid statement because both ‘one’ and ‘two’ belong to the same structure ‘bill’. This statement assigns the values of one.billnumber and one.amount to two.billnumber and two.amount respectively.

173

More on data types & variables

Page 174: C++ Basics to Advanced

An example using Structures concept:

//Declaring the structure phonebook

struct phonebook {

char name[40]; char city[40]; int tel;

}; int main( ) {

phonebook a1; cout<<"Enter the name : "; cin>>a1.name; cout<<"Enter the city : "; cin>>a1.city; cout<<"Enter the telephone number : "; cin>>a1.tel; cout<<"\nThe size of the structure variable is : "<<sizeof(a1); cout<<"\nThe entry you made is (a1): "; cout<<a1.name<<"-"<<a1.city<<"-"<<a1.tel; return 0;

}

The output would be:

Enter the name : Ajay Enter the city : Chennai Enter the telephone number : 21345 The size of the structure variable is : 84 The entry you made is (a1): Ajay-Chennai-21345

Remember: Arithmetic operator works only with built-in data types. Structures are user defined data types.

Hence it is not possible to say: a3 = a1 + a2;

when a1 and a2 are structure variables even if they belong to the same structure type.

174

More on data types & variables

Page 175: C++ Basics to Advanced

Nesting of Structures

Let us add one more entry column to our phonebook structure. This entry will be the birthday of the person. Birthday means that we should store the date, month and year of birth. To illustrate nesting we can group date, month and year into another structure. Check out the program below:

//Another structure called ‘date’

struct date { int day; int month; int year; };

struct phonebook { char name[40]; char city[40]; int tel; date birthday; //birthday is a variable of the structure ‘date’ };

int main( ) {

struct phonebook a1; cout<<"Enter the name : "; cin>>a1.name; cout<<"Enter the city : "; cin>>a1.city; cout<<"Enter the telephone number : "; cin>>a1.tel; cout<<"Enter the date of birth (day, month and year) : "; cin>>a1.birthday.day>>a1.birthday.month>>a1.birthday.year; cout<<"\nThe size of the structure variable is : "<<sizeof(a1); cout<<"\nThe entry made is:"; cout<<a1.name<<"-"<<a1.city<<"-"<<a1.tel; cout<<"\nBirthday is on : "<<a1.birthday.day<<"-"<<a1.birthday.month<<"-"<<a1.birthday.year; return 0;

}

175

More on data types & variables

Page 176: C++ Basics to Advanced

The output of the program is:

Enter the name : Mathew Enter the city : Delhi Enter the telephone number : 65478 Enter the date of birth (day, month and year) : 2 12 1980 The size of the structure variable is : 96 The entry made is:Mathew-Delhi-65478 Birthday is on : 2-12-1980

First of all have a look at the figure below:

Within the phonebook structure we make use of another structure (date). This is called as nesting of structures.

date birthday;

will declare a structure variable ‘birthday’ belonging to the structure ‘date’.

Thus ‘date’ will have three member elements namely ‘day’, ‘month’ and ‘year’. Since ‘birthday’ comes under the ‘phonebook’ structure we can refer to the member ‘day’ by coding:

176

More on data types & variables

Page 177: C++ Basics to Advanced

a1.birthday.day

The rest of the program should be easy to follow. Now what do you think will be the size of ‘a1’. Size of a1 = 40 (char array) + 40 (char array) + 4 (integer) + size of date structure (which is 12 bytes). Therefore size of a1=96 bytes.

We’ll take a look at two more data types that are less frequently used. It’s always better to know what are the various options available because there could be certain instances where they could prove useful.

More on data types & variables 177

Page 178: C++ Basics to Advanced

Unions and more

The following topics are covered in this section:

• Enumerated Types • Union • Passing Structures to Functions • Recap

Enumerated Types

To enumerate means to count. In C++ an enumerated data type permits you to assign different names to your integer values.

The syntax is:

enum enumeration-name{the various constants separated by a comma};

For example:

enum day{mon,tue,wed,thu,fri,sat,sun};

will consider ‘mon’ as being equivalent to 0, ‘tue’ as 1, ‘wed’ as 2 and so on.

mon = 0 tue = 1 wed = 2 thu = 3 fri = 4 sat = 5 sun = 6

If you want to start from zero onwards then you have to specify it:

enum day{mon=1,tue,wed,thu,fri,sat,sun};

Now mon will be equal to the integer value of 1, tue will be 2 and so on.

mon = 1 tue = 2 wed = 3 thu = 4 fri = 5

178

More on data types & variables

Page 179: C++ Basics to Advanced

sat = 6 sun = 7

To create a variable of the enumerated type you simply need to write:

day today, tomm;

Or you could combine the enumeration definition and variable declaration into one statement as follows:

enum day{mon,tue,wed,thu,fri,sat,sun} today, tomm;

Whichever method you use the result is the same. ‘today’ and ‘tomm’ are variables of type ‘day’ which can take any one of the constant values (sun,mon,tue etc…). The integer values that you assign could also be negative integers. Is it possible for 2 enumeration constants to have the same value?

Check this out:

enum day{mon=2,tue,wed,thu=3,fri,sat,sun};

The values would be as below:

mon = 2 tue = 3 wed = 4 thu = 3 fri = 4 sat = 5 sun = 6

It is very much possible to make two or more enumeration constants have the same integer value.

You might wonder of what use is the enumeration type? Actually whatever you do with enumerated types is actually equal to working with integers. You can compare enumerated variables, you can increment them etc. The only advantage is that instead of using numbers you can use some useful names. This is especially useful in larger programs where you might want to indicate some particular state by using a name rather than a number. This would aid in understanding the program without any confusion. You could even consider the example that we’ve considered above; would it be better to refer to days of the week as ‘sun’, ‘mon’ etc…or would it be better to have integer values of 1,2,3 etc.?

Remember: When you attempt to display any enumeration constant, the program will display only the corresponding integer value.

179

More on data types & variables

Page 180: C++ Basics to Advanced

Union

Unions are similar to structures in certain aspects. They also group together variables of different data types and individual members can be accessed using the dot operator. The difference is in the allocation of memory space. A structure will allocate the total space required for a structure variable but a union will allocate only the space required by one element (the element that occupies the maximum size). Suppose you have a union consisting of 4 variables (of different data types), then the union will allocate space only to the variable that requires the maximum memory space. For example:

struct shirt { char size; int chest; }mine;

will allocate a memory space of 5 bytes to the structure variable ‘mine’ (assuming 4 bytes for an integer and 1 byte for a character).

The same thing could be re-written using unions as follows:

union shirt { char size; int chest; }mine;

Now, the union variable ‘mine’ will be allocated only 4 bytes (the compiler knows that a character will require one byte while an integer needs 4 bytes. Hence it allots ‘mine’ a total of only 4 bytes).

Can you reason out what are the consequences of conserving memory space in this fashion? It means that when you use a union only one element will have a valid value at any instance. In the above example, we can have a valid value for either mine.size or for mine.chest

Both the elements cannot have a valid value at the same time. You might be wondering what is meant by a valid value; just check out the example below:

#include<iostream.h> union shirt { char size; int chest; int height; }; int main( ) {

180

More on data types & variables

Page 181: C++ Basics to Advanced

shirt mine; cout<<"\nSize of the union is : "<<sizeof(mine); cout<<"\nWhat size (S/M/L)? "; cin>>mine.size; cout<<"\nThe size is : "<<mine.size; cout<<"\nThe chest measurement is : "<<mine.chest; cout<<"\nThe height measurement is : "<<mine.height; cout<<"\n\nWhat is the chest measurement? "; cin>>mine.chest; cout<<"\nThe size is : "<<mine.size; cout<<"\nThe chest measurement is : "<<mine.chest; cout<<"\nThe height measurement is : "<<mine.height; cout<<"\n\nWhat is the height measurement? "; cin>>mine.height; cout<<"\nThe size is : "<<mine.size; cout<<"\nThe chest measurement is : "<<mine.chest; cout<<"\nThe height measurement is : "<<mine.height; return 0;

}

The output would be as below:

Size of the union is : 4 What size (S/M/L)? s The size is : s The chest measurement is : -858993549 The height measurement is : -858993549 What is the chest measurement? 23 The size is : � The chest measurement is : 23 The height measurement is : 23 What is the height measurement? 12 The size is : - The chest measurement is : 12 The height measurement is : 12

Explanation: You should be able to understand what is meant by a valid value. When we enter the value for ‘chest’, the value of ‘size’ is a weird symbol (an invalid value).

The memory allocation would be as shown below:

181

More on data types & variables

Page 182: C++ Basics to Advanced

The above diagram should make it clear as to why ‘height’ and ‘chest’ values are always the same. Both are integers, both take up 4 bytes, both occupy the same area, so when the compiler reads the 4 byte integer it will display the same value in both cases. It is up to the programmer to ensure that he operates on the correct data. Again, the reason for the invalid character display should be clear: Once the integer value (for height or chest) is stored and you attempt to read the character value the compiler will read one byte out of the four bytes (which are used for the integer).

The main use of unions is when you want to conserve memory space and when you are sure that at any instance you will not require the values for all the elements of a variable. For instance let us say that we have a stockpile of T-shirts in a factory. The computer could maintain a database about the sizes of each T-shirt. The size of T-shirts is either mentioned as small, large and medium or the size is specified in terms of the chest size. Thus every T-shirt will have only one of the two specifications (either a letter or a number). A union is ideal for this purpose.

Passing Structures to a Function

If you want to pass an individual element of a structure to an element, you can simply use the dot operator and pass the element. If you want to pass an entire structure to a function, then simply pass the structure variable as an argument. Let’s see an example:

struct phonebook { int pin; int tel; };

void disp(struct phonebook p) {

182

More on data types & variables

Page 183: C++ Basics to Advanced

cout<<endl<<"The pincode is : "<<p.pin; cout<<endl<<"The tel. no. is : "<<p.tel; }

int main( ) { phonebook m; m.pin=60001; m.tel=23651; disp(m); return 0; }

The output is:

The pincode is : 60001

The tel. no. is : 23651

There isn’t much of a problem in passing a structure to a function but this method has a significant drawback when used with large structures. You will realize this when we discuss in depth about passing values to functions.

Recap

• Scope of a variable refers to the region within which the variable exists.

• A local variable exists only within the block where it is declared whereas a global variable is visible throughout the entire program.

• Automatic variables will not retain their values whereas static variables will retain their value when program flow exits a function.

• An array is a collection of logically related variables of the same data type. • Array elements occupy contiguous memory locations. • Character arrays store strings and has to be terminated by the null character. • A structure is a collection of logically related variables of different data types. • Nesting of structures is permitted. • Enumeration permits the programmer to assign different names to integer constants. • A union is similar to a structure but allocates memory space sufficient to hold the

maximum data type within the union. It doesn’t allocate space for all the data types.

183

More on data types & variables

Page 184: C++ Basics to Advanced

Chapters 4 to 6

1.Q) If an array is initialized as shown below:

int ar[5]={1,2,3}; What will be the value for ar[3]?

Answer.)

A.) Array elements will be initialized to 0. Hence ar[3] and ar[4] will be 0.

2. Q.) int ar[5]; What will be the value for ar[3]?

Answer.) A.) All the array elements will have unpredictable values (commonly called garbage values).

3. Q.) Will the following code work (if it will, what's the output)?:

int ar[5]={1,2,3}; for (int i=0;i<5;i++) { cout<<i[ar]; }

Answer.)

A.) i[ar] is like writing 1[ar], 2[ar] etc. This is legal and i[ar] is equivalent to ar[i]. Thus the output will be: 12300

Generally this is not used in programs because it can lead to confusion and this is not a good practice. Why this works is explained in Pointers.

4. Q.) In the below structure how can we access ‘x’?

struct st1 {struct{struct{int x;} st2;} st3;}y;

Answer.)

A.) This might seem really confusing. First of all just rewrite the coding as shown below:

struct st1 { struct { struct { int x; } st2; } st3; }y;

Now it might not seem all that complicated. To access ‘x’ you should go from the outermost structure to the innermost one (i.e. from ‘y’ to ‘st2’). So we can access ‘x’ by using:

y.st3.st2.x

184

Q&A - Chapter 4-6

Page 185: C++ Basics to Advanced

5. Q.) Can you predict what will happen?

# include <iostream.h> int main( ) { unsigned int j=2; cout<<j--; while(j>=0) { cout<<j--; } return 0; }

Answer.)

A.) The program will run forever because ‘j’ is an unsigned integer and thus it will never become negative.

6. Q.) Can main( ) be recursive?

Answer.)

A.) C++ doesn’t permit the program to call the main ( ) function.

7. Q.) What will happen?

int main( ) { int array1[]={1,2,3}; int array2[]={10,20,30}; array2=array1; return 0; }

Answer.) A.) You will get a compilation error because you cannot assign arrays using the = operator.

8. Q.) What is the result of the code given below:

static int x=2;

int main( ) { int sum=0; do { sum+=(1/x); }while(0<x--); return 0; }

Answer.) A.) A run-time error (divide by zero error).

185

Q&A - Chapter 4-6

Page 186: C++ Basics to Advanced

9. Q.) What would be the output for the following code:

int main( ) { int i=5; int j=6; int k=7; int result; result = j>k ? 2 : i>j ? 3 : 4; cout<<result; return 0; }

Answer.)

Answer is 4. Conditional operator has associativity from right to left. Thus start simplifying from the rightmost side of the expression. The conversion would be as below:

j>k ? 2 : i>j ? 3 : 4;

For the part i>j ? 3 : 4; the result will be 4. Now plug this into the original problem and you will get:

j>k ? 2 : 4;

The result of this is 4 (because j is not greater than k).

10. Q.) What does the code fragment yield:

const int SIZE = 5; int array1[SIZE]={1,2,3,4,5}; cout<<array1[2,3];

Answer.)

In some other programming languages, this notation is used to access elements of a multi dimensional array. But in C++ the expression within the square bracket is evaluated as:

2,3

the result of which is 3 (the comma operator principle is applied here). Thus the equivalent statement is:

cout<<array[3]; 11. Q.) int main( )

{ int rad; cout<<"\nEnter the radius:"; cin>>rad; if(rad>0) { double area=3.14*3.14*rad; cout<<area; } return 0; }

Answer.)

A.) Yes. In C++ we are allowed to declare variables anywhere in the code (it is not mandatory that all variables have to be declared in the beginning).

186

Q&A - Chapter 4-6

Page 187: C++ Basics to Advanced

12. Q.) Explain the following code snippet:

switch(choice) { case 'y': case 'Y': cout<<"\nYou entered yes"; break;

case 'n': case 'N': cout<<"\nYou entered no"; break; default: cout<<"\nWrong choice"; break; }

A.) This is an illustration of a fall-through switch case. If the value of choice is ‘y’ or ‘Y’, the same set of statements will be executed:

cout<<"\nYou entered yes"; break;

The programmer needn’t code separately for case ‘y’ and case ‘Y’ since in either case the same set of statements have to be executed.

Q&A - Chapter 4-6 187

Page 188: C++ Basics to Advanced

More questions (chapters 4 to 6)

Interview and Viva questions

1. Explain the following data structures and distinguish between them: a.) Structure b.) Union c.) Array

2. Why do we need/use functions? 3. What’s the advantage of using a do-while loop? 4. Is it possible to implement a ‘for’ loop as a ‘while’ loop and vice-versa? 5. When do we use a ‘for’ loop and when do we use a ‘while’ loop? 6. What does scope of a variable mean? Distinguish between local and global variables. 7. Explain the following storage types:

a.) auto b.) extern c.) register d.) static

8. What does contiguous memory location mean? 9. Give an example of an enumerated data type. 10. How does a ‘const’ variable differ from a macro? Which one is preferable? 11. Give a simple example of recursion. Is an iterative alternative possible for all

recursion uses?

Programs

Q.) Write a menu-based calculator to perform simple arithmetic operations. (Menu-based means that the user should be presented with a menu from which he/she can choose the operation they want to perform. After an operation is completed the menu should be redisplayed unless the user wishes to exit the program. Hint: use switch-case statement).

Q.) Write a program to display whether a given year is a leap year or not. If it is a leap year check whether it is a century leap year (i.e. the year is divisible by 4 as well as by 100).

Q.) Write a program to display the following pyramid of stars on the screen (using loops):

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

187

Page 189: C++ Basics to Advanced

Q.) Write a program to display the following numerical pyramid on the screen: (similar to the program on pyramid of stars. Instead of stars you will have to print the numbers in proper sequence).

1 2 3 2 3 4 5 4 3 4 5 6 7 6 5 4 5 6 7 8 9 8 7 6 5 6 7 8 9 0 1 0 9 8 7 6

Q.) Write a program to get the marks obtained in 6 subjects by a student and calculate his average percentage (remember to obtain the maximum marks possible in each subject also).

Q.) Obtain a positive integer input from the user and display all the perfect numbers existing less than the number entered by the user. A perfect number is one whose sum of divisors will equal the number itself. For example 6 is a perfect number because 6 has the divisors 1,2 and 3. 1+2+3=6.

Q.) Write a function that can be used to check whether a number is prime or not.

Q.) Use loops to obtain the result of the series:

x/1! + x2/2! + x3/3! + x4/4! + … + xn/n!

where the value of ‘n’ and ‘x’ has to be entered as command line arguments.

Q.) Write a program to obtain a string as input and identify whether the string is a palindrome. (For example: a string ‘malayalam’ is a palindrome because the word remains the same if reversed).

Q.) Write a program to obtain a word (in a character array) and then display the word backwards. The reversed word should be stored in another character array.

Q.) Use a function to rotate an array of numbers. The number of positions to be rotated will be provided by the user. A positive number means that the array should be rotated to the right and a negative number means a left side rotation. For example if the user inputs an array as: 1 2 3 4 and specifies a value of +3 then the resultant display should be: 2 3 4 1. (When rotating to the right the last value should come to the first place in case we exceed the last element).

Q.) Write a program to print the numbers from 100 to 1 without using any type of loop statement. Hint: use recursion.

Q.) Write a program to obtain the date (as three integer values for date, month and year). The output should display the date, name of the month and the year. The output should also state the number of days in that particular month and whether the year is a leap year or not.

For example: if the user inputs the date as 11 10 1980, the output should be:

188

Page 190: C++ Basics to Advanced

October 11,1980.This month has 31 days. This is a leap year.

(Use switch-case to display the name of the month and enclose the switch case within a function so that it can be called whenever needed).

Q.) In the above program, provide an option such that the user can enter a certain number of days and the program should calculate and display the corresponding date.

Q.) Use looping technique to print the alphabets from ‘a’ to ‘z’ on the screen. (Hint: use ASCII values and type conversion).

Q.) Write a program to convert a decimal number into a binary number (keep dividing by two and the remainders will form the binary number).

Q.) Using recursion write a program to convert a decimal number into its binary equivalent.

Q.) Write a program to store a set of names using arrays (you have to use 2-Dimension character arrays) and display the names.

Q.) Write a program to find the greatest among ‘n’ numbers (value of ‘n’ is provided by the user). Use ternary operator.

Q.) Create a function which will calculate the number of days between two dates present in the same year (you have to take care of leap years as well).

Q.) Modify the above function such that you can find the number of days between two dates in different years.

Q.) Create a simple game of Tick-Tac-Toe (also called as ‘X and 0’) made for a user to challenge the computer. The user should have a display similar to the diagram below:

The user will choose a particular square by specifying the corresponding row and column. After the user enters his/her choice, the program should display the new diagram with the corresponding square marked. Once the user makes a move, the computer should make its move.

189

Page 191: C++ Basics to Advanced

The logic of the game can be improved such that the computer cannot be defeated. Also you could provide an option wherein the user can choose whether the computer should play first or not.

(hint: use separate functions for each of the tasks like computer’s move, user’s move, checking for a win after every move, checking for a draw, displaying the diagram after every move etc. These functions can be called from within the main( ) function using looping technique. You could also use global variables).

190

Page 192: C++ Basics to Advanced

Pointers- an Intro

The following topics are covered in this section:

• Address of a Variable • Run time and Compile time • Pointers • Be Careful

Address of a variable (An introduction to pointers)

Before we get into pointers let’s take a real-life analogy. A city consists of a lot of houses. To locate a house you need to have an address (and that address should be unique otherwise we would have lots of confusion). Similarly, computers deal with memories instead of cities. They store data in memory at particular locations. Memory consists of lots of bits. Each memory location can store a byte (8 bits make a byte and it is more logical to consider them as bytes rather than as individual bits). A memory location is like a house, which has a variable (instead of people) residing in it. Actually it is the value of the variable that resides in the house. You may remember that a character requires one byte for storage, an integer two/four bytes etc. Computers use memory addresses to access the memory locations.

Pointers are variables that store memory address. This address is usually the address of another variable. All variables are stored in memory locations.

Just like a house has a unique address, every memory location will have its own address. In computers the address is a hexa decimal number. For example: 0X8566fff4 is a hexadecimal number representing an address. Hence when we say that values of variables are stored in the computer, we actually mean that the value has been stored in a particular address.

When we write:

int x=5;

we might be thinking that ‘x’ is physically having a value of 5. In reality, ‘x’ will correspond to some memory location and it is in this location that the value 5 is stored (5 is stored in a binary format and not as a decimal number). This is why a variable is sometimes defined as a named memory location. It is more convenient for us to work in this way (a programmer cannot be expected to know the addresses where variables are allocated memory).

So, how do we find out the address of a variable? The ‘address of’ operator is denoted by ‘&’.

191

Pointers

Page 193: C++ Basics to Advanced

This can be applied to any variable as illustrated below:

#include <iostream.h> int main ( ) { int var=100; cout<<"Value : "<<var; // The output will be 6 cout<<"\nAddress : "<<&var; // Output will be the address of var return 0; }

The output would be:

Value : 100 Address : 006AFDF4

Pointers

Pointers are variables that store memory address. This address is the address of another variable. Hence a pointer points to another variable.

Declaring a Pointer:

Syntax:

data-type *name-of-pointer;

When * is used in declaration statements, it denotes a pointer. The data type denotes the data type to which the pointer points.

Let's see an example:

int main( ) {

int marks = 70; // marks is an integer int * p; // p is a pointer p = &marks; // p points to marks cout<<"\nAddress is : "<<p; // This displays the address. cout<<"\nValue at that address is : "<<*p; // display the value. return 0;

}

The only doubt you may have is about

cout<<* p;

192

Pointers

Page 194: C++ Basics to Advanced

In this context * is known as a de-referencing or indirect value operator. This operator will give the value stored at a particular memory location (in other words it will give the value stored at a particular address). It is the complement of the ‘&’ operator. Hence, *p means value at p (which holds address of marks).

The output of the program is:

Address is : 006AFDF4 Value at that address is : 70

To summarize:

• we can say that * means 'value at the address'.

• Both the operators * and & can be considered as pointer operators.

The figure below should make the concept of the program very clear to you:

Everything is stored in memory. So what about the pointer itself. A pointer stores an address; but where does it store it? The answer can be found in the above figure. The pointer itself has a memory address where it stores the address of the variable that it points to. Confusing? Basically, the pointer has an address where it stores another address. In the earlier program, if you type the following code:

cout<<&p;

You will get the result as 006AFDF0. That is the address of the pointer itself.

193

Pointers

Page 195: C++ Basics to Advanced

This brings to view another point. The difference between pointers and a general variable is that address is primary for pointers. The value stored at the address is secondary. For other variables, the value is primary while the address where the value is stored is secondary.

If you want to find the value of ‘marks’ you would just write:

cout<<marks;

But if you want to find the address of ‘marks’ you have to write:

cout<<&marks;

In the case of pointers,

cout<<p;

will give the an address (address of the variable it points to) and the statement

cout<<*p;

will give you the value of the variable.

Be careful with Pointers

This section deals with some of the mistakes that can be committed using pointers.

1.) Make sure that your pointer variable points to the correct type of data. If you declare a pointer of type integer then make sure that the address it holds contains an integer.

float y = 6.6; // y is a float quantity int * p; // p points to an integer type p = &y; // Wrong - p is supposed to point to an integer but y is a float.

2.) Be careful while making multiple pointer declarations.

Suppose you want to declare two pointers: p1 and p2 that point to an integer data type. You might be tempted to write the declaration as:

int * p1, p2;

Seems right, doesn't it? This declaration is wrong. This will declare p1 as a pointer pointing to integer type. The compiler will consider p2 as an integer and not a pointer. Since * precedes p1, the compiler considers only p1 as a pointer. Hence be careful while declaring pointers.

194

Pointers

Page 196: C++ Basics to Advanced

3.) A pointer should store an address.

Consider an extract from a program given below:

int *p; // marks is a pointer that points to data type long. *p = 82; //wrong

Note the point that we have not yet stored any memory address in marks.

When a pointer is created the compiler will allot memory to hold an address. It will not allocate memory to hold the data to which the address points. Perhaps it's a bit confusing.

The problem with our program is that we are supposed to store an address in p. After storing an address in p you can then store some value at that address. What we have done is that we haven't assigned any address to p. Without an address you can't store a value. Hence the value 82 cannot be placed anywhere. A pointer which is not initialized is called a ‘dangling pointer’.

4.) Pointers are not the same as integers. Pointers hold a memory address and hence they cannot be multiplied or divided. Pointer addition will be discussed later.

5.) Not advisable to assign a memory address on your own (unless you are really sure of what you are doing).

Consider the following:

int * p; //Correct p = 006AFDF4; //wrong

A pointer stores a memory address. So you may think that the second line is correct. We have assigned a hexadecimal number to p (which is a pointer). The problem is that the compiler doesn't know that 006AFDF4 is a memory address. You may get an error message saying ‘data type mismatch’. Instead you can write it as:

p = (int * ) 0x006AFDF4; // This is correct.

This is correct because we are forcibly telling the compiler that this is a memory address (you’ll learn about forcing in the section on ‘casting’).

6.) Some programmers prefer to initialize a pointer to NULL. NULL is a predefined constant with a value of 0. When initialized to null, the memory address stored in the pointer will be: 0x00000000. The code below will compile but it is an erroneous code:

int *p; int a=5; cout<<*p; // value displayed is 910569528

195

Pointers

Page 197: C++ Basics to Advanced

In this code, the pointer ‘p’ hasn’t been assigned the address of ‘a’. ‘p’ will have some random memory location and reading the value at this location produces a garbage value. Programmers thus prefer to use:

int *p=NULL; int a=5; cout<<*p;

This code will generate an error when it is run in your computer because ‘p’ is pointing to the location 0x00000000.

7.) Potential hazards of using uninitialized pointers:

There are instances when a programmer declares a pointer and forgets to initialize it. Later in the code the programmer uses this pointer and this can lead to serious errors. Since the pointer has some unknown address value, when we dereference such a pointer we cannot predict what will be the value. A bigger problem might arise if you attempt to change the value stored at that address. For ex:

int *p; *p=5;

In this case, the value at an unknown location will get changed to 5. This particular memory location could actually be some other variable in your program. Thus indirectly you would have changed the value of another variable and it will become difficult to trace the problem.

You may be wondering why we need to tell the compiler as to what type of data a pointer is going to point to? A pointer just holds an address, so why do we need to specify the data type pointed to?

The problem will occur when you make use of the dereferencing operator to get the value stored at the address. If a pointer points to a character, then the compiler knows that when we dereference the pointer it should only read one byte. If the pointer points to a short int the compiler knows that it has to read 2 bytes and so on. If we didn’t specify what data type the pointer will point to, then the compiler has no idea as to how many bytes it should read when dereferenced. And if we never dereferenced a pointer then we’d never know what is stored at a particular memory address!

What’s the difference between:

int *ptr;

and

int* ptr;

196

Pointers

Page 198: C++ Basics to Advanced

It is a matter of choice. Some programmers prefer the first one while you may frequently encounter the second type of declaration. The advantage of using the first method is that you are less likely to commit the mistake of declaring a pointer and an integer in one statement. Ex: if you want to declare to pointer ptr1 and ptr2, it is less likely to type

int *ptr, ptr2;

Use whichever method you are comfortable with.

197

Pointers

Page 199: C++ Basics to Advanced

More Pointers - II

The following topics are covered in this section:

• Arithmetic operations on Pointers • Pointers and Arrays

Arithmetic operation on Pointers

You can't perform multiplication and division on pointers but you can do addition and subtraction on pointers. Suppose that p1 is a pointer that points to an integer and the address is 100 (yes, addresses will usually be a larger hexadecimal number but assume 100 as a memory address for simplicity). Also assume that an integer occupies 4 bytes. Now if we say:

p1++ ;

what would the above expression do? The value of p1 (i.e. the address pointed to by p1) gets changed. The address becomes 104 not 101 because an integer occupies 4 bytes. If an integer occupied 2 bytes then the result would be address 102. Similarly if you do:

p1--;

the address of p1 will be 98.

Hence, when a pointer is incremented it points to the memory location of the next element of its data type.

Similarly you could say the following:

p1 = p1 + 10;

This makes p1 point to the tenth element of p1's data type beyond the current position. If p1 points to a character type then p1 + 10 would move the pointer 10 bytes. Suppose p1 were a integer then p1 would move 20 bytes (if int occupies 2 bytes).

Can we add two pointers?

Addition of two pointers is not allowed. It doesn’t make logical sense to add two pointers because you won’t know what the new address will be. You’ll get a compiler error if you attempt to perform such an operation. But subtraction of two pointers is allowed since this will give you the number of elements lying between the two addresses.

You can use pointers to find the size occupied by a data type (i.e. without using the sizeof operator) as shown below:

198

Pointers

Page 200: C++ Basics to Advanced

long int i; long int *ptr; ptr=&i; cout<<endl<<(long(ptr+1)-long(ptr));

This would give an output of 4 (the size of a long integer). You may be wondering why we are specifying long in the cout statement. If you typed:

cout<<endl<<((ptr+1)-(ptr));

the compiler would not evaluate the value for ptr+1. Instead it would simply expand the expression as: (ptr+1-ptr) which would give an output of 1. In the expression:

cout<<endl<<(long(ptr+1)-long(ptr));

long( ) is called explicit casting. This will be discussed in detail later (we force the compiler to store the result of ptr+1 in long integer format and we do the same for ptr).

The statement

cout<<endl<<(long(ptr+1)-ptr); //ERROR

would cause an error saying that a pointer can only be subtracted from another pointer (or you’d get the message: Illegal pointer subtraction).

Assignment Operator with pointers:

Whenever we make use of pointers in a program we should be careful about what we are dealing with (i.e. are we wanting to refer to the memory location or do we want to use the value stored at that memory location). Consider two pointers, p1 and p2 (both pointing to integer data types).

p1=p2;

*p1=*p2;

In the first case, both p1 and p2 will now point to the same memory location. But in the second case only the value stored at the location pointed by p1 will change (i.e. the address will not change).

Similarly, if (p1 == p2) and

if (*p1 == *p2)

are completely different. In the first case we are checking whether both pointers contain the same address while in the second case we check whether the value contained at the memory location pointed by them is equal.

199

Pointers

Page 201: C++ Basics to Advanced

Pointers and Arrays

Arrays can be accessed using pointers instead of their index numbers. Consider the following program:

int main( ) {

int marks[3]; int* p; p = &marks[0]; // Pointer points to the first element of array. marks[0]=58; marks[1]=61; marks[2]=70; cout<<endl<<*p; // Output is 58, because p has address of array[0] cout<<endl<<*(p+1); // output is 61, explanation below. return 0;

}

Output is:

58 61

‘endl’ is a manipulator and it is the same as "\n". It is used to go to a new line. When you say *(p + 1), the pointer adds one to itself (this means that the pointer increments itself by the number of bytes of its data type). Hence it will go to the next integer address which is marks[1]. The output will thus be 61.

Pointers and arrays are very closely related. In the above program instead of:

p = &marks[0];

try writing:

p = marks;

You might at first feel that this would lead to an error but it won’t. This is a perfectly correct statement. When you refer to an array name without using the index number, it means that you are referring to the address of the array (or in other words the address of the first element of the array).

Remember: An array’s name without an index number is the same as the address of the first element of the array. (This is a common question in C++ tests).

You might wonder then what is the difference between a pointer and an array name (i.e. an array identifier)? Are both the same?

200

Pointers

Page 202: C++ Basics to Advanced

Well, a pointer of type integer (like ‘p’ that was declared above) can point to any integer variable. ‘p’ can point to any other variable (as long as the variable is an integer type). But the array is constant. Whenever you use the array name, it will only refer to the same array. This means that the array is similar to a constant (it remains the same) and in fact it is sometimes called a constant pointer. Thus in the previous example:

p = marks;

is correct because pointer ‘p’ can take different values but

marks = p;

is invalid because ‘marks’ is like a constant pointer (value cannot be changed).

We refer to an array value by indexing:

marks[2]=70;

using pointer we can write it as:

*(p+2)=70;

but even indexing in pointers is allowed:

p[2]=70;

is also a valid statement.

Remember:

*(p+2) is 70 but

*p + 2 will not be 70. Since * has a higher operator precedence than +, the result of *p + 2 will be 58 + 2 (assuming that *p has a value 58). When using pointers to access array elements ensure that you use the parentheses.

Similarly it is not that only the pointer should be called by using the * operator. Since an array is also like a pointer, the following:

marks[1] = 61;

is the same as:

*(marks+1) = 61;

201

Pointers

Page 203: C++ Basics to Advanced

Beware: Don’t confuse the de-reference operator (*) with the same * that is used to declare a pointer. Though they both seem the same, they are totally different in function; one is to declare a pointer and the other is to access the value held at that particular address.

int j; int* p = &j; j = 5; cout<<*p;

The above piece of coding is correct because we are initializing the pointer ‘p’ to the address of the variable j.

Try this:

int* p=5;

Absolutely wrong! Cannot assign a value to ‘p’. Consider a modification of the above program.

#include <iostream.h> int main( ) {

int marks[3]; int* p; p = &marks[2]; // Pointer points to the third element of array. marks[0]=58; marks[1]=61; marks[2]=70; p = p-1; //pointer decrements by one, goes to the previous integer address cout<<endl<<*p; // p is having address of marks[1]. cout<<endl<<*(p-1); // p is further decremented by one. This points to marks[0]. return 0;

}

The output would be:

61 58

There shouldn’t be any problem with the above program. We have made use of pointer arithmetic in an array.

202

Pointers

Page 204: C++ Basics to Advanced

We had discussed earlier about passing arrays to functions earlier and you can use any one of the following methods:

void disp(int a[ ] )

or:

void disp(int a[3] )

Now you should have understood as to how the above two methods really work. In effect we are actually passing the address of the array to the function. Thus you could also pass a pointer to a function (of course the pointer should have the address of the array). See the example below:

#include <iostream.h> void clear(int *point, int size) {

for (int i=0;i<size;i++) { *(point+i)=0; }

} int main( ) {

int *p; int marks[3]={50,60,70}; cout<<"The original marks are : "<<marks[0]<<" "<<marks[1] <<" "<<marks[2]; p = marks; clear(p,3); cout<<endl<<"The cleared marks are : "<<marks[0]<<" "<<marks[1] <<" "<<marks[2]; return 0;

}

The output will be:

The original marks are : 50 60 70 The cleared marks are : 0 0 0

‘p’ is a pointer to an integer having the address of marks array. Hence this pointer is passed to the function ‘clear’ which uses this pointer to clear the entire array (it sets all the values of the elements to 0).

203

Pointers

Page 205: C++ Basics to Advanced

Instead of:

clear(p,3);

you could even have used:

clear(marks,3);

This would have also worked perfectly well because ‘p’ and marks mean the same thing.

Another modification you could have done is to change the function syntax to:

void clear(int m[ ],int size) { for (int i=0;i<size;i++) { m[i]=0; } }

The relationship between pointers and arrays may seem confusing at first, but with practice you’ll soon master the topic.

Remember: When an array is passed to a function it is passed by reference (and not by-value). If the function makes any changes to the array then the original array will be affected because the address is passed and not the value (this is called ‘passing by reference’ and is discussed in the next section).

204

Pointers

Page 206: C++ Basics to Advanced

Pass by value and pass by reference (and reference variables)

Pass by value and Pass by Reference

We have seen as to how to pass values to a function through arguments. Actually there are two ways to pass values to a function through arguments. These two methods are explained below with examples.

Pass By Value:

Example: void check (int x) {//body of function } int main ( ) { int b = 10; check (b); }

In this function, ‘x’ is a parameter and ‘b’ (which is the value to be passed) is the argument. In this case the value of ‘b’ (the argument) is copied in ‘x’ (the parameter). Hence the parameter is actually a copy of the argument. The function will operate only on the copy and not on the original argument. This method is known as PASS BY VALUE. So far we have dealt only with pass by value (except when passing arrays to functions).

// Pass by value illustration

#include <iostream.h>

int square (int x) { return x*x; }

int main ( ) { int num = 10; int answer; answer = square(num); cout<<"Answer is "<<answer; // answer is 100 cout<<" Value of a is "<<num; // num will be 10 return 0; }

205

Pointers

Page 207: C++ Basics to Advanced

You can see that the value of ‘num’ is unchanged. The function ‘square’ works only on the parameter (i.e. on x ). It does not work on the original variable that was passed (i.e it doesn't work on ‘num’).

The diagram makes it quite clear as to what happens when we call square(num). The following initialization takes place:

int x = num;

and the function square( ) operates only on a copy of num.

Pass By Reference

In pass by reference method, the function will operate on the original variable itself. It doesn't work on a copy of the argument but works on the argument itself. Consider the same square function example:

// Illustration of pass by reference

#include <iostream.h>

void square (int *x) { *x = (*x) * (*x);

206

Pointers

Page 208: C++ Basics to Advanced

} int main ( ) { int num = 10; square(&num); cout<<" Value of num is "<<num; // Value of num is 100 return 0; }

As you can see the result will be that the value of a is 100. The idea is simple: the argument passed is the address of the variable ‘num’. The parameter of ‘square’ function is a pointer pointing to type integer. The address of ‘num’ is assigned to this pointer. You can analyze it as follows: &num is passed to int *x, therefore it is the same as:

int *x = &num;

This means that ‘x’ is a pointer to an integer and has the address of the variable num.

Within the function we have:

*x = (*x) * (*x);

* when used before a pointer will give the value stored at that particular address. Hence we find the product of ‘num’ and store it in ‘num’ itself. i.e. the value of 100 is stored in the address of ‘num’ instead of 10 which was originally present there. The diagram below illustrates the difference between pass by value and pass by reference. Now when we dereference ‘x’ we are actually manipulating the value stored in ‘num’.

207

Pointers

Page 209: C++ Basics to Advanced

This is the pass-by-reference method which was used in C. In C++ there is a different approach. Of course you can use the above method, but C++ has its own special way.

C++ style of Pass by Reference

In the C style we have seen that you need to make use of & and lots of * symbols to pass-by-reference. In C++ we make use of the ‘reference parameter’ (or reference variable) and can avoid the use of so many symbols. All you need to do is put & before your function's parameter.

Example:

void square (&x)

Whatever operation is done on x will actually affect the original calling argument. Basically ‘x’ can be said to be an implicit pointer. The difference is that you needn't use *x to operate on the parameter. Consider the same example:

// Pass by reference C++ style example

void square ( int &x ) // x becomes a reference parameter { x = x * x; //no need to write *x = (*x) * (*x); as we did earlier }

int main ( ) { int num =10; int answer; square(num); // No need to say &num as we did earlier cout<<" Value of num is "<<num; // Value of a is 100 return 0; }

The line x = x * x ; actually operates on ‘num’ and not on a copy of ‘num’. When the C++ compiler sees that a function has been declared with its arguments having ‘&’, it knows that the function is a pass-by-reference type.

208

Pointers

Page 210: C++ Basics to Advanced

More about reference variables

Reference variables are not applicable only to function parameters. You can have them in other places of the program as well. Consider the following program:

#include <iostream.h> int main( ) {

int x; int &ref = x; //ref is a reference variable x=5; cout<<endl<<x<<" "<<ref; x++; cout<<endl<<x<<" "<<ref; ref++; cout<<endl<<x<<" "<<ref; cout<<endl<<&x<<" "<<&ref; return 0;

}

The output is:

5 5 6 6 7 7

0x0065FDF4 0x0065FDF4

In the above program ‘ref’ is a reference variable (in other words it is an alternative name for the variable ‘x’). So, if you perform an operation on ‘x’, the value of ‘ref’ will also change and vice-versa. The concept of reference variables is that even though we provide different names to the two variables, they both will refer to the same memory address. In the above program, the address of both ‘ref’ and ‘x’ is the same.

• Usually reference variables will only be used with respect to functions (using them as shown in the program above can lead to confusion). Passing by reference is especially useful in passing structures and objects to functions.

• In the case of structures, if you use pass-by-value, memory space will be used up to create a copy of the structure within the function. Passing the structure by reference will avoid this overhead.

• In the case of classes, if you pass an object by pass-by-value you will be invoking an extra destructor (because a copy of the object is made). In passing an object by reference we can avoid this problem (this will be discussed later).

• References can also be returned by functions (dealt with in operator overloading and in streams).

209

Pointers

Page 211: C++ Basics to Advanced

Can arrays be passed by value?

When we pass arrays to a function we are actually passing the address of the first element of the array. This is as good as passing a pointer to the function. Thus the function will be directly operating on the original array and not on a copy of the array. There is no way of creating a function which when called will operate on a copy of the array (unless you create a new array within the function and manually copy the elements from the original array to the new array).

Test yourself on Pass by Reference

This section is intended to make sure that you've understood pass by reference. Write a C++ program to get two numbers from the user and swap the values using a function. You have to do it using reference parameters.

The solution will be as follows: #include <iostream.h> void swap (int &x, int &y) //pass by reference { int t; t = x; x = y; y = t; } int main ( ) { int a , b; cout<<"Enter the value for a : "; cin>>a; cout<<"Enter the value for b : "; cin>>b; cout<<"a and b before swap are : "<<a<<","<<b; cout<<endl; swap (a,b); cout<<"a and b after swap are : "<<a<<","<<b; return 0; }

210

Pointers

Page 212: C++ Basics to Advanced

More on references

• Reference variables are aliases (an alternate name to a variable).

int x; int &ref=x; //we tell the compiler that ‘ref’ is another name for ‘x’

Working on ‘ref’ is the same as working on ‘x’ and vice-versa.

But since a reference variable is an alias, we should initialize the alias.

int x; int &ref; //ILLEGAL reference variable should be initialized.

This makes sense because an alias should be an alternate name for something (and we need to tell the compiler what that something is).

211

Pointers

Page 213: C++ Basics to Advanced

• Non-const references should be initialized using L-values (i.e. using something which is permitted on the left hand side of an assignment expression).

int &ref=5; //ERROR

This is an error because ‘5’ is an integer constant and it is not an L-value (we cannot use 5 on the left side of an assignment). Logically speaking also this isn’t possible because a reference variable is an alias (you use an alias instead of another name). One might argue, “instead of the constant 5, I intended to use the term ref”. But when we think from the viewpoint of the compiler, we can understand the complications involved if this were permitted. Let’s say this statement was legal. Now (though the programmer might not purposely do it), there is nothing to stop the programmer from coding:

int &ref=5; //ERROR (but let’s assume this is permitted) ref=10;

What now; where is the new value of 10 supposed to be stored? (there is no location for it).

But if we use a const-reference:

const int &ref=5; //Permitted

the compiler won’t complain. The reason is that now we tell the compiler, “This is a const reference and in case I attempt to assign any values to it then you can flag an error”. The compiler feels reassured and it will permit the use of ‘ref’ instead of 5.

Note: For the above purpose, it would be more clearer and effective to use

const int ref=5;

(since we are not really making use of any functionality of a reference variable).

• We have seen that passing by reference is similar to passing a pointer (as was done in C

programming). In fact reference variables and pointers are quite similar. You might wonder, “why not use pointers itself?”

When passing a pointer, the function definition will be cluttered with a lot of dereferencing.

Passing pointers (C style) Passing by reference void square (int *x) { *x = (*x) * (*x); }

void square (int &x ) { x = x * x;

}

The code is clean and reader-friendly when passing by reference.

212

Pointers

Page 214: C++ Basics to Advanced

• We can use the ‘const’ keyword in reference parameters to ensure that the function doesn’t modify the argument. For example:

//Function won’t modify the argument

void print(const int &x) { cout<<endl<<"The value is:"<<x; }

void square (int &x) { x = x * x; }

int main ( ) { int y=5; print(y); square(y); print(y); return 0; }

The advantage of using void print(const int &x) is that when someone reads the code later they will immediately know that the function print( ) doesn’t modify the argument.

The following:

void square (const int &x) //compiler error { x = x * x; }

is an error. The compiler will complain saying, “you are attempting to modify the value of the constant reference ‘x’”.

• Same address:

int x; int &ref=x; cout<<endl<<&x; //address of ‘x’ cout<<endl<<&ref; //address of ‘ref’

Both addresses will be the same (which is why working on ‘x’ is the same as working on ‘ref’).

213

Pointers

Page 215: C++ Basics to Advanced

• We can return references from functions but beware.

int& dummy( ) { int x=5; return x; }

int main ( ) { cout<<dummy( ); return 0; }

The function dummy( ) is supposed to return a reference. Though the code might work as expected (an output of 5); the compiler will issue a warning (sometimes you might also get unexpected results). The warning is: “reference to local variable `x' returned”. The variable ‘x’ would be destroyed when we reach the end of the function dummy( ). A reference is just an alias. Thus when ‘x’ is destroyed, we shouldn’t use the alias of ‘x’ as well. The rule is: Don’t return references in such scenarios.

214

Pointers

Page 216: C++ Basics to Advanced

More Pointers - II

The following topics are covered in this section:

• Returning pointers from functions • Run Time and compile time • Dynamic Allocation

Returning pointers from functions

You can return pointers (or arrays) from functions. The syntax to declare a function which will return a pointer is:

return-data-type* function-name (pararmeters);

For example:

int* create ( );

This is to declare a function called create ( ) that will return a pointer to an integer. Check out the example given below:

#include <iostream.h> int* create( ) {

int marks[3]; int *pt=marks; for (int i=0;i<3;i++) { marks[i]=80; } return pt;

} int main( ) {

int *p; p=create( ); cout<<endl<<"The marks are : "<<*(p)<<" "<<*(p+1) <<" "<<*(p+2); return 0;

}

215

Pointers

Page 217: C++ Basics to Advanced

The output is:

The marks are : 80 80 80

As you can see, we return ‘pt’ (which is a pointer to an integer) from the function. This is like returning an address from the function. There is nothing special about returning pointers from functions but you have to be careful with the syntax for the function header.

Run-time and Compile-time

The concept of OOPs (Object Oreinted Programming) makes emphasis on making decisions during run-time rather than at compile-time. Run-time is the time when someone runs your program. Compile-time is the time at which you compile the program.

To differentiate between run-time and compile-time let's take a real life example. Let us suppose that tonight you decide you are going to walk to office tomorrow. You decide that you will start from home at 7:30am. You have decided these two things at night itself. This is similar to compile-time. Decision is taken before the time of action.

Now in the morning, you start from home as decided at 7:30. You walk and on the way someone, your girlfriend or boyfriend, comes in a car. They stop beside you and offer to give you a lift to your office. You had initially decided to walk but suddenly change your mind. Why walk all the way to office? Maybe I could walk tomorrow. You step into the car and reach office. This was a run-time decision. You decided on the spot during the time of action.

Coming back to C++, run-time decisions provide more flexibility to adjust. As you can see, in real-life, most of us prefer to take decisions depending on the situation that arises instead of fixing a plan earlier. We take decisions on the spot instantaneously and in C++ this can be illustrated with an example. For example: in a program that makes use of an array for storing a series of numbers, you may declare an array of 20 elements. But sometimes the user may want to enter more elements. Hence you may declare an array of 200 elements. The compiler will allocate memory space for all the 200 elements during compile time itself. But your user may not always make use of the 200 elements. Sometimes it may be 10 sometimes 50. By declaring 200 elements the space allocated to the unused elements is a waste. It would be better if we could decide on the size of the array during run-time (i.e. when the user runs your program rather than fixing the size when you write and compile the program). According to the user’s preference the program could allot the required space for the array. This is a compile-time decision and this can be implemented through pointers, as we shall see later.

216

Pointers

Page 218: C++ Basics to Advanced

Dynamic Allocation

Whenever you declare variables required amount of space is allocated for them in the memory. But there are certain instances when we might not be able to predict how much space might be required when the program executes; it may depend on the user.

Suppose that you want to write a program to get the marks from the user and display the entered values. You do not know how many marks the user will enter (it could be for 2 subjects or for 8 subjects). So, you might write the following code:

# include<iostream.h> int main ( ) {

int size,i; cout<<"Enter the size of the array : "; cin>>size; int marks[size]; //WRONG cout<<"\nEnter the marks: "; for (i = 0; i<size; i ++) { cin>>marks[i]; } cout<<endl<<"The marks you entered are : "; for (i = 0; i<size; i ++) { cout<<endl<<marks[i]; } return 0;

}

The program won’t compile. Why? When you reason out you might feel that it is logically correct. Think from the compiler’s point of view:

Two integers ‘i’ and ‘size’ have been declared. The compiler will allocate memory for these two integers. Next, we ask the user to input the value for ‘size’. This value is stored in memory. Next we say:

int marks[size];

Seems right, doesn’t it? We have got the value for ‘size’ from the user and now we are declaring an array called ‘marks’ which depends on what the user entered. What’s wrong with this? The problem is with memory allocation. When the compiler reads each declaration of a variable it will keep allocating memory for the variables in memory. Now the compiler cannot allocate a memory space for the array ‘marks’. Why? Because the user will enter the value of ‘size’ only when the program is run not when it is being compiled. The compiler has no idea whether to allocate space for one integer or for 10 integers (because it depends on the value of ‘size’). The compiler will give an error saying that the size of ‘marks’ is undefined. This should

217

Pointers

Page 219: C++ Basics to Advanced

also illustrate the difference between compile-time and run-time. So, how to overcome this problem?

Simple. You could give the variable ‘size’ some value to start with. Replace:

cout<<"Enter the size of the array : "; cin>>size;

with:

size=4;

Now everything should be fine. Is it so? Try it and you’ll get the same compiler error. The compiler reads and stores the value of 4 for ‘size’ but it still will not substitute that value in:

int marks[size];

The compiler assumes that ‘size’ is a variable (since it was declared like that) and since a variable’s value can change in the program, it will not compile the code. One way to correct this is by declaring the maximum size of the ‘marks’ array by saying:

int marks[4]; //program will compile

There is no need for the variable ‘size’ and the user can enter only a maximum of 4 values because that is the space allocated for the array ‘marks’.

Another way to correct the program is to declare the variable ‘size’ as a constant.

const int size=4;

Now you can use:

int marks[size];

The reason that this is valid is because the compiler makes note of the fact that ‘size’ is a constant and has been given a constant value 4. Since this value will never change in the program and space will be allocated for 4 elements in the array ‘marks’. C programmers made use of macros (instead of ‘const’):

#define SIZE 4

to define constants. When the compiler comes across the term ‘SIZE’ anywhere in the program it will simply substitute the value of 4 for ‘SIZE’.

But whatever you do, the compiler limits you to fixing the size of the array at compile-time. Can you decide the array size at run-time? Dynamic allocation comes to the rescue.

218

Pointers

Page 220: C++ Basics to Advanced

Dynamic allocation means allocating (or obtaining) and freeing memory at run-time. There are certain cases where run-time decisions are better. For example, deciding the size of an array. Similarly you can free up allotted memory in your program when you don’t need a particular array by deleting the entire array itself. The two operators used for this purpose are: ‘new’ and ‘delete’.

‘new’ is used to allocate memory while ‘delete’ is used to free the allocated memory (memory which was allocated by ‘new’). The free memory available is sometimes referred to as the heap. Memory that has been allocated by ‘new’ should be freed by using ‘delete’. If you don't use ‘delete’ then the memory becomes a waste and cannot be used by the system. This is called memory leak (i.e. when allocated memory is never returned to the heap). You could go on taking memory from the heap till it gets exhausted. This will lead to memory leak and can cause problems to your program and to other programs which attempt to take memory from the heap.

The syntax is:

data-type * name = new data-type; delete name ;

Beware: Both data types should be the same.

Example:

int * p = new int; delete p;

Remember: delete p;

means that the data pointed to by ‘p’ is deleted. The pointer ‘p’ will not get deleted. The following coding is correct:

int m = 20; int *p = new int; *p=5; cout<<*p<<endl; //output is 5 delete p; //5 will be deleted but pointer ‘p’ still remains p=&m; //valid cout<<*p; //value of 20 displayed

The new and delete operators are very useful when dealing with arrays. In this case the syntax is slightly modified. Let's consider an example:

219

Pointers

Page 221: C++ Basics to Advanced

int main ( ) {

int size,i; cout<<"Enter the size of the array : "; cin>>size; int *marks = new int[size]; cout<<"\nEnter the marks: "; for (i = 0; i<size; i ++) { cin>>marks[i]; } cout<<endl<<"The marks you entered are : "; for (i = 0; i<size; i ++) { cout<<endl<<marks[i]; } delete [ ] marks; return 0;

}

The output would be:Enter the size of the array : 3 Enter the marks: 50 64 53 The marks you entered are : 50 64 53

marks is an array of integer type whose size is determined by the user. If the user types 3, then the size of marks array is 3. The array has been allocated using new operator. We get the input of the array using a ‘for’ loop and then we display the entered values using another for loop.

After having used the array we free up the memory space using delete. Since we are freeing up space used by an array, we have to free up space used by each element of the array. The syntax to free up the space is:

delete [ ] name-of-array;

The point is that you have to use the square brackets when dealing with arrays (so that the compiler knows that you are referring to an array).

Can we go on taking up memory from the heap? No, the heap is a finite memory space. There is a chance of the heap getting completely exhausted. When you write programs using ‘new’ operator it is always advisable to give a provision to check whether the heap is exhausted or not. If the heap is exhausted, an exception will be returned. This will be dealt in the "Exception handling" section later.

220

Pointers

Page 222: C++ Basics to Advanced

More Pointers - IV

The following topics are covered in this section:

• Pointers to Functions • Some pointer declarations

Pointers to Functions

You can even create pointers that point to functions. The primary use of such pointers is to pass functions as arguments to another function. You have to be very careful while declaring pointers to functions. Take note of the position of the parentheses:

return-data (*pointer-name) (arguments);

We’ll write a simple program to add two numbers. For this purpose we shall define a function called sum( ). This function sum ( ), will be passed to another function called func( ) which will simply call the sum ( ) function.

#include <iostream.h> int sum(int a, int b) { return (a+b); }

void func(int d, int e, int (*p1)(int,int)) { cout<<"The result is : "<<(*p1)(d,e); }

int main( ) { int (*p)(int,int); p=sum; func(5,6,p); return 0; }

The output is:

The result is : 11

Though the program is simple a few statements might appear confusing. The statement

int (*p) (int,int);

221

Pointers

Page 223: C++ Basics to Advanced

declares a pointer to a function that has a return value of integer and takes two arguments of type integer.

sum( ) is a function with return data type of integer and also with two arguments of type integer. Hence the pointer ‘p’ can point to the function sum ( ). Thus we assign the address of the function sum ( ) to ‘p’:

p = sum;

We’ve also defined another function called as ‘func ( )’ which takes two integer arguments and a third argument which is a pointer to a function. The idea is to pass the function sum ( ) to the function ‘func( )’ and call the sum ( ) function from func ( ).

func(5,6,p);

will call the func ( ) function and it will pass pointer ‘p’ to func ( ). Remember that p is a pointer to sum ( ). Hence in reality we are actually passing a function as argument to another function.

Let’s expand the program one step further by creating another function called product( ) which will return the product of the two arguments.

#include <iostream.h> int sum(int a, int b) { return (a+b); }

int product(int x,int y) { return (x*y); }

void func(int d, int e, int (*p1)(int,int)) { cout<<endl<<"The result is : "<<(*p1)(d,e); }

int main( ) { int (*p)(int,int); p=sum; func(5,6,p); p=product; func(5,6,p); return 0; }

222

Pointers

Page 224: C++ Basics to Advanced

The output is:

The result is : 11

The result is : 30

The program is very similar to the previous one. I just wanted to illustrate that you can pass any function to func ( ) as long as it has a return type of integer and it has two integer arguments.

Practical use: You might be wondering why we’d need to pass functions as arguments to other functions. There are instances when you might create generic functions, which the user can tailor to suit his/her needs. For example: Signal handling functions usually contain a function as one of their arguments. These functions are used in a program to react to certain external events- for example if the user presses CONTROL+C keys, the default action is for the program to terminate. But you might write a program in which you want to print “YOU CAN’T TERMINATE THIS PROGRAM” every time the user presses CONTROL+C. To perform this there are standard signal handling functions. These functions generally take the following 2 arguments:

1.) The signal for which you want your program to react (CONTROL+C is just one of the possible signals your program might receive).

2.) The function which has to be performed when your program receives that signal.

The function might be something like:

int sighandler (int sig, pointer-to-function);

All the user needs to do is decide what signal he wants to handle and then decide on the function he wants to perform in case that signal is received (there’s no need to rewrite the entire signal handling function for his application; which would be time consuming. Programming is all about reusing code rather than rewriting). Can you think of any other simpler way in which a generic function like sighandler can be created?

223

Pointers

Page 225: C++ Basics to Advanced

Pointer Declarations

Pointer declarations are a bit confusing. Read through the various declarations described below to clear your doubts:

Declaration What it means int *p; pointer to an integer void p(char *str); function that accepts a pointer to a character

int (*p)(char *str); pointer to a function that accepts pointer to character as argument and returns an integer.

int *p(char *str); p is a function that accepts pointer to character as argument and returns a pointer to integer.

int *p[5]; p is an array of pointers to integers int (*p)[5]; p is pointer to an integer array of 5 elements

void (*p)(char (*str)[] ); pointer to function that accepts pointer to character array

The list can be made more complicated but this should be sufficient to understand how pointers are generally declared. A couple of declarations are pretty interesting:

int *p[5]; - p is an array of pointers to integers

int (*p)[5]; - p is pointer to an integer array of 5 elements

The subscript operator has a higher precedence over * in the above declaration. So if we say:

int *p[5];

the compiler will create an array of pointers. To forcefully evaluate *p first, we use the parentheses and declare:

int (*p)[5];

Now the compiler considers ‘p’ as a pointer and so this means that ‘p’ is a pointer to an array of integers.

224

Pointers

Page 226: C++ Basics to Advanced

More Pointers - V

The following topics are covered in this section:

• Pointers to Structures • Multiple indirection • Pointers to constants • Constant Pointers • Void Pointers

Pointers to Structures

This is similar to creating pointers of other data types but you should know how to access structure elements through pointers.

struct phonebook { int pin; int tel; }; int main( ) {

phonebook record[2]; phonebook *p; p=record; //Points to record[0] p->pin=60004; p->tel=23451; p=p+1; //Now points to record[1] p->pin=50023; p->tel=89732; p=record; cout<<endl<<"The pincode is : "<<p->pin; cout<<endl<<"The tel. no. is : "<<p->tel; p=p+1; //Points to record[1] cout<<endl<<"\nThe pincode is : "<<p->pin; cout<<endl<<"The tel. no. is : "<<p->tel; return 0;

}

225

Pointers

Page 227: C++ Basics to Advanced

The output is:

The pincode is : 60004 The tel. no. is : 23451 The pincode is : 50023 The tel. no. is : 89732

You’ll notice that to access the individual elements we have made use of different operators. When you use pointers, you should not use the dot operator. Instead we make use of the arrow operator (->).

p->pin=60004;

Actually, the dot operator (or the member operator) can be used but you have to be careful about operator precedence. To use the member operator we’ll have to dereference the pointer and then use it. The following expression:

*p.pin

would be wrong. The dot operator is a post-fix operator and it has higher precedence over the dereferencing operator (which is a pre-fix operator). So to set it right we will have to use:

(*p).pin

Thus we could also have used the following code in our program:

cout<<endl<<"The pincode is : "<<(*p).pin; cout<<endl<<"The tel. no. is : "<<(*p).tel;

Usually programmers do not use this method because there is a chance of forgetting the parentheses (and the arrow operator is simpler to use).

Also take note of how pointers can be used with structure arrays. Note that

p = p+1;

causes ‘p’ to point to the next structure variable ( i.e. record[1] ).

226

Pointers

Page 228: C++ Basics to Advanced

Pointer to Pointer (Multiple Indirection)

In C++ you can create a pointer that will point to another pointer. In fact you can even create a pointer that points to a pointer that points to a pointer. But here we shall only deal with the case of a pointer that points to another pointer (the same concept can be extended to other cases).

The figure below should make the concept clear. Let ‘marks’ be an integer variable (let us assume that an integer occupies 4 bytes of memory).

int *p; int marks=80; p=&marks; int **p2; p2=&p;

Thus p2 is a pointer which points to a pointer of type integer (i.e. p2 points to ‘p’).

The pointer p2 can be used for two purposes:

• p2 can be used to refer to the address of ‘p’ • p2 can also be used to access the value of marks.

If you want to access the value of marks through p2 then you can type:

cout<<**p2; //result will be 80 (which is stored in ‘marks’)

227

Pointers

Page 229: C++ Basics to Advanced

Pointer to constants:

When a pointer points to a constant term, the pointer cannot modify the value of the constant term. The declaration of a pointer to a constant is as follows:

const data-type *pointer-name;

Example:

const int num=20; const int *ptr; ptr = &num;

• The address of a constant cannot be assigned to a pointer unless the pointer is declared as pointing to a constant. The following is incorrect:

const int num=20; int *ptr; ptr=&num; //Error (pointer not declared as pointing to a constant)

• A pointer to a constant cannot be assigned to a non-constant pointer.

const int num=20; const int *ptr; ptr=&num; int *ptr2; ptr2=ptr; //Error because ptr2 is a pointer to a non constant

• The code fragment below is correct:

const int num=20; const int *ptr; ptr=&num; const int *ptr3; ptr3=ptr; //Correct because ptr3 is a pointer to a constant

Thus you can use pointers to constants as function parameters if you do not want the function to modify the argument.

The following is fine:

const int num=20; const int *ptr; ptr = &num; ptr++; //no problem

The pointer ‘ptr’ itself is not a constant (which means that we can change the value held in ptr at any time).

228

Pointers

Page 230: C++ Basics to Advanced

Constant Pointers:

A pointer, which is declared to be a constant, cannot be changed after initialization. Thus it will always retain the same value. Syntax for declaration:

data-type *const pointer-name = address;

The constant pointer has to be initialized and this value will be retained throughout the program. For example:

int num = 20; int *const ptr = &num; //constant pointer initialized to address of num.

You cannot try to increment the pointer:

ptr++;

because ptr is a constant pointer and it’s value cannot be changed. You’ll get a compiler error saying “L-value is a constant”. But:

cout<<*(ptr+1);

is correct. Why? In this case we are only displaying the value stored at the next memory location; we are not trying to change the value held in ‘ptr’ (of course, this will produce some garbage value).

The following code fragment is incorrect:

int num=20; int *const ptr; //has to be initialized here itself ptr=&num;

Void Pointers

If a pointer is declared as pointing to void then it can point to any data type. The syntax for declaring a void pointer is:

void *pointer-name;

For example:

int num=20; double db=1; void *ptr; ptr = &num; ptr = &db;

229

Pointers

Page 231: C++ Basics to Advanced

In the above code fragment the void pointer ‘ptr’ is first assigned the address of an integer and then assigned the address of a double quantity. Though you can assign any data type you cannot attempt to access the value using the dereferencing operator. The following code will produce errors:

int num=20; double db=1; void *ptr; ptr = &num; ptr = &db; cout<<*ptr; //Error – cannot use * to access value stored.

Also you cannot try to assign a void pointer to a pointer of some other data type. Pointer arithmetic is also not allowed in void pointers. If a function can operate on any kind of data type then the void pointer can be used (for example: the operator ‘new’ when overloaded will have a void pointer as its argument because this operator can work with any data type).

230

Pointers

Page 232: C++ Basics to Advanced

More Pointers - VI

The following topics are covered in this section:

• Pointers to characters • Pointers Demystified (2-D arrays and pointers)

Pointer to Characters:

Check out the following code:

int main( ) { char *name = "Tintin"; cout<<name; return 0; }

The output will be Tintin.

char *name = "Tintin";

will create a null terminated string (i.e. an array of constant characters) and the address of the 1st character will be stored in the pointer.

cout<<name;

This will print all the characters starting from the address held by pointer ‘name’ till it encounters the null character. A pointer to a character is treated as a null terminated string. The following will also work:

char name[]="Tintin"; cout<<name;

In this case also ‘name’ will contain the address of the first character and when we display it, the entire string will be displayed.

Note: When you print a character array it will print till a null character is encountered.

The following will create problems:

char name[]={'T','i','n','t','i','n'};

cout<<name;

231

Pointers

Page 233: C++ Basics to Advanced

When individual characters are assigned to a character array, it is the responsibility of the programmer to include the null character. In the above example we haven’t assigned a null character and thus the resultant output will contain garbage values:

Tintin¦¦8_e

Strings are constants and you cannot modify them after initializing them. Thus the following code will produce an error:

char *name = "Tintin";

*name="h"; //COMPILER error

The reason is because the right hand side is a char[2] (the letter h and the null character). In other words you cannot assign a string to a character (*name can only hold a character since it is a pointer to a character data type).

So, we could try the following:

char *name = "Tintin";

*name=’h’; //Run Time error

Now the compiler is satisfied with what you’ve done. Why? Because ‘name’ points to a character and you’ve asked the compiler to store a character at that memory location. The two types match and the compiler gives the green flag.

But when you execute the program, it will crash because of a run-time error. Why? Because a string in memory is a constant and you are not supposed to change the value. To prevent such bugs from creeping into your code, it is a good idea to use the keyword const:

const char *name = "Tintin";

*name='x'; //Compiler error

Now you’ll get a compiler error because the compiler has been informed that ‘name’ is pointing to a constant and thus it shouldn’t be able to modify the data it holds.

232

Pointers

Page 234: C++ Basics to Advanced

Pointers Demystified:

Before getting into pointers lets refresh our memory on a few important concepts:

• Variables are actually named memory locations.

• When we ask for a variable value, the program has to access the memory address and retrieve the value stored there.

• The same concept applies to arrays as well.

• Arrays are stored contiguously (i.e. in consecutive memory locations).

Now, let’s go a bit deeper into pointers now that we are familiar with the basic concepts. We’ll again take up our discussion on pointers and arrays. Consider the following code snippet:

short int marks[5]; //some assignments cout<<marks[2];

The computer always works in terms of memory addresses. So when we say marks[2], the program has to calculate the actual address of marks[2]. This can be done by using a simple formula. The program does the following:

Address of marks[2] = Base address of the array + offset

where offset will be the number of bytes to be added depending on the size of the data type and the element to be accessed. In a equation format it would be:

offset = 2*sizeof(short int)

Base address of the array = address of marks[0]

Instead of using

marks[2]

we can also use

*(marks + 2)

since marks actually contains the address of the first element and adding 2 will take us to marks[2] (pointer addition).

Let’s consider two cases.

233

Pointers

Page 235: C++ Basics to Advanced

Case I Sometimes, when you are dealing with arrays it might be a good idea to use the pointers to access elements rather than using array indexing. The pointer method might improve the performance. Let’s take a simple example:

int main( ) { short int marks[5]={80,70,60,75,90}; int i; short int *ptr; ptr=marks;

cout<<endl<<"Using indexing: "; for (i=0;i<5;i++) { cout<<"\t"<<marks[i]; }

cout<<endl<<"Using pointers: "; for (i=0;i<5;i++)

{ cout<<"\t"<<*(ptr++); } return 0; }

The output will be:

Using indexing: 80 70 60 75 90

Using pointers: 80 70 60 75 90

Rather than the output we need to focus on the difference between the 2 methods used. In the first method we used array indexes to display the value of each element:

for (i=0;i<5;i++) { cout<<"\t"<<marks[i]; }

Our program, for each value of ‘i’, is forced to calculate the offset of the element from the base address. In other words, each time the program has to calculate:

&marks[0] + i*sizeof(short int)

234

Pointers

Page 236: C++ Basics to Advanced

In this way the program calculates the address and retrieves the value stored at that location. You might wonder what’s the problem in this? Let’s take a look at the second method, using pointers:

short int *ptr; ptr=marks; for (i=0;i<5;i++) { cout<<"\t"<<*(ptr++); }

‘ptr’ is a pointer which initially holds the address of the first element of the array marks. The first time the program enters the for loop, it will display the value of the first element of the array. Then ‘ptr’ is incremented. Incrementing ‘ptr’ is equivalent to:

ptr = ptr + sizeof(short int)

Each time the program executes the loop it has to move the pointer to the next element using the above equation. The difference in array indexing and pointer referencing lies in the 2 equations. In the array indexing method the program has to perform a multiplication whereas in the case of pointers this is not required. By using the second method we can improve performance (speed of execution) because multiplication needn’t be performed (and multiplication is generally a time consuming operation for computers). In small programs you may not notice much of a difference but when you are dealing with larger data types this could cause a significant improvement.

Case II

But this doesn’t mean that using pointers instead of array indexing will always improve performance. It all depends on the situation (just try to think of the problem from the compiler’s point of view). For example, let’s say we have an array:

int salary[10]; int id;

Later in some part of the code we have the statement:

cout<<”Enter the employee ID:”; cin>>id; cout<<”Salary of that employee is:”<<salary[id];

In this case even if you try to use a pointer to access this element you won’t be able to prevent the compiler from multiplication. In the first case we were able to bypass the multiplication step because it was a loop and each time we didn’t need to perform multiplication. In the second case you wouldn’t be able to do so.

235

Pointers

Page 237: C++ Basics to Advanced

The following:

marks[2];

and

*(marks+2);

are actually the same. When we say marks[2] the compiler would internally convert it into *(marks + 2). The following code snippet should clarify your doubts:

int weights[4]={10,20,30,40}; int *ptr=&weights[0]; cout<<endl<<weights[2]; cout<<endl<<*(weights+2); cout<<endl<<*(ptr+2); cout<<endl<<ptr[2]; cout<<endl<<*(2+ptr); cout<<endl<<2[ptr];

All the statements above will yield the same result: 30. The notation:

2[ptr]

might seem absurd but it proves the point that the compiler doesn’t differentiate between ptr[2] and *(ptr + 2) or *(2 + ptr).

Two-dimensional arrays and pointers:

Let us say that we’ve declared a 2-D array:

int marks[4][2];

Though we feel that this array is similar to a tabular structure, we don’t have tables in memory. Array elements are stored contiguously in memory (irrespective of whether it is a one dimension or multi-dimensional array). We refer to the 2 dimensions as rows and columns (the first square bracket denotes the row and the second denotes the column number) but as far as the computer is concerned, all the elements are just stored continuously in memory. So, how would the array marks[][] be stored in memory?

236

Pointers

Page 238: C++ Basics to Advanced

Thus, the first row elements are stored first, followed by the second row elements and so on. When we refer to an element as:

marks[3][1]

the program has to calculate the address of the element to retrieve the value.

The address of marks[3][1] = base address of the 2-D array + offset

This is similar to what we saw for 1-D arrays.

Base address of the 2-D array = address of marks[0][0]

and

offset = (number of columns * element’s row number) + element’s col. number

In our case:

offset (in terms of the number of elements) = (2 * 3) + 1

To obtain the offset in terms of bytes, just multiply the above value by sizeof(short int).

The concept might seem confusing at first but once you substitute some values you should be able to grasp the idea.

So, how do we refer to 2-D array elements using pointers?

A 2-D array is a pointer to an array of 1-D arrays. If we declared an array as:

marks [4][5]

then when we say mark[0] we are referring to the first row.

mark[1] will point to the second row and so on. Let’s take this one at a time. A 2-D array is a pointer to an array of 1-D arrays. In our example, each row of the array contains 5 elements. These 5 elements form the set of 1-D arrays. Thus each 1-D array will make up a row of our original array. If we use:

marks[0]

237

Pointers

Page 239: C++ Basics to Advanced

it is equivalent to

marks

and it contains a set of elements (the elements are the individual rows: marks[0], marks[1] etc.). marks[0] is an array with elements marks[0][0], marks[0][1], marks[0][2]…marks[0][4].

We finally arrive to the conclusion that a 2-D array is a pointer to an array of 1-D arrays.

For a normal 1-D array,

marks [2] = *(marks + 2)

To refer to an element in a 2-D array, say:

marks [2][3]

we can use the notation:

*(marks[2] + 3)

This tells the program to take the address of row2 and add 3 elements to it. But we’ve seen that marks[2] = *(marks +2). Thus:

marks[2][3] = *( *(marks + 2) + 3 )

The double asterisk confirms what we stated at the beginning. It denotes that our 2-D array is in fact a pointer to a pointer.

Try out the following code snippet:

int marks[4][2]={60,75, 80,65, 90,95, 87,76};

cout<<endl<<marks[3][1]; cout<<endl<<*(marks[3] + 1); cout<<endl<<*(*(marks + 3) + 1); cout<<endl<<*(*(marks) + (2*3) + 1);

All statements will yield the same result: 76.

238

Pointers

Page 240: C++ Basics to Advanced

Core Concepts of OOP

Object Oriented Programming (OOP)

Introduction:

Introduction:

Structured programming can be roughly divided into two categories:

Procedural Object Oriented

Structured programming is basically dividing a program into smaller modules that are easier to manage. All of

what we have covered up to this point is part of procedural programming. In this the programmer divides the

program into smaller functions (rather than putting the entire code within one function). Each function would

have its own variables and sharing of variables between various functions is also possible (using global

variables).

What we will see henceforth is related to object oriented programming (OOP). Large programs were difficult to

handle in procedural programming and OOP was developed to reduce complexity of software development. In

C++, OOP is implemented using classes. The main concepts in OOP are:

Data Abstraction Data encapsulation (hiding) Polymorphism Inheritance

Data Abstraction:

The fundamental data types (like int, float, double, char etc.) might suffice for simple programs. It is difficult for

a programmer to always think in terms of the fundamental data types alone. For example, what data type would

you use if you want to model a car in a computer application? A car has many properties and each one will be of

a different data type (the speed would be an integer, the colour would be a string and so on). We could use a

structure to solve our problem but structures also have their limitations. When you create a structure, the user

(another programmer who might use your structure or you yourself) can directly manipulate with the member

data of the structure. Let’s say that we have created a structure called ‘car’ which has 2 data types for speed and

color. You can create an instance of the structure car (say ferrari) and then assign the value for ‘speed’ as 99999.

Or you could also set the colour as ‘xyzer’. This would lead to incorrect data and you wouldn’t want the user to

239

Classes & OOP

Page 241: C++ Basics to Advanced

enter such values. Thus a structure wouldn’t permit us to control the way data is stored or accessed. A user can

simply modify anything by directly accessing it. Wouldn’t it be more convenient if we could have some control

over our data type?

Whenever you create such a complex data type, there will also be some operations that you would want to

perform on that data type. In C++, these operations (which are implemented through functions) and the data can

be bound together as a class. A C++ class is a data type that the programmer defines. Data abstraction refers to

the capability of a programmer to create new, user-defined data types. Classes are also called abstract data types

(ADTs or user-defined data types) because they are created using the process of abstraction. So, what is the

process of abstraction?

Let us consider a simple example of a person. Every person in this world has many attributes (like age, date of

birth, name, gender, marital status, salary, number of children, hobbies, strengths, weaknesses, diseases,

nationality etc.). If we are designing a patient database application for a hospital, we might not require storing

attributes like salary, strengths and weaknesses of a person. So while designing this application we will only

choose the particular features of the patient that we are interested in (like age, date of birth, nationality, disease

history, gender etc.).

Now if we have to design another system for a corporation, we will consider the person as an employee. An

employee database system wouldn’t need to store information like disease history, haemoglobin level etc. In this

case we would only be interested in date of birth, salary, qualification etc.

In both cases, our requirement is to store detail information about a person. In one case we have a patient and in

one case we have an employee. Depending on our application we selected what we wanted. Abstraction is the

process in which we selectively choose what is needed for our application discarding the unnecessary attributes

(based on the requirements). You’ll appreciate ADT as you progress through this chapter.

Data Encapsulation:

Data encapsulation/ data hiding is an important feature of object oriented programming. The mechanism of

hiding data is to put them in a class and make them private. The data is now hidden and safe from any accidental

manipulations, i.e. no function (from outside the class) can change the member data. Actually there are two

things you can hide: implementation of functions (to the user it doesn’t matter as to how you’ve implemented a

particular function) and data. In procedural programming it is possible to only hide the implementation details

but you cannot hide/ protect data. OOP lets you achieve this. Why do we need to really hide anything? Who are

we hiding the data from? These questions will be answered later in this chapter. A simple example is the case of

the ‘car’ we considered earlier. We don’t want the user to directly access ‘speed’ and modify it. By making

‘speed’ private, we prevent the user from doing this.

240

Classes & OOP

Page 242: C++ Basics to Advanced

Polymorphism:

Polymorphism means having many forms. Polymorphism can be seen frequently in the English language. There

are many English words, which have a different meaning depending on the context of use. The statements “close

a book”, “close the file”, “close the door” and “close the deal” all make use of the verb ‘to close’ but the

meaning of each statement depends on the context. Another example is the sentence, “I’ve cracked the exam”.

The meaning of cracked in this case is different from the crack used in a sentence like, “The pot cracked”. In

both sentences the word is the same but its interpretation varies depending on the context.

In the same way you can think of many programming examples. For instance, consider the + operator. When it is

used on numbers it will act as an addition operator, adding two numbers mathematically and giving the result.

When the + acts on two strings, the result will be the concatenation of the two strings (we’ll take a look at string

objects which permit us to use + later). For example:

“new” + “delhi” = “newdelhi”.

Thus, though the operator is the same (+), it can perform different actions depending on the type of the operands.

This is a simple example of polymorphism. A few other examples of polymorphism are:

The << and >> operators are bit-shifting operators and they are also used for displaying information on the screen or for storing values in a variable (the circumstance decides whether they are used to shift bits or as input and output operators). The division operator (/) when operating on two integers will produce an integer as the result of division. But if one of the operands are floating point numbers then the result will also be a floating-point number.

In all these cases, the same operator performs different functions depending on the situation. This is called

polymorphism.

Inheritance: Just as the name implies, inheritance refers to children inheriting property from their parents. In

C++, the parents are called the parent classes and the children are called the derived (or child) classes. The idea

of inheritance is to prevent classes from being redefined over and over again. If a programmer has already

created a class and you want to make use of the same class with some additional features, then you needn’t re-

write the entire class description again. Instead, you can derive a class from the original one (hence all the

existing features of the class will be available in your class also) and you can add the extra features you need to

your class. This is called re-usability of code. Instead of re-writing, we can re-use through the concept of

inheritance. Let’s take the example of animals: a lion belongs to the cat family; the cat family comes under the

mammals’ category and the mammals category will come under the general group called animals. Using

inheritance, if a lion is being described then only the unique features of a lion need to be defined (you needn’t

define the features of animals, mammals and cats). Thus the class ‘lion’ will be inherited from the class ‘cat’

which will in turn be inherited from the class ‘mammals’ which is inherited from ‘animals’.

241

Classes & OOP

Page 243: C++ Basics to Advanced

OOP Languages:

Object Oriented Programming did not originate in C++. In fact it was already existing and OOP was combined

with C programming to develop C++. A few of the OOP languages are:

Simula Modula SmallTalk Ada C++ Java

Some of these languages are said to be ‘pure OOP’ while others are ‘hybrid OOP’. ‘Pure OOP’ means that

everything in a program has to be tied with classes (and you cannot use separate functions). Java is an example

of pure OOP. C++ comes under hybrid OOP because you can use OOP as well as the normal C style coding

(involving separate functions and data).

A closer look into OOP:

The world can be considered to consist of many objects. Objects will have attributes and

behaviours. A water-heater is a simple example of an object. It has certain attributes or

properties (like colour, size, maximum and current temperatures etc.) and there are certain

behaviours associated with the water-heater (like switching on the heater, increasing the

temperature or heating for a specified time interval, switching off the heater etc.). These are

actions that can be performed on the heater. Or in other words they are actions which can

modify certain properties of the heater (for instance by switching on the heater the current

temperature of the heater will change).

A car is another example of an object. It has a lot of attributes such as fuel capacity, current

speed, top speed, number of wheels, type of gearbox etc. There are also a lot of operations

which you can perform on this object. For example: you can accelerate the car, apply brakes

etc. The attributes of a car will have some values at any given instance of time. Once the car is

in motion, you can say that at a particular time the speed of the car is 30 km/hr (thus current

speed will be 30km/hr). Similarly, the color of the car is red or the car has four wheels. The

values for the attributes at any given instant of time define the state of the object. There are

242

Classes & OOP

Page 244: C++ Basics to Advanced

two types of states an object can have: static and dynamic. Some attributes of the car will not

change over a period of time. The number of wheels in the car is always going to be four

(unless you are making a new prototype!). The colour of the car would also remain the same

for a long time. These attributes contribute to the static state of the car. The current speed of

the car is a dynamic property which will change frequently depending on the actions

performed upon the car. In OO terminology you will encounter the following terms

frequently:

State Behaviour Identity

Behaviour of an object refers to the set of operations (or actions) that can be performed on an

object.

Every object will have some attribute that can be used to uniquely identify the object. For

example let’s take the example of a car as an object. All cars have colour as an attribute. But

can you distinguish two cars based on their colours? Definitely not. But you can distinguish

two cars based on their registration number. Hence registration number is the attribute which

can be used to uniquely identify a car. If you take a banking example then the account number

is a unique way to identify an account (no two accounts can have the same account number).

An object will have two parts:

1. Interface 2. Implementation

In a car, the interface is the acceleration and braking actions which can be performed on the car (there are many

more but lets just limit ourselves to these two actions). The driver is going to be the user of the car. When the

driver presses the accelerator pedal, there are a lot of things that happen within the car which actually cause the

rpm (rotations per minute of the wheel) to increase. Is the driver concerned about what actually happens within

the engine? No. The driver just wants the car to accelerate on pressing the pedal and is least bothered about the

underlying mechanisms used by the manufacturers to achieve this. He doesn’t care about how the engine is

designed or as to how the piston is moving to achieve acceleration. All he knows (and wants to know generally)

is that the car should accelerate when he presses the pedal. These internal mechanisms are called implementation

details in OOP terminology. One of the central features of OOP is to separate the interface from the

implementation. The person using an object should not know/worry about the implementation. This is what is

termed encapsulation.

243

Classes & OOP

Page 245: C++ Basics to Advanced

Classes and Objects in C++:

In C++ classes are used implement OOP. A class will contain two types of members: member data and member

functions. The member functions can be used to operate on the member data within the class. The data members

correspond to the attributes while the member functions correspond to the behaviour. Instead of the term

‘function’, some programmers use the term ‘method’.

The term ‘class’ and ‘object’ might seem confusing at first. Basically you cannot directly use a class (we need to

create an instance of the class and we call this an object). In our fundamental data types we have int, double, char

etc. But are we using them directly? For example, do we say:

int = 5;

No. If we were to do this then we would never be able to create different integer variables. We create an instance

of an integer when we say:

int x = 5;

Since classes are also data types (user defined data types), they also follow the same principle. You have to

create instances of a class to do anything useful. An object is an instance of a class, i.e. only when you define an

object, will the compiler allocate memory for the object. Class is like a model (or a template) from which you

can create many objects of the same type. A template can be compared to the plan of a building. When the plan

is drawn, we have not yet allocated the area on land for construction. We only know about how the building

structure will be. But when construction work begins, the area will be allocated. Similarly, the compiler allocates

memory space for every object that is created. This is why a class is called an abstraction (in other words a class

is a generality while an object is a specific instance of the class). Let’s say we have a class called student, with

the attributes:

id name age

We can create two students by saying: student Watson, Hastings;

Now, Watson and Hastings are 2 students. Each of them will have an id, name and age (we can modify their

attributes separately). You will be able to distinguish between a class and an object clearly when we write a few

programs.

Everything in a class (data and functions) is private, protected or public. They are called access-specifiers

(because they decide how the class members can be accessed).

244

Classes & OOP

Page 246: C++ Basics to Advanced

private:

As the name suggests, whatever is in the private area of a class can only be accessed from within the class. If the data is made private then it can be accessed only through member functions of the class. If a function is made private then it can be called from within another member function. Data/function is made private by default (i.e. if you don’t mention any specifier).

protected:

The specifier ‘protected’ is used in inheritance and will be dealt with later.

public:

Public members of a class are accessible from outside the class. Hence when objects are

created, they can access the public members directly. Usually, data is made private while the

functions are made public (this is done to ensure data encapsulation). These public functions

can operate on the private data.

The syntax for a class is:

class name-of-class { private : data-type variable-name; //these are private public : functions; //these are public }; // End of class- make a note of the terminator.

It is not a must that data should be private and functions should be public. You can have private functions as well

public data.

Remember: No member of your class can be declared as ‘register’ or ‘extern’.

Note: Before getting into classes, you should know that there are two types of programmers who work with classes: the class designer and the class user. The designer (or creator) creates a class and the user makes use of the class in his/her programs.

The term ‘user’ usually represents the person who will use an application developed by a programmer. But if the term ‘user’ is used while explaining classes, then it refers to a ‘class user’ (a class user is another programmer).

In our example codes, we will be the designers as well as the users of the class (of course if you provide the code to someone else, then we will be considered the designers while they will be the users). A class creator will try to hide from the user whatever is not required by the user (why provide more options to the user and lead to complications!). Hide whatever is possible so that the class user cannot tamper with things that they are not supposed to use.

245

Classes & OOP

Page 247: C++ Basics to Advanced

Demonstration of a Class

A program to demonstrate Classes

Before getting into the nuances of classes let’s take a look at a few simple examples to illustrate classes and

objects.

Let’s say that we want to create a timer, something like a stopwatch. We should be able to set the timer to a start value, pause it, stop it or start it. Thinking in terms of OOP we would have one member data:

• count (let’s keep it as an integer)

Being the first example, we’ll implement some simple functions:

• initialize( ) • display( ) • increment( )

The function names are self-explanatory and the functions would operate on the member data (i.e. someone who

uses our timer shouldn’t be able to change the value of count directly. If this were allowed then the user might

set count to a negative value or might misuse it). The user of our timer object should be able to access our timer

in a controlled manner.

#include <iostream.h> class Timer { private: int count;

public:

void initialize( ) { cout<<"timer!"; count=0; }

void display( ) { cout<<"remaining:"<<count; }

void increment( ) { count=count+100; }

};

246

Classes & OOP

Page 248: C++ Basics to Advanced

int main( ) { Timer t1,t2; t1.display( ); t1.initialize( ); t1.display( ); t1.increment( ); t1.display( ); t2.initialize( ); t2.increment( ); t2.increment( ); t2.display( ); return 0; }

When you run the program you may get an output similar to this:

Seconds remaining:4289044 Resetting timer! Seconds remaining:0 Seconds remaining:100 Resetting timer! Seconds remaining:200

Since this is our first program in classes, we’ll dissect it line-by-line.

Dissection of the program Explanation for each part

class Timer {

Begin the declaration of our class called ‘Timer’.

private:

int count;

As part of data hiding, we’ve put the member data

(count) within the private section of the class. Only

the public functions of the class can access ‘count’.

public:

void reset( )

{

cout<<"timer!";

count=0;

}

The keyword public indicates that everything

following this label is part of the public section of the

class.

void initialize( ) { cout<<"timer!"; count=0; }

initialize( ) is a member function of class Timer

which will be used for resetting the value of count to

zero. Since it is present in the public section, anyone

using our class can call this function (and since this

function is part of the class, it is permitted to access

the member data ‘count’).

247

Classes & OOP

Page 249: C++ Basics to Advanced

void display( ) { cout<<"remaining:"<<count; }

void increment( ) { count=count+100; }

We’ve defined 2 more functions display ( ) and

increment ( ) which also access the member data

‘count’.

}; Signals the end of our class declaration.

int main( )

{

Timer t1,t2;

Next comes the main( ) function in which we’ve

created two objects. ‘t1’ is an object or an instance of

the class Timer. ‘t2’ is another object (i.e. we now

have two timer objects in our program). Every

instance of a class will have its own copy of the

member data. This is similar to how we used to

create variables from the fundamental data types (ex:

int x, y; creates two integer variables).

t1.display( );

All public members of the class can be called using

the dot operator. Thus we call the member function

display using the dot operator. This would display the

value of the t1’s variable ‘count’. Since it hasn’t been

initialized, you’ll find a garbage value on your

screen.

t1.initialize( );

t1.display( ); Set member data count to 0.

t1.increment( );

t1.display( );

We’ve incremented the value of count from 0 by 100.

Thus the last display would yield: Seconds

remaining:100

t2.initialize( );

t2.increment( );

t2.increment( );

t2.display( );

These will all act on the ‘count’ of object t2. Thus at

the end of our program: count (t1) will be 100 count

(t2) will be 200

return 0;

} End of the program.

Remember: When we call member functions using the object t1 we are only manipulating the ‘count’ of t1. The

object t2 remains unaffected.

248

Classes & OOP

Page 250: C++ Basics to Advanced

Remember: Only functions belonging to the class can access the private members of the class.

This example should have clarified your doubts about a class and an object. We create objects from a class. The

class acts like a general framework from which we can create many objects. Each object will have the same

member functions and data but the value for the data can be different.

If we apply what we learnt about OOP earlier we can state this in another way: Each object that we create will

have its own state. In real life this corresponds to having two timers which are identical in functionality but

independent of each other (each one can have a different time).

Try adding the following statement to the code above:

cout<<t2.count; //ERROR

The compiler would complain saying: cannot access private member declared in class 'Timer'

Let’s modify our class by adding a public member data:

class Timer { private: int count;

public: int dummy;

void initialize( ) { cout<<"timer!"; count=0; }

};

int main( ) { Timer t1,t2; t1.dummy=90; cout<<t1.dummy; return 0; }

In this case, ‘dummy’ is a public member data of the class Timer. Thus an object of class Timer can directly access dummy (which means you can use the dot operator on dummy). But if you try:

cout<<dummy;

the compiler would complain saying that the identifier dummy is undeclared. ‘dummy’ can only be accessed through a Timer object.

249

Classes & OOP

Page 251: C++ Basics to Advanced

Note:

You generally won’t find programs with a function like display ( ) which we’ve used above. Instead of this we

would generally define a function through which the user (i.e. the programmer who would later use our class)

can retrieve the value of the member data. So, instead of the function:

void display( )

{

cout<<"remaining:"<<count;

}

we can define a function:

int get_count( )

{

return count;

}

Now the user can code something like this:

int main( )

{

Timer t1;

cout<<”The time remaining is:”<<t1.get_count( );

return 0;

}

Similarly we could also provide another function called set_count using which the user can change the value of the count.

void set_count(int x) { count = x; }

This can be used instead of the increment( ) function we implemented earlier. But there is a problem in the above function. Can you spot it?

The user of our class, can code: t1.set_count(-200);

and the compiler would faithfully obey (it’s like creating a digital clock which can be accidentally/ intentionally

set to negative time!). To prevent this then we have to test the value of the argument passed within the member

function set_count( ).

250

Classes & OOP

Page 252: C++ Basics to Advanced

Remember: In real application programming, a class designer would avoid using I/O within member functions

(i.e. it is preferable to avoid using cout or cin within member functions). This task is left to the user of the class

(of course the class designer should provide some member functions through which the user can set member

data). In this book you may find cout statements within member functions; these are primarily used to illustrate

concepts and how C++ works.

Some important points about classes:

You cannot access private members of a class directly.

Private members can be accessed only through the public member functions.

An object in the program can directly access only the public members through the dot operator (the dot

operator is also called the member operator). Private members cannot be accessed directly by an object.

Whenever we call a member function, we actually say that ‘we are passing a message to the object’. This is

another term frequently used in OOP.

Can a function be private?

Yes. Nothing prevents you from doing so. Once you declare a function as private, it cannot be accessed using the

dot operator. You can only call the private function from within a public function. But you might be wondering

why would need to create a private function? This topic will be dealt with an example after learning about

constructors and destructors.

Remember: By default, all data and functions in a class are made private. To make something public you have

to explicitly specify the access specifier in your class declaration. It is a good practice to explicitly state which

members are private and public in your class (as done in the above program).

251

Classes & OOP

Page 253: C++ Basics to Advanced

Constructors

In our first example in creating a class Timer, we used a member function called initialize( ). This was used to set the starting value of count to some legal value.

void initialize( ) { cout<<"timer!"; count=0; }

Basically, our idea was that whenever someone creates a Timer object the starting value of count shouldn’t have a garbage value. So we defined a member function which would assign values to our member data. Anyone who uses our class is expected to call the initialize( ) function first before using the Timer object. But there is a problem with this; when a user creates a Timer object he/she may forget to call the initialize function. This would create a lot of problems in the code later. Wouldn’t it be wonderful if we could initialize the member data of an object just like we initialize variables of fundamental data types?

Remember: Initialization and assignment are different. Initialization is done at the time of defining the variable. This is discussed at the end of this section.

Variables of fundamental data types can be initialized (at the time of declaration). For example:

int variable = 5;

This line declares ‘variable’ to be an integer and assigns the value of 5 to ‘variable’. Similarly, how about initializing an object? Constructors serve this purpose. When an instance of a class (or an object) is created, a special member function for that class called a ‘constructor’ is invoked. You can declare your own constructor but if you don’t then the compiler will provide a ‘default constructor’ for the class.

A constructor is a member function with same name as the class. It is usually used for the initialization of an object and it is executed at the time of creation of the object. Constructors cannot return any data.

Basically, objects are created to model real life entities and when an object is created, you will want some particular data (or properties) of the object to be initialized. The idea is to prevent an object from having an uncertain or undetermined state at the time of its creation. So, all initializations of member data are done using the constructor.

A constructor is just like a function except that you won’t have a return data type and also the name of the constructor should be the same as the name of the class.

General syntax for a constructor is:

constructor-name { //body of constructor }

Another form that can be used is:

constructor-name ( ) : member-data (value) { }

Let's take a look at a simple program to illustrate the use of constructors.

252

Classes & OOP

Page 254: C++ Basics to Advanced

class counter { private : int count;

public : counter ( ) // Constructor sets member data count to zero. { count=0; }

//you can define more functions };

int main ( ) { counter c1; return 0; }

When the compiler reads the line

counter c1;

it goes to the public part of the class and sees the constructor used. Since

counter c1;

has no arguments the compiler makes use of the constructor without parameters. Hence the count of c1 is initialized to zero. This is what we wanted to do: Create an object and at the time of creation set the member data to a particular value. Instead of zero you can even give a value. For example:

counter ( ) { count=7; }

If you use the above constructor, then the value of the member data count is initialized to 7. The alternate form for this constructor is:

counter ( ) : count (7) { }

So, what’s the difference in the two forms? This form makes use of an initializer list (i.e. count(7) forms the initializer list over here) to initialize the values while the constructor body is empty. In the other method we initialize the members within the constructor body.

Remember: In the strictest sense, the first form:

counter ( ) { count=7; }

isn’t really an initialization (it is only an assignment). We’ll discuss this later.

253

Classes & OOP

Page 255: C++ Basics to Advanced

Beware: When using an initialization list the order in which the members are initialized depends on the order in which you declare them within the class. The first member in the class will be the first member to be initialized. If your initializations depends on the sequence then be careful while using initializer lists.

In the case of assigning values within the constructor body, the order only depends on the order in which you write the statements within the body (and not on the declaration order).

This is an apt juncture at which we can discuss about initialization and assignment. Before going into classes, let’s consider our primitive (or fundamental) data types.

int amount;

amount = 92;

In this case we have declared (and defined) a variable called ‘amount’. In the second statement, we assign a value of 92 to this variable. Now take a look at the following:

int amount = 92;

In this case we have initialized the variable ‘amount’ with a value of 92. Initialization is done at the time of declaration.

You might recall that:

int amount=92;

and

int amount(92);

are equivalent.

Now let’s come back to our discussion of constructors.

counter ( ) { count=7; }

We are actually assigning a value to the member data count. This isn’t an initialization (the member ‘count’ was first defined and then later we are assigning a value of 7 to the member). But when you use an initializer list:

counter ( ) : count (7) { }

the member data ‘count’ is initialized with a value of 7. Generally, using an initializer list is more efficient than assignment but in the case of simple member data types this may not really matter. We’ll take a look at this topic again later.

If we do not provide a constructor, the compiler will provide its own default constructor (but this default constructor will not do anything useful - i.e. it will not initialize our member data). The compiler will not provide its default constructor if we provide our own constructor. A constructor with no parameters is also called a default constructor.

254

Classes & OOP

Page 256: C++ Basics to Advanced

Constructors with Parameters (Parameterized Constructors):

Constructors are like functions and they can also take parameters just like other functions. An example is shown below:

class counter { private : int count;

public :

counter (int x) : count (x) { }

// Some other functions in the class }; int main ( ) { int y; counter c1(2); cout<< "What value of count do you want initialise it to?"; cin>>y; counter c2(y);

return 0; }

The statement:

counter c1(2);

means that the constructor with one parameter is invoked. The argument passed to the parameter is 2. This value is now assigned to the ‘count’ of object ‘c1’.

count (x)

is equal to saying

count = x.

Hence ‘count’ of object c1 is two. You could also write the constructor as:

counter (int x) { count=x; }

Similarly, counter c2 (y);

means that ‘count’ of c2 will be initialized to the value of y (i.e. the value the user types in). You could also perform some validations within the constructor to ensure that the user doesn’t initialize the member data to an invalid value.

Remember: Constructors cannot return values.

255

Classes & OOP

Page 257: C++ Basics to Advanced

Overloaded Constructors:

Constructors being similar to functions can also be overloaded. To overload a constructor the parameter type or the number of parameters in the constructors should be different. It is possible that in our program we might want to have one constructor with no arguments and one parameterized constructor to initialize the member data to some other values. This is illustrated below:

class rect { private: int length, breadth;

public: rect( ) //constructor with no parameter { length=breadth=0; }

rect(int x, int y) //constructor with parameters { length=x; breadth=y; }

};

int main( ) { rect a; //executes ‘no parameter constructor’ rect b(1,2); //invokes the parameterized constructor return 0; }

The above program will create an object ‘a’ whose length and breadth will be initialized to 0. The object ‘b’ will have its length and breadth initialized to 1 and 2 respectively. Another way to write the above program would be as follows:

class rect { private: int length, breadth;

public: rect(int x=0, int y=0) //if no argument is passed, x=0 and y=0. { length=x; breadth=y; } };

int main( ) { rect a; rect b(1,2); return 0; }

256

Classes & OOP

Page 258: C++ Basics to Advanced

The result will be the same as earlier. In this program we do not explicitly specify the default constructor. We make use of a single constructor to deal with both situations. If the program creates an object without any arguments then the constructor function will be executed with the default parameter values (specified to be 0 in this case). If an argument is passed to the constructor, then the parameter will be assigned the values of the argument.

Default Constructors and Arrays of Objects:

Just like creating an array of integer data type, we can also create an array of objects belonging to a particular class. When you create an array of objects the class should have a no argument constructor (in other words the class should have a default constructor).

Consider the following program:

# include <iostream.h>

class rect { private: int length, breadth;

public: rect(int x, int y) { length=x; breadth=y; } };

int main( ) { rect ob[3]; //ERROR return 0; }

In the above program we have a parameterized constructor but no default constructor. Thus the statement:

rect ob[3];

will cause a compile time error. The reason why this does not work is because when you create an array of objects there is no way in which you can pass arguments to the parameterized constructor. The compiler will not know what to pass for each of the objects. Thus if you want to create an array of objects then the class should have a default constructor.

The program can be corrected as shown below:

class rect { private: int length, breadth; };

257

Classes & OOP

Page 259: C++ Basics to Advanced

int main( ) { rect ob[3]; //can be compiled without error return 0; }

In the above program there is a default constructor provided by the compiler itself. But usually it is better to provide our own default constructor (so that we can initialize the data) as below:

class rect { private: int length, breadth;

public: rect( ) //default constructor { length=0; breadth=0; }

};

int main( )

{ rect ob[3]; //can be compiled return 0; }

Let’s recall the main points about default constructors:

If no constructor is provided, the compiler provides a default constructor (but this constructor does nothing).

If any constructor (even if only a parameterized constructor) is provided, the compiler will not provide a default constructor.

A constructor with no parameters is called a default constructor.

A constructor with default arguments is also a default constructor. For example: rect(int x=0, int y=0) //if no argument is passed, x=0 and y=0.

{ length=x; breadth=y; }

This is a default constructor.

258

Classes & OOP

Page 260: C++ Basics to Advanced

Classes continued The following topics are covered in this section:

• Scope Resolution Operator • Destructor • Dynamically creating objects • Objects in Functions (returning and passing them) • Initializing an object using an existing object

Scope Resolution Operator (::) In the earlier section on classes, we have defined the member functions within the class itself. Hence there was no need to declare the function. But is it possible to define the member function of a class outside the class? We have already discussed inline functions. Even in classes you can explicitly declare functions to be inline using the ‘inline’ keyword. Remember: When you declare and define a function within the class it is made an inline function. If you define a function outside a class (using scope resolution operator), it will be treated like a normal function (not inline). Usually when using classes, any function that is just one or two lines long will be defined within the class itself. Longer functions are defined outside the class. Member functions can be defined outside the class by using the scope resolution operator. Using this we can declare a function within the class and define it outside the class. Let’s take a look at the syntax:

return-data-type name-of-class-to-which-it-belongs :: function-name (parameters) Consider the example below:

class Timer { private: int count; public: void display( ); //declared inside the class void increment( ); Timer( ); }; //Function defined outside the class using scope resolution operator void Timer::display( ) { cout<<"remaining:"<<count; }

void Timer::increment( ) { count=count+100; }

259

Classes & OOP

Page 261: C++ Basics to Advanced

Timer::Timer( ) { cout<<"timer!"; count=0; } int main( ) { Timer t1; t1.display( ); t1.increment( ); t1.display( ); return 0; }

Everything in the above program is same except that we have defined all the member functions outside the class. For this purpose we use the scope resolution operator to tell the compiler to which class the function belongs to. The statement

void Timer::display( ) tells the compiler that the function ‘display’ belongs to the class ‘Timer’ and has no return data type (void). Similarly we have used the scope resolution operator to define the function increment( ). In this example we’ve also defined the constructor outside the class:

Timer::Timer( ) { cout<<"timer!"; count=0; }

Since a constructor cannot return any value we don’t specify any return data type (not even void should be used as return data type). Remember: Though the functions are defined outside the class, they still belong to the class. The scope resolution operator is used to say that this function belongs to this class. Programmers will usually define functions outside the class because they want to separate the implementation from the interface. When a programmer creates a class and supplies it for others to use he may not give away the implementation details to the public. Instead he’ll only provide the users with the interface and declaring functions outside a class aids a programmer in achieving this. We’ll discuss how this can be achieved later. Destructor: When an object is created the constructor is invoked (or called). What happens when an object is no longer needed? The complement of the constructor is called. As the name implies, the destructor will destroy the object that was created. For a constructor we use the same name as the class name and for a destructor also we will use the same class name. But to differentiate between the constructor and destructor, C++ makes use of the tilde (~) symbol for the destructor.

class class-name {

class-name( ) //constructor { } ~class-name ( ) //destructor { }

}; The important features of a destructor are:

A class can have only one destructor function. Destructor cannot have any arguments. It cannot return anything.

260

Classes & OOP

Page 262: C++ Basics to Advanced

If a destructor is not explicitly specified by the programmer, there is no problem. In some cases a destructor needs to be specified.

So, what are the cases where we need to bother about the destructor? The most common example is when you have allocated some memory using the ‘new’ operator. Memory allocated using ‘new’ should always be freed using ‘delete’ before the termination of the program. Thus in this case we will specify the appropriate ‘delete’ statement in the destructor to ensure that the memory is freed up. Let’s see a simple program to understand a destructor:

#include <iostream.h> class car { private: int speed; char color[20]; public: car( ) //Constructor { cout<<"\nCreating a Car"; } ~car( ) //Destructor { cout<<"\nDestroying the Car"; } }; //End of class declaration int main( ) { car mine; return 0; }

The output would be: Creating a Car Destroying the Car

Dynamically creating objects: In the chapter on pointers we discussed how we can allocate memory dynamically. This concept can be extended to allocate memory for user-defined data types also (like objects). C programmers would be familiar with the dynamic allocation operators ‘malloc’ and ‘free’ (the C++ equivalent are ‘new’ and ‘delete’). One of the most significant features of ‘new’ and ‘delete’ is that these operators are aware of constructors and destructors. Let’s try out a simple example:

class car { private: int speed; char color[20]; public: car( ) //Constructor { cout<<"a Car"; } ~car( ) //Destructor {

261

Classes & OOP

Page 263: C++ Basics to Advanced

cout<<"the Car"; } }; int main( ) { car *mycar=new car; delete mycar; return 0; }

The output will be: Creating a Car Destroying the Car

As you can deduce from the output, creating an object using ‘new’ ensures that the constructor for that object is called. Similarly delete calls the destructor of the object. This won’t happen if we were to use ‘malloc’ and ‘free’. Objects in Functions: Objects can be used in functions just like other data types are used. Objects can be passed as argument to a function and an object can be returned from the function. These two topics will be dealt below separately. Passing Objects to Functions: An object can be passed to a function as shown in the program below:

#include <iostream.h> class rect { private: int length, breadth; public: rect(int x, int y) { cout<<endl<<"New rectangle created"; length=x; breadth=y; } void area( ) { cout<<endl<<"The area of the rectangle is : "<<length*breadth; } ~rect( ) { cout<<endl<<"Rectangle destroyed"; } }; void calculate(rect r) //object used as parameter { r.area( ); } int main( ) { rect ob(5,4); calculate(ob); return 0; }

262

Classes & OOP

Page 264: C++ Basics to Advanced

The output will be: New rectangle created The area of the rectangle is : 20 Rectangle destroyed Rectangle destroyed

‘ob’ is an object of type ‘rect’. It is initialized using the parameterized constructor. The function calculate can accept arguments of type ‘rect’ (i.e. it can be passed ‘rect’ objects). We have created only one object (ob) but the destructor has been executed twice; why? When we say:

calculate(ob); ‘ob’ will be passed by value to the function ‘calculate’ (i.e. the function will not directly work on the object ‘ob’. It will only work on a copy of the object ‘ob’). Since it works on a copy of the object, a temporary object of type ‘rect’ will be created when calculate ( ) function is executed. When the program exits the calculate ( ) function, the temporary object has to be destroyed and this is why the destructor has been invoked twice in the above program (once for destroying the temporary object and once for destroying the object ‘ob’). You might wonder why the constructor is also not executed twice? When a temporary object is created the constructor is not invoked. When a temporary object is created the program wants to maintain the state of the temporary object exactly the same as the original object (i.e. the member data values of the original and temporary object should be the same). Constructors are usually used for initializing and if the temporary object gets initialized then the values in the temporary object will be different from the original. So the constructor is not executed for a temporary object creation. This topic is discussed again in the “Copy Constructors” section. Returning Objects from Functions: What happens while passing an object to a function will happen even when you return an object from a function (i.e. a temporary object is created while returning an object also).

class rect { private: int length, breadth; public: rect(int x, int y) { cout<<endl<<"New rectangle created"; length=x; breadth=y; } void area( ) { cout<<endl<<"The area of the rectangle is : "<<length*breadth; } ~rect( ) { cout<<endl<<"Rectangle destroyed"; } }; rect dummy_func( ) { rect r(5,5); return r; }

263

Classes & OOP

Page 265: C++ Basics to Advanced

int main( ) { rect ob(9,9); ob=dummy_func( ); ob.area( ); return 0; }

The output is: New rectangle created New rectangle created Rectangle destroyed Rectangle destroyed The area of the rectangle is : 25 Rectangle destroyed

The header of dummy_func( ) is: rect dummy_func( )

This signifies that dummy_func( ) is a function which has no parameters and returns an object of type ‘rect’. Based on the output we can say that the destructor is invoked one extra time (just as it was done when we passed an object to a function). In this case the destructor is invoked due to the statement:

ob=dummy_func( ); While returning an object the program will create a temporary object (which gets destroyed at the end). This extra temporary object can create problems if you make use of dynamic memory allocation. We’ll revisit this topic after we learn copy constructors. Initializing an object using an existing object: In fundamental data types, the following is possible: int age1 = 10; int age2 = age1; We’ve used one variable to initialize another. The same is possible with objects also but care has to be taken.

Consider the following example: #include <iostream.h> class rect { private: int length, breadth; public: rect(int x, int y); void area( ) { cout<<endl<<"The area of the rectangle is : "<<length*breadth; } ~rect( ) { cout<<endl<<"Rectangle destroyed"; } };

264

Classes & OOP

Page 266: C++ Basics to Advanced

//Constructor can be defined outside the class using scope resolution operator rect::rect(int x, int y) { cout<<endl<<"New rectangle created"; length=x; breadth=y; } int main( ) { rect ob1(10,9); rect ob2=ob1; rect ob3(ob1); ob1.area( ); ob2.area( ); ob3.area( ); return 0; }

The output will be: New rectangle created The area of the rectangle is : 90 The area of the rectangle is : 90 The area of the rectangle is : 90 Rectangle destroyed Rectangle destroyed Rectangle destroyed

Quite interesting! We’ve created 3 objects and we have 3 destructors but only one constructor! The code: rect ob1(10,9);

works normally. This would create an object called ‘ob1’ and it would invoke the constructor (and finally a destructor). The problem lies in the following 2 statements:

rect ob2=ob1; rect ob3(ob1);

Something unexpected happens here. When trying to initialize an object using another object, C++ uses its own assignment operator. You might ask, “We’ve only used one constructor in our code. Where is this copy constructor?”. The compiler provides a default copy constructor when we don’t provide one. In the above example this doesn’t create much of a problem because our destructor doesn’t do anything useful (and if we never defined a destructor we would never have realized the existence of the copy constructor). But there are cases where the destructor is defined to perform some important clean up work and in these situations the default copy constructor can create havoc in your code. We’ll discuss this later in this chapter.

265

Classes & OOP

Page 267: C++ Basics to Advanced

Classes Explanation

A Practical use of classes:

The Problem:

Living forms (plants, animals and humans) haven’t been fully understood by man. There are many things in life that are very complex and the most complex of all is the human being. A simpler life form would be the bacteria. Nowadays, researchers are trying to mimic or imitate the behaviour of living organisms (like bacterial movement). Consider the problem of optimization. Optimization means finding the best point in a given area. How do we decide which point is best? As a simple example, consider an equation with a single variable ‘x’. Let us assume that you have to find the lowest value for that equation (the equation is also called as a function). In this case the best value means the lowest value. Let the equation be:

cost = 10*x + 100 …given that x is greater than 0 and can only take positive integer values between 1 and 100.

What value of x do you think gives the least cost?

If x=1 then cost=110; if x=2 then cost=120 and so on…

It is quite clear that x=1 will give the least cost. Hence the best point (or optimum value) is x=1.

That was very easy. The equation was very simple, involving just one variable. In real application you would have equations involving more than one variable and you will also have many conditions (the only condition we used was that x should lie between 1 and 100).

You can extend the problem to the case of two variables.

Let’s assume that we have to find the least value for a given equation that has two variables (x

and y). Let the limits of x and y be between 1 to 100 (and assume that they can take only

integer values). The solution space (or the area in which you have to find the best value) will

be as below:

Maxi limit

50 X axis

Solution Space

1000

50 Y -axis

100

266

Classes & OOP

Page 268: C++ Basics to Advanced

It is clear from the above graph that there will be 100*100 (=10,000) possible coordinates (in other words 10,000 possible combined values for x and y). The simplest way to find the minimum value for a function involving x and y would be to substitute all the 10,000 combinations, find the cost for each combination and then choose the minimum value. This seems to be a straightforward and simple method, but you can imagine the computational time needed when the solution space increases in size. And if you include floating point values, you are going to end up with a major problem. This is why we need techniques for optimization. We need to use algorithms that can determine the best point in a given solution space as quickly as possible (i.e. the algorithm should not calculate values for each and every combination).

This is where bacterial movement comes into the picture. You might have heard of the E.Coli bacteria. This bacterium lives in the human body and has a particular method of movement. It keeps searching for areas where there is more food (or nutrition). Assume that the bacteria is initially at some place in your intestine. It will take a small step in one particular direction. If the new place has more nutrition than the previous position, then it will continue to move in the same direction. Otherwise, it will take a different direction and proceed. Suppose the new position has more nutrition, it will take another step in the same direction. Again if the next position is favourable, it will proceed in the same direction. This movement is a very simplified version of the actual movement (there are more complicated steps involved in bacterial movement but we will not deal with them here). Now you can apply the same bacterial movement algorithm to the problem of optimization. The advantage is that you won’t be computing values for each and every point in the solution space (i.e. we will program in such a way that after a particular number of iterations the program will terminate). There are many other organisms in nature which have different ways of movement and this could also be applied to the problem of optimization (in fact algorithms relating to ants, bees and snakes have been developed already).

So, where do we make use of classes and objects?

Bacteria is a general category and it forms the class. All types of bacteria will have some stepsize (stepsize refers to the length of the stride taken by the bacteria in moving once) and some particular direction as well (bacteria are in three-dimensional space and they are free to take any direction). Thus, these features are common to all bacteria. We can then model bacteria as a class. This class could contain the following functions:

• To initialize the starting position of the bacteria. Every bacterium when it is created will have a particular position in 3 dimension. This has to be specified by three variables: x,y and z.

• To generate the direction in which the bacteria should move (in mathematics this is called the unit vector).

• To find the new position after the bacteria moves. • An algorithm to decide whether to move to the new position or not (move only if new

place is better than the present one).

Every instance created from the class ‘bacteria’ is an object. Each object will have a different starting position (decided randomly) and each bacteria will move in different directions (i.e. each one will try to find the best point).

267

Classes & OOP

Page 269: C++ Basics to Advanced

Thus, we can create 10 objects of type ‘bacteria’ and make each bacteria search the solution space. After some particular number of iterations, we can stop the bacterial movement and find out the positions of each of the objects.

Demonstration of the Bacteria Class

We shall create a class called ‘bacteria’. This class will model a real life bacteria

and we shall assume that the bacteria is going to move only on a 2-D (two-dimensional) area

(i.e. we will only bother about the x and y axis). Of course you can extend it to 3-D as well.

To keep track of the position, we shall make use of two variables x[2] and y[2].

x[0] and y[0] will be used to store the starting position. Once the bacteria moves, the new position will be stored in x[1] and y[1]. For the next movement, x[1] and y[1] will become the starting point and so we set:

x[0]=x[1] and

y[0]=y[1]

Another data member is the variable ‘stepsize’. This determines the length of each stride of the bacteria. Higher the value, the more distance the bacteria will cover each time.

The other two variables needed are ‘ai’ and ‘bi’. This is used to specify the direction of the movement (those of you who have learnt about vectors will know about the properties of a unit vector). Anyway a unit vector is needed to decide the direction of movement.

268

Classes & OOP

Page 270: C++ Basics to Advanced

The program written below is meant to illustrate the use of comments as well. Many programmers have different styles of commenting but you should always make use of comments throughout your program.

//**************************************************************** //Aim :Illustrate the movement of a bacteria based on food available * // The class bacteria models a general bacteria * //Author :Santhosh * //Written on :12 Feb 2004

//Revised on :16 Feb 2004 * // (modified the unit_vec( ) function) * //****************************************************************

class bacteria {

protected: float x[2],y[2],food[2]; //To store the old and new position int stepsize; float ai,bi; //Unit vector

public:

bacteria( ) //Constructor with no arguments {

x[0]=rand( ); y[0]=rand( ); stepsize=1; food[0]=x[0]+y[0]; ai=bi=food[1]=0; x[1]=y[1]=0;

}

void disp_final( ); //To display new position void disp_initial( ); //Display initial position void unit_vec( ); //To generate direction void calculate( ); //To move the bacteria void move( ); int test( ); };

269

Classes & OOP

Page 271: C++ Basics to Advanced

void bacteria::unit_vec( )

{

ai = rand( ); bi = rand( ); float mag = (ai*ai) + (bi*bi); mag = sqrt(mag); ai = ai/mag; bi = bi/mag;

}

void bacteria::calculate( )

{

x[1]=x[0]+(stepsize*ai); y[1]=y[0]+(stepsize*bi); food[1]=x[1]+y[1];

}

int bacteria::test( )

{

if (food[1]>food[0]) //only if food increases we want to move bacteria { return 1; }

else return 0;

}

void bacteria::move( )

{

x[0]=x[1]; y[0]=y[1]; food[0]=food[1];

}

270

Classes & OOP

Page 272: C++ Basics to Advanced

void bacteria::disp_initial( )

{

cout<<endl<<endl<<"The original position of bacteria is : "; cout<<x[0]<<" , "<<y[0];

}

void bacteria::disp_final( ) {

cout<<endl<<"The new position of bacteria is : "; cout<<x[1]<<" , "<<y[1];

}

int main( )

{ int round; bacteria fever;

for (round=0; round<3; round++)

{ fever.disp_initial( ); fever.unit_vec( ); fever.calculate( ); while (fever.test( )= =0)

{ fever.unit_vec( ); fever.calculate( ); }

fever.move( ); fever.disp_final( ); }

return 0; }

271

Classes & OOP

Page 273: C++ Basics to Advanced

The output would be:

The original position of bacteria is : 41 , 18467 The new position of bacteria is : 41.2325 , 18468 The original position of bacteria is : 41.2325 , 18468 The new position of bacteria is : 42.0056 , 18468.6 The original position of bacteria is : 42.0056 , 18468.6 The new position of bacteria is : 42.3698 , 18469.5

The test( ) function is used to ensure that the bacteria moves to positions with more food. If the amount of nutrition at the new place is less, then we will find a new unit vector and search in another position. In the above program the food will always keep increasing in the new position (because there is no provision provided for generating a negative unit vector). In real applications you would have to provide means for doing that as well. Also, the food function here is simply ‘x’ coordinate plus ‘y’ coordinate; in real applications you will have complicated equations.

The output may appear a little weird. What does 42.3698 , 18469.5 mean? Actually we are working with vectors; thus the output is as good as saying:

42.3698 x + 18469.5 y

42.3698 units on the x axis and 18469.5 units on the y axis.

This is a very simple illustration. In reality, the bacteria would probably have to move around thousands of times and it has to move in some logical manner. Over here we make the bacteria move in a completely random manner. And you will also need to use a lot more bacteria objects to search for the best point (one bacteria object will not be sufficient to search a huge area).

272

Classes & OOP

Page 274: C++ Basics to Advanced

The use of Inheritance:

We’ve modeled a general class called ‘bacteria’ but the problem is that certain types of bacteria have different methods for movement (for example: E.Coli and TB bacterium move differently). E.Coli and TB are bacteria but they have different movements. Now what can we do? Should we declare another class called ‘ecoli’ and ‘tb’? Should we again declare the coordinate variables x,y,z and the member functions in each of these classes? And if we should do that, what is the purpose of having a general ‘bacteria’ class?

The idea of having a general bacteria class serves two purposes:

• First of all, we are modeling the real world (we have different types of bacteria, each with some unique features). But all bacteria have certain common features and it is these common features that we declare in the general bacteria class. It is easier to think and program based on real life objects (this is the basis of OOP).

• Secondly, we can use the concept of inheritance to solve our problems. Using this we needn’t redefine all the variables and functions that are present in the ‘bacteria’ class again.

The ecoli and TB will also be modeled as classes but they will be derived from the general ‘bacteria’ class. In this way the two classes will inherit the properties of the ‘bacteria’ class. This is known as inheritance. Thus now you can create objects of type ‘TB’ and ‘ecoli’ as well. Inheritance will be dealt with in Chapter 10.

Suppose another programmer in future wants to add another type of bacteria, he can simply derive a new class from the existing ‘bacteria’ class (thus saving time and reusing the code).

273

Classes & OOP

Page 275: C++ Basics to Advanced

Data Encapsulation

Let us assume that you have created a new library that you want to distribute to other

programmers. For example, working with images (bmp, gif or jpg) isn’t that easy in C++.

You have to write methods (functions) for reading an image, identifying what type it is,

displaying the image etc. There are many image-processing applications where the first step is

to read an image. The simplest application is Optical Character Recognition (OCR). In OCR

we write a program that will be able to recognize hand-written letters or numbers. For

instance, if you write 8, the computer should be able to recognize that an 8 has been written

(no matter how bad the handwriting is). Whatever you write on paper will be scanned into the

system and stored in the form of an image file. A programmer developing an OCR application

could either start from scratch or he could focus on his problem (i.e. OCR).

By starting from scratch, he has to write a lot of coding just for the purpose of reading an

image. Instead if he can get access to some good Image processing library (which will contain

files for reading images), he could simply use the #include directive and concentrate on the

OCR algorithm.

So, let’s say that you have created a library for the purpose of image processing. You will

make use of the concept of classes. You will create a class called ‘Image’, which contains

member functions for reading an image, identifying the type of image etc. You will also have

some member data like the height and width of the image. When you distribute your library to

others, you give them the opportunity to create objects (or instances) of your ‘Image’ class.

You will also have provided a little help file describing the various functions that are available

for use. Note: You will only describe the use of the various functions (ex: the function syntax

and what it will do). You will not be providing an insight into the implementation aspect of

the function (i.e. you will not be explaining the function definition).

Now, the OCR person will start coding his application. There is a chance that he might use the

same member data identifier ‘length’ and ‘width’ in his program. Imagine the consequence if

‘length’ and ‘width’ are not made private within your class. There is a good chance that the

user will fiddle around with the data (knowingly or unknowingly) and their values will get

274

Classes & OOP

Page 276: C++ Basics to Advanced

altered by the user (which is what you don’t want). The result is that your functions, which

operate on these member data, will produce some ambiguous results. By hiding your data, you

prevent such problems from happening because the user will never be able to access or

modify your class’ private members.

The idea of encapsulation is to prevent users from having access to everything. They should

be able to access members that are needed for their use. In larger classes there may be many

members (data and functions) which are not required for someone who is going to use your

class. These members should be made private.

Remember: Usually the class declarations are written in a *.h header file (this is called as the

interface) while the definitions for the member functions are written in a *.cpp source file

(this will be the implementation). Thus even if the implementation is changed the interface

remains unaffected (more about this in Multiple file programming).

Thus to summarize:

• Data encapsulation means hiding the implementation of a class from the user. We

restrict the user in such a way that the user can perform only limited operations on the

private part (and that is only through the member functions). The user cannot access

any of your private members directly.

• Another advantage of hiding the implementation is that you (the class designer) may

decide to change the process of implementation. You might have discovered some

better and effective method for doing the same purpose. By hiding the

implementation, all you need to do is to change the function definition. The user will

never have to change his code (because you have only changed the implementation,

which the user has nothing to do with). The code written by the user will still run even

though you have changed the implementation.

275

Classes & OOP

Page 277: C++ Basics to Advanced

More on Classes and Objects

The idea of OOP is to write programs which will mirror the real world. Whenever you write a program using classes you have to decide as to what are the members of the class? To do these take the real-world entity that you are going to model. For example: Let us model a class based on a cricket player (say a batsman). Our problem is that we want to write a program to maintain records of different batsman (particularly we need to keep track of their highest and lowest scores). Every batsman will have the following attributes (or properties):

• A name • A player number • Date of Birth • Native place • Best score (or highest score) • Worst score • Physical description (like hair colour etc.)

We are not interested in storing information about all the attributes and hence we must isolate the attributes that are relevant to our problem. Considering our problem we will only need to use the name, player number, best score and worst score properties.

Next we have to decide on the methods (or functions) that are required in the class. The following will be needed:

• Initialize the member data. • A display function to display the player details (i.e. display the member data values) • Function to update the best score after every match. • Function to update the worst score after every match.

Classes are sometimes modeled diagrammatically as shown below:

In this example, ‘batsman’ is the class and any instance created from this class is an object (i.e. if you create an object having values for each of the properties then that object is said to be an

276

Classes & OOP

Page 278: C++ Basics to Advanced

instance of the class). For example: if we create an object called ‘ob1’ with the following member values:

Name: Sachin

Number: 10

Highest score: 200

Lowest score: 5

then ‘ob1’ is an instance of the class (or entity) ‘batsman’. This should clarify any doubts you might be having about the difference between a class and an object.

Similarly whatever real life object you consider, you can model them into classes depending on the problem.

Private Member functions:

Usually member data are made private while functions (or methods) are made public. There might be instances where you might want to make certain functions private (i.e. you may not want the user to directly access these functions). Private functions can only be called from within public member functions. These functions are also called ‘helper functions’ Why do we need them?

Let’s take the example of the class ‘batsman’. After every match the user will enter the batsman’s new score and then he will have to call two functions to update the batsman’s record (i.e. the user has to call update_best ( ) and update_worst ( )). It is unnecessary to bother the user with this kind of a double function call. Why should the user access these two functions directly? Instead of this, we could define another function called update ( ) which will call update_best ( ) and update_worst ( ). In this way the user needs to only call one function after every match.

The idea of classes is to restrict user access. We don’t want the user to access data or functions unnecessarily. So, we will make update_best ( ) and update_worst ( ) as private functions while update ( ) will be a public function.

277

Classes & OOP

Page 279: C++ Basics to Advanced

// To illustrate the use of helper functions (private functions)

#include <iostream.h>

class batsman { private: int player_number; int best_score,worst_score; void update_best(int); void update_worst(int);

public: batsman(int n, int b, int w) //constructor { player_number=n; best_score=b; worst_score=w; }

void update(int); void display( ); };

void batsman::update(int x) { update_best(x); //private function is called update_worst(x); cout<<"\n\nThe scores have been updated\n"; }

void batsman::display( ) { cout<<"\nHighest score : "<<best_score; cout<<"\nLowest score : "<<worst_score; }

void batsman::update_best(int y) //defining the private functions { if (y>best_score) { best_score=y; } }

278

Classes & OOP

Page 280: C++ Basics to Advanced

void batsman::update_worst(int z) { if (z<worst_score) { worst_score=z; } }

int main( ) { batsman b(1, 140, 20); cout<<"The scores before the match is "; b.display( ); b.update(180); cout<<"\nAfter this match the scores are "; b.display( ); return 0; }

The output will be:

The scores before the match is Highest score : 140 Lowest score : 20 The scores have been updated After this match the scores are Highest score : 180 Lowest score : 20

In this example it may seem unnecessary to provide two functions called update_best ( ) and update_worst ( ). Both could have been combined into one function but this example was meant to illustrate how private functions can be used in more complex classes.

279

Classes & OOP

Page 281: C++ Basics to Advanced

Friends and more!

The following topics are covered in this section:

• Friend Function • Friend Class • Static Members • Constant Objects

Friend Function: The private member data of a class can be accessed only by the class' member functions. Well, there is one exception. A friend function will be friendly with a class even though it is not a member of that class. By the term friendly we mean that the friend function can access the private members of the class. Check out the example below:

#include <iostream.h> class car { private: int speed; char color[20];

public: void input( ) { cout<<"\nEnter the color : "; cin>>color; cout<<"\nEnter the speed : "; cin>>speed; }

friend void display(car); //Friend of the class 'car' };

void display(car x) { cout<<"\nThe color of the car is : "<<x.color; cout<<"\nThe speed of the car is : "<<x.speed; }

int main( ) { car mine; mine.input( ); //accessing a member function display(mine); //passing the object ‘mine’ to the friend function return 0; }

280

Classes & OOP

Page 282: C++ Basics to Advanced

The output is:

Enter the color : red Enter the speed : 345 The color of the car is : red The speed of the car is : 345

First of all we've created a class named car. It has two private data: color and ‘speed’ and one public function ‘input’. It also has a friend function called display ( ).

Next comes the definition of display ( ):

int display (car x)

The parameter specifies an object of type ‘car’. The function has been defined to output:

x.color; x.speed; The idea is that you will pass an object of type ‘car’ to this function and the function will display the corresponding color and speed values for that particular object.

In the main ( ) part, an object called ‘mine’ has been created whose private data are set by the user through the input ( ) function. Next the friend function has been called:

display(mine);

Remember: Friend functions are not members of any class but they can access private data of the class to which they are a friend.

Beware: Since they are not members of any class, you should not call them using the dot operator.

You might be wondering what's the big point of friend functions. In the above example, we could have made ‘display’ as a member function of the class instead of declaring it as a friend function. What's the use? A friend function can be friendly to 2 or more classes. The friend function does not belong to any class, so it can be used to access private data of two or more classes. No other function can do that!

#include <iostream.h> class virus; // forward declaration of ‘virus’ class

class bacteria { private: int life; public: bacteria( )

281

Classes & OOP

Page 283: C++ Basics to Advanced

{ life=1; }

friend void check(bacteria, virus); };

class virus { private: int life; public: virus( ):life(0) {}

friend void check(bacteria, virus); };

void check (bacteria b,virus v) {

if (b.life= =1 || v.life= =1) { cout<<"\nSomething is alive"; } if (b.life = = 1) { cout<<"\nA bacteria is alive."; } if (v.life = = 1) { cout<<"\nA virus is alive."; }

}

int main( ) { bacteria fever; virus cholera; check(fever, cholera); return 0; }

In the second line of the program we have the statement:

class virus;

282

Classes & OOP

Page 284: C++ Basics to Advanced

This is a forward declaration of the class ‘virus’. This is done because when the compiler reads through the lines in the ‘bacteria’ class, it encounters the word ‘virus’ in the friend function declaration. Until this point it doesn't know what ‘virus’ is because we will be defining the ‘virus’ class later on. So we tell the compiler in advance that ‘virus’ is a class by declaring it in the starting itself. If you don't declare it, you will get errors. Just try it out.

One more note: You should declare the friend function in both the classes where you want it to be a friend.

In this program we want to check whether any organism is alive at present by testing the value of ‘life’. Of course you can write individual member functions in each class but the use of a friend function makes it simpler and easier to understand the logic.

Friend Classes

Just like functions are made friends of classes, we can also make one class to be a friend of another class. In this way, the friend class will have access to all the private members of the other class.

#include <iostream.h> class add { private: int x,y; public: add( ) { x=y=4;| }

friend class support; //support is now a friend of add };

class support { public: void sum(add ob) //it can access private members of class 'add’ { cout<<"The sum of the 2 members is : "<<(ob.x+ob.y); }

};

283

Classes & OOP

Page 285: C++ Basics to Advanced

int main( ) { add ad; support sup; sup.sum(ad); return 0; }

The output will be:

The sum of the 2 members is : 8

In this program, the class ‘support’ is a friend of the class ‘add’. Thus the class ‘support’ can access all the private members of the class ‘add’ (i.e. ‘x’ and ‘y’). Friend classes are rarely used.

Static Class Members

A class is just an empty area. No area is allocated when you create a class. So, only when you create an object of that class will the compiler allocate space to the object. Again, this means that:

private: int x;

does not allocate space for an integer x. When you create objects belonging to this class, required space is allocated for holding the integer. Each new object that you create belonging to this class will have its own version of the integer ‘x’. The keyword ‘static’ helps you to create a single copy for all objects belonging to the same class.

#include <iostream.h> class bacteria { private: static int count;

public: void modify( ) { count=count+1; cout<<endl<<"The new count is : "<<count; }

};

284

Classes & OOP

Page 286: C++ Basics to Advanced

int bacteria::count=5; // memory allocated for ‘count’ over here int main( ) { bacteria b; bacteria plague; b.modify( ); plague.modify( ); plague.modify( ); return 0; }

The output is:

The new count is : 6 The new count is : 7 The new count is : 8

The integer ‘count’ is declared to be static. But again outside the class we say:

int bacteria::count=5;

This is done so that the compiler will allocate space for the integer ‘count’. If ‘count’ were not static, when would the compiler allocate space to it? It would allocate space when an object is created. But in the case of static variable members in a class, we will create it only once and so we have to ensure that the compiler creates that one instance of the variable. In this case we’ve initialized it to a starting value of 5. If you simply type:

int bacteria::count;

‘count’ would be automatically initialized to 0. Since ‘count’ is static, each time you change the value of ‘count’, all the objects belonging to that class will make use of the new value.

So far we’ve seen static member data but you can also make functions static in a class. Just precede the function by the keyword static. Static functions will be useful to operate on static member data.

285

Classes & OOP

Page 287: C++ Basics to Advanced

Constant Object

Classes are user-defined types and objects can also be made constant using the ‘const’ keyword. When an object is made constant there are some peculiar properties that you should take note of. A constant object is created to avoid making changes to any of the values of the member data (that is the purpose of constant in general and so is the case with constant objects).

1.) Constant objects cannot access member functions that are not constant.

#include <iostream.h> class bacteria { private: int life,count;

public:

bacteria( ) { life=1; }

void display( ) { cout<<endl<<"The life of bacteria is : "<<life; }

};

int main( ) { const bacteria fever; fever.display( ); //COMPILER ERROR return 0; }

The error results because display ( ) is not a constant member function. Ideally, an object that is constant should not be able to modify any of its member data values. You might ask, "The display function doesn’t modify any values; so why can’t we call it?" The compiler when reading through the code will not believe that a member function doesn’t change any values (even if it really doesn’t). There is a good chance that the member function might be defined in some other file and the compiler might never know about the definition (you’ll see this in multiple file programming). Hence, irrespective of whether the function changes a value or not, a constant object cannot access the non-constant functions.

286

Classes & OOP

Page 288: C++ Basics to Advanced

2.) So, the question arises, what are constant functions? Constant functions can be called by constant objects. The compiler doesn’t know when a function modifies data values and when a function doesn’t. When you make a function constant, the compiler believes that the function will not change any of the member data values. To create a constant function you have to simply add the keyword ‘const’ at the end of the function.

class bacteria { private: int life,count;

public: bacteria( ) { life=0; }

void display( ) const { cout<<endl<<"The life of bacteria is : "<<life; }

};

int main( ) { const bacteria fever; fever.display( ); return 0; }

The output is:

The life of bacteria is : 0

The program will now run because a constant object is calling a constant member function.

Beware: Don’t make a function constant and then write a code within the body to modify some member data value. The compiler will produce an error. Try it and see.

287

Classes & OOP

Page 289: C++ Basics to Advanced

3.) Can a constant member function never modify any member value? Again there is an exception to this. If a member data is declared as ‘mutable’, then the constant member function can modify this member data. You might wonder what purpose this would solve? There might be a case where you want to keep count of something for all objects (irrespective of whether they are constant objects or not). In this case, a constant object should also be able to access such member data.

Let’s take the same case of the bacteria class. We shall make the member data ‘life’ mutable.

class bacteria { private: mutable int life;

public: bacteria( ) { life=0; }

void display( ) const { life=life+1; cout<<endl<<"The life of bacteria is : "<<life; }

};

int main( ) { const bacteria fever; fever.display( ); return 0; }

The output is:

The life of bacteria is : 1

Since ‘life’ is mutable, the constant function can modify this variable.

288

Classes & OOP

Page 290: C++ Basics to Advanced

Copy constructors, pointers to objects

The following topics are covered in this section:

• Pointers to Objects • this Pointer • How objects are stored in memory • Copy Constructors

Pointers to Objects

You can create pointers to objects and to access the member functions you will have to use the arrow operator just like you would do for structures.

class bacteria {

private: int life; public: bacteria( ) { life=1; } void display( ) { cout<<"Life is : "<<life; }

}; int main( ) { bacteria *p; bacteria ecoli; p=&ecoli; p->display( ); return 0; }

The output is:

Life is : 1

You cannot attempt to access any private members of a class using a pointer to an object (data hiding is retained irrespective of whether you create an object or a pointer to an object).

289

Classes & OOP

Page 291: C++ Basics to Advanced

How are objects stored in memory?

We know that classes are just a framework- they do not occupy memory space. Instances of the class (objects) will occupy memory space. But a class has member data as well as member functions. It is obvious that every instance of the class should have its own copy of the member data; but is it necessary that every object should have a copy of all the member functions? The member data values will vary from object to object but the member functions are going to be the same.

Member functions are implemented by taking advantage of this fact and hence only a single copy of the member functions is stored in memory. For example:

#include <iostream.h>

class heater

{ private: int temp;

public: heater( ) { temp=0; }

void set_temp(int x) { temp=x; cout<<"temperature is now:"<<temp; }

//…..other functions

};

int main( ) { heater h1,h2,h3; h1.set_temp(40); h2.set_temp(50); h3.set_temp(60); return 0; }

In the above program, each of the 3 objects will have their own ‘temp’ member but the function set_temp( ) is common to all 3 of them.

The diagram below illustrates this fact.

290

Classes & OOP

Page 292: C++ Basics to Advanced

Now an interesting question arises. If all objects are going to access the same member functions how does the function operate on the correct object? How does the function know on which object it has to act upon? How is it that when h1 calls set_temp(), the member ‘temp’ of h1 gets set?

This indicates that there must be a way in which the member function can identify who called it? The member function set_temp( ) should be able to differentiate between the objects h1, h2 and h3. To achieve this we pass a special member to the function (which the function uses to identify who called it). This member is the ‘this’ pointer.

‘this’ Pointer

We’ve seen that objects can call member functions (public functions) by simply using the dot operator. But the member functions are not called as we might think. The address of the object (i.e. the object calling the function) is passed to the member function. This is passed as an argument to the member function. You might be thinking, "We never mentioned any argument in my member function. Where does this argument go?"

Actually this is a kind of hidden argument (something that isn’t explicitly stated). Where does this argument go? The hidden argument is the address of the object and hence it has to go to a pointer. What is the name of the pointer? This pointer is called as ‘this’ pointer because it will point to the address of the object which called the member function (it will point to this object or in other words the present object). In an earlier example (the one with a class called ‘car’), we came across the function:

void input( ) { cout<<"\nEnter the color : "; cin>>color; cout<<"\nEnter the speed : "; cin>>speed; }

This is a member function belonging to the class called ‘car’. ‘color’ and ‘speed’ are member data of that class.

291

Classes & OOP

Page 293: C++ Basics to Advanced

In reality, the above function is equivalent to:

void input( ) { cout<<"\nEnter the color : "; cin>>this->color; cout<<"\nEnter the speed : "; cin>>this->speed; }

In fact the compiler will convert our code into something similar (since it has to use the ‘this’ pointer to access the member data of the correct object). The ‘this’ pointer is useful when a member function of a class has to pass the address of the current object to another part of the program (or another function).

Beware: The ‘this’ pointer is not present in static member functions. It is also not present in friend functions (because friend functions are not members of a class).

Copy Constructors

It’s time to go back to the problem we discussed earlier (the mystery of the default copy constructor). A copy constructor is invoked when you initialize a new object of a class using an existing object. This will happen when:

• You pass a copy of an object as argument to a function (i.e. when passing by value).

• When you return an object from a function

• Or initialize an object during declaration using another object.

If we don’t specify a copy constructor, the compiler already has a default copy constructor. This default copy constructor will simply perform a bit-by-bit copy of the original object to the new object (i.e. it will blindly copy everything to the new object). This can lead to problems (especially if you use the ‘new’ and ‘delete’ operators in the constructor and destructor of the class). An example was discussed earlier in this chapter (using the ‘rect’ class).

We’ll start by considering the same example:

class rect { private: int length, breadth;

public: rect( ) { cout<<endl<<"This is the constructor with no arguments";

292

Classes & OOP

Page 294: C++ Basics to Advanced

length=breadth=0; }

rect(int x, int y);

void area( ) { cout<<endl<<"The area of the rectangle is : "<<length*breadth; }

~rect( ) { cout<<endl<<"Rectangle destroyed"; } };

rect::rect(int x, int y) { cout<<endl<<"New rectangle created"; length=x; breadth=y; }

int main( ) { rect ob1(10,9); rect ob2=ob1; rect ob3(ob1); rect ob4; ob4=ob3; ob1.area( ); ob2.area( ); ob3.area( ); ob4.area( ); return 0; }

The output is: New rectangle created This is the constructor with no arguments The area of the rectangle is : 90 The area of the rectangle is : 90 The area of the rectangle is : 90 The area of the rectangle is : 90 Rectangle destroyed Rectangle destroyed Rectangle destroyed Rectangle destroyed

293

Classes & OOP

Page 295: C++ Basics to Advanced

Let’s try to map the output with the code we wrote:

rect ob1(10,9); rect ob2=ob1; rect ob3(ob1); rect ob4; ob4=ob3;

We’ve created 4 objects and we have 4 destructors but only 2 constructors. Why?

ob1 is created using the parameterized constructor. No problems here since we are calling the constructor directly.

ob2 and ob3 do not call the constructor. They are initialized using ob1 and in these 2 cases it is the default copy constructor that is called.

ob4 is created using the no argument constructor. Thus we’ve called only 2 constructors and the problem is because of our default copy constructor. What we need to do is to provide our own copy constructor. A copy constructor for the above program would be defined as shown below:

rect(const rect &rc) { cout<<endl<<"invoked copy constructor."; length=rc.length; breadth=rc.breadth; }

This is relatively simple. It is just another constructor which takes an argument of an object (we pass the object to this constructor by reference and not by value – if we passed it by value we’ll be creating another temporary object). Add the above code to the program and run it again.

The output will now be:

New rectangle created invoked copy constructor. invoked copy constructor. This is the constructor with no arguments The area of the rectangle is : 90 The area of the rectangle is : 90 The area of the rectangle is : 90 The area of the rectangle is : 90 Rectangle destroyed Rectangle destroyed Rectangle destroyed Rectangle destroyed

294

Classes & OOP

Page 296: C++ Basics to Advanced

As you can see, the copy constructor is called twice (for ob2 and ob3). That’s good but you might ask, “Why should I worry about this copy constructor?”. We’ll take another example.

Let us suppose that we are using the ‘new’ operator in the constructor of the class and ‘delete’ is used in the destructor of the class. We also have a function whose argument is an object of that class. While calling this function, we will pass an existing object to the function. The function will create a temporary object (a copy of the object) but it will not invoke the constructor for this new object. Hence the ‘new’ operator (present in the constructor) is not invoked and an exact replica of the passed object is made. During destruction, the temporary object will invoke the destructor (which has the ‘delete’ operator and it will delete the portion of memory allocated to the original object). When the original object is destroyed, it will also invoke the destructor and this will again try to free the same memory. This can lead to serious problems. To solve this problem we have to define our own copy constructor.

Let ‘bacteria’ and ‘virus’ be two classes. Each one will have a variable called ‘life’ created dynamically using the ‘new’ operator. We shall also define a friend function to which we will pass objects of both ‘bacteria’ and ‘virus’.

#include <iostream.h>

class virus; class bacteria { private: int *life; public: bacteria( ) { life=new int; cout<<"\nCreated Bacteria"; *life=1; }

~bacteria( ) { cout<<"\ndestroyed bacteria."; delete life; } friend void check(bacteria, virus); };

class virus { private: int *life;

public: virus( )

295

Classes & OOP

Page 297: C++ Basics to Advanced

{ life=new int; cout<<"\nCreated virus"; *life=0; }

friend void check(bacteria, virus);

~virus( ) { delete life; cout<<"\ndestroyed virus"; } };

void check (bacteria b, virus v) { if ( (b.life[0]==1) || (v.life[0]==1) ) { cout<<"\nSomething is alive"; } }

int main( ) { bacteria fever; virus cholera; check(fever,cholera); return 0; }

The output will be:

Created Bacteria Created virus Something is alive destroyed bact. destroyed virus

And then an error is generated when using VC++ (because the same memory area is being deleted twice)….

In Visual C++ compiler it generates an error during run time and other compilers will also produce something similar (or in the worst case your program could freeze). Anyway, this can lead to drastic effects so you have to program in such a way that this problem does not occur. We’ll add our own copy constructors to both the classes.

296

Classes & OOP

Page 298: C++ Basics to Advanced

Add the following to the bacteria class:

bacteria(const bacteria &ba) { cout<<endl<<"invoked bacteria copy constructor."; life = new int; life[0]=ba.life[0]; }

and the following to the virus class:

virus(const virus &vi) { cout<<endl<<"invoked virus copy constructor."; life = new int; life[0] = vi.life[0]; }

Run the program and the output will now be:

Created Bacteria Created virus invoked virus copy constructor. invoked bacteria copy constructor. Something is alive destroyed bacteria. destroyed virus destroyed virus destroyed bacteria.

There is only one part to explain in the above program, the copy constructor. Let’s take a look at the copy constructor for the ‘bacteria’ class.

bacteria(const bacteria &ba) { cout<<endl<<"invoked bacteria copy constructor."; life = new int[2]; life[0]=ba.life[0]; life[1]=ba.life[1]; }

So, what do we do here? The basic idea is to copy all the member data values from the original object into the new object that is being created. But we don’t want to simply copy bit-by-bit. We are making use of the ‘new’ operator in the constructor of the class and so, in the copy constructor we make use of the ‘new’ operator again. In effect, this will make the program allot a new memory area to ‘life’ of the new object. ‘life’ of the original object and ‘life’ of the new object created will not be in the same area. But we want the value of the new life to be the same as the original one. So we code:

297

Classes & OOP

Page 299: C++ Basics to Advanced

life[0]=ba.life[0]; life[1]=ba.life[1];

This ensures that the values will be the same.

Now when the temporary object is destroyed, the destructor will be called and memory will be freed. When the original object is destroyed, the destructor is called again and memory (a different area) is freed up. Hence there is no clash of memory.

Another question is why do we use const in:

bacteria(const bacteria &ba)

A copy constructor is used only for the purpose of copying; to initialize a new object based on an existing one. In this case you don’t want the copy constructor to modify the existing object (so we use ‘const’).

Note: The above example was purely meant for explaining the idea of copy constructors. You wouldn’t really be creating a variable like ‘life’ dynamically.

Another solution:

Instead of using the copy constructor we could have taken a more simpler approach as well. The problem arose because we were passing 2 objects to the friend function by value. If we passed them by reference then the compiler would never need to create temporary objects. What should we do to achieve this? First declare the friend function as:

friend void check(bacteria &, virus &);

and then define it as:

void check (bacteria &b, virus &v) { if ( (b.life[0]==1) || (v.life[0]==1) ) { cout<<"\nSomething is alive"; } }

Try the program and you’ll get what we actually wanted:

Created Bacteria Created virus Something is alive destroyed virus destroyed bact.

298

Classes & OOP

Page 300: C++ Basics to Advanced

Beware: The copy constructor will not work when you use the assignment operator (=) to assign one object to another. In such cases, you have to overload the = operator to avoid the problem (operator overloading is discussed in the next chapter).

If our above program had:

int main( ) { bacteria fever; bacteria ecoli; ecoli=fever; //RUN-TIME ERROR return 0; }

we’ll again get into memory problems because now we are using the assignment operator to copy ‘fever’ to ‘ecoli’. This leads to the same problem we had with copy constructors (both fever and ecoli will now access the same memory location for ‘life’ and both will try to free the same memory location). This just proves the point that when you perform such assignments, the copy constructor does not come into play.

Remember:

bacteria ecoli=fever;

is different from

bacteria ecoli; ecoli=fever;

The first case is an initialization using an exiting object (copy constructor comes into action) but in the second case we are performing an assignment (assignment operator comes into action).

299

Classes & OOP

Page 301: C++ Basics to Advanced

Miscellaneous Topics on Classes

The following topics are covered in this section:

• Returning Objects revisited • The keyword 'explicit' • Constructor using initializer list • Declaration and definition revisited • Recap

Returning Objects revisited (return value optimization):

Returning an unnamed object (instead of a named object) might help the compiler to optimize the code to improve efficiency.

Method I (copy constructor invoked) Method II Returning an unnamed object

class base { private: int val; public: base(int x=0) { val=x; cout<<endl<<"Constructor;value:"<<val; } ~base( ) { cout<<endl<<"Destructor; value:"<<val; } base ret_base( ) { base btemp(val+1); return btemp; } base(const base &rc) { cout<<endl<<"Copy constructor"; val=rc.val; } }; int main( ) { base b1(5); b1.ret_base( ); return 0; }

class base { private: int val; public: base(int x=0) { val=x; cout<<endl<<"Constructor;value:"<<val; } ~base( ) { cout<<endl<<"Destructor;value:"<<val; } base ret_base( ) { return base(val+1); //unnamed object } base(const base &rc) { cout<<endl<<"Copy constructor"; val=rc.val; } }; int main( ) { base b1(5); b1.ret_base( ); return 0; }

300

Classes & OOP

Page 302: C++ Basics to Advanced

OUTPUT:

Constructor; value:5 Constructor; value:6 Copy constructor Destructor; value:6 Destructor; value:6 Destructor; value:5

OUTPUT:

Constructor; value:5 Constructor; value:6 Destructor; value:6 Destructor; value:5

In the first method we create an object in the function and return it:

base btemp(val+1); return btemp;

In this case, a constructor and destructor are called for objects ‘b1’ and ‘btemp’. In addition a copy constructor and destructor are called on a temporary object when we return an object from the function:

return btemp;

In the second method we return an unnamed object directly:

return base(val+1);

Here neither is an additional destructor called nor is the copy constructor invoked. This is called return value optimization (wherein the compiler is able to avoid the creation of a temporary object; thus improving efficiency).

Note: The compiler tries its best to optimize code using various techniques (so that it can reduce work for the processor which in turn helps improve efficiency of our program). Return value optimization is one such optimization technique.

The keyword ‘explicit’

Let’s consider a simple program using classes. The class ‘circle’ has 2 constructors: a single argument constructor and a no argument constructor.

#include <iostream.h>

class circle { private: int radius;

public: circle( ) { cout<<"No argument constructor invoked"; radius=0; }

301

Classes & OOP

Page 303: C++ Basics to Advanced

circle(int x) { cout<<"Single argument constructor invoked"; cout<<"is:"<<x; radius=x; }

int get_radius( ) { return radius; }

};

void display_area(circle c1) { int r=c1.get_radius( ); cout<<"circle has an area:"<<(3.14*r*r); }

int main( ) { display_area(10); return 0; }

The output will be:

Single argument constructor invoked Radius is:10 The circle has an area:314

The problem that can be caused is quite obvious. We used a statement:

display_area(10);

but the actual function is declared as:

void display_area(circle c1)

The display_area( ) function is supposed to take a circle object as its argument whereas it seems to be accepting even an integer. How is this possible?

When the compiler encounters:

display_area(10);

it checks up and realizes that display_area( ) needs a circle object. So it goes to the class ‘circle’ to see if it can perform some conversion (i.e. if it can convert an integer into a circle object). The compiler finds a single argument constructor:

circle(int x)

and decides to use this constructor to convert the 10 into circle c1(10). This is called an implicit conversion (i.e. the compiler does the conversion without prompting the user- it considers this a

302

Classes & OOP

Page 304: C++ Basics to Advanced

normal scenario). If we didn’t have a single-argument constructor, the compiler would not permit this. For example if our constructor were:

circle(int x, int y)

instead of

circle(int x)

then the compiler would give an error message saying “conversion from `int' to non-scalar type `circle' requested”

So, what should we do to prevent this implicit conversion from taking place? Should we avoid using a single argument constructor? C++ provides a keyword called ‘explicit’.

Instead of the constructor:

circle(int x) { cout<<"Single argument constructor invoked"; cout<<"is:"<<x; radius=x; }

add the keyword ‘explicit’ as shown below:

explicit circle(int x) { cout<<"Single argument constructor invoked"; cout<<"is:"<<x; radius=x; }

and now try to compile the program. You will get a compilation error pointing to the function call:

display_area(10);

along with the error message “conversion from `int' to non-scalar type `circle' requested”

To summarize this section:

• When we have a single argument constructor, the compiler might try to perform an implicit conversion to convert an object into an object of the required type.

• This can be prevented by using the keyword ‘explicit’ in the constructor.

303

Classes & OOP

Page 305: C++ Basics to Advanced

Constructor using initializer list:

The following code:

int main( ) { const int x; return 0; }

will not compile because a ‘const’ has to be initialized. The compiler error will be:

‘x’ : const object must be initialized if not extern

The next code snippet will also produce a compiler error:

int main( ) { int x; int &ref; return 0; }

This time we have created a reference variable but haven’t initialized it. The error message will be:

'ref' : references must be initialized

Keeping these two facts in mind, let’s go back to initializer lists. The constructor for the class can be written as:

counter ( ) { count=7; }

or we could use the form:

counter ( ) : count (7) { }

The second method makes use of an initializer list (i.e. count(7) forms the initializer list over here) to initialize the values while the constructor body is empty. The first form isn’t really an initialization; it’s just an assignment.

The first form is equivalent to writing:

int count; count = 7;

Thus the variable ‘count’ is first created and later assigned a value of 7. Initialization means giving a value to the variable at the time of creation; i.e.

int count=7; is an initialization.

304

Classes & OOP

Page 306: C++ Basics to Advanced

To prove that

counter ( ) { count=7; }

is just an assignment, let’s write a small program.

#include <iostream.h>

class counter { private: const int count;

public: counter( ) { count=0; }

};

int main( ) { return 0; }

Compiling this code you would get at least a couple of errors:

'count' : must be initialized in constructor base/member initializer list l-value specifies const object

The second error is produced because of the line:

count=0; //A const object cannot be assigned a value.

Or you might get the error: assignment of read-only member `counter::count'

This program clearly indicates that the constructor we’ve used is simply performing an assignment operation rather than initialization of member data. Let’s modify the code:

#include <iostream.h>

class counter { private: const int count;

public: counter( ):count(0) {}

305

Classes & OOP

Page 307: C++ Basics to Advanced

void display( ) { cout<<"count is:"<<count; }

};

int main( ) { counter c1; c1.display( ); return 0; }

This code will compile and execute correctly.

Thus a constructor is of the general form:

class-name(arguments) : member(value), member(value)… { //body of constructor for any assignments }

To summarize:

• All const and reference member data types have to be initialized using an initializer list. • Anything within the body of a constructor is only an assignment and not an initialization. • If one class contains another class (i.e. one object contains an object of another type), then

you’ll have to initialize the other object in the initializer list.

Beware: When using an initialization list the order in which the members are initialized depends on the order in which you declare them within the class (does not depend on the order specified in the initializer list). The first member in the class will be the first member to be initialized. If your initializations depends on the sequence then be careful while using initializer lists.

306

Classes & OOP

Page 308: C++ Basics to Advanced

Declaration and Definition revisited:

Now we are in a better position to understand these 2 terms (which were introduced in the first chapter itself).

Declaration:

We tell the compiler that “this is the name of an object of this type which I might use later in my program.” Example:

class virus; //class declaration

We tell the compiler beforehand that ‘virus’ is a class.

int getHealth ( ); //function declaration

extern int error_no; //declaration of error_no

The last example is used when you are writing code in multiple files (where one part of the code uses variables defined in another file). This will be dealt with later.

But the point to note is that in the above 3 cases, we are only declaring something to the compiler (a function, a class and an object). The compiler does not allocate any memory space for them.

Definition:

Let’s start of with the examples:

//Defining a class class virus { private: int life;

//rest of the class };

//defining a function int getHealth ( ) { return health; }

In a definition we provide the compiler with details. What about the next case?

//defining and declaring int error_no;

This is what we’ve been using frequently so far. This statement defines error_no (the compiler allocates storage space for it) and also declares error_no as type integer.

307

Classes & OOP

Page 309: C++ Basics to Advanced

Recap

• The main concepts of OOP are: data abstraction, data encapsulation, inheritance and

polymorphism.

• Classes form the basis for OOP. Using classes, data and the functions that can operate on the

data can be bundled together.

• An object is an instance of a class.

• The 3 access specfiers used in classes are private, protected and public. These specifiers

determine as to who can access the class members.

• The constructor is a special function invoked when an object is created and the destructor is

invoked when the objected is deleted (or destroyed).

• Every class is provided with a default constructor by the compiler if the programmer doesn’t

define any constructor.

• A constructor without parameters is a default constructor.

• Constructors can be overloaded using different parameters.

• When creating an array of objects, the class should have a default constructor.

• When an object is passed to a function or when an object is returned from a function, a

temporary object is created (the temporary object will only invoke the destructor but not the

constructor).

• Friend functions are not members of a class but they can access the private members of the

class.

• Objects can be made constant but they can only access constant functions and they cannot

alter the value of member data (unless the member is declared to be ‘mutable’).

• Static members can be accessed even without using an object.

• Copy constructors should be defined in a class in case the dynamic memory allocation

operators are used in the constructor and destructor.

• Copy constructors are not invoked in assignments.

• The ‘explicit’ keyword is used with constructors to prevent implicit conversions.

• Initializer lists are used to initialize the member data of an object.

308

Classes & OOP

Page 310: C++ Basics to Advanced

Chapters 7 and 8 (Classes and Pointers)

Workshop Quiz

Q.) Predict the output:

void change( int *b, int n) { int i; for( i = 0; i < n; i++) { *(b+i) = *(b+i) + 5; } }

int main( ) { int a[]={ 2,4,6,8,10 }; int i; change(a,5); for( i = 0; i <= 4; i++) { cout<<“ ”<<a[i]; } return 0; }

Answer.) 7 9 11 13 15

A.) I don’t think this should pose much of a problem. We are passing the address of the first element of an array to the function (through a pointer). Within the change ( ) function, we modify each element by adding 5 to it. Hence the output will be:

7 9 11 13 15

2. Q.) What will the program below do?

void func(int *x, int *y) { int *t; t = x; x = y; y = t; }

int main( ) { int a=2; int b=3; func(&a,&b); cout<<endl<<a<<" , "<<b; return 0; }

Answer.) Nothing

On seeing a temporary variable and assignment statement, people jump to the conclusion that the above program will swap the values of two integers. But this is WRONG. The program actually does nothing.

void func(int *x, int *y) {

int *t; t = x; x = y; y = t;

}

Within this function, all we are doing is assigning addresses (we are in no way altering ‘a’ or ‘b’). The output will be: 2 , 3

You could swap the values at a particular address (which is what we always do) but here we are simply using the addresses. Hence ‘a’ and ‘b’ are not swapped.

309

Page 311: C++ Basics to Advanced

3. Q.) What is the value of ‘sum’ in the program below:

int getNewValue(int x) { static int div=1; return(x/++div); }

int main( ) { int a[ ]={12,24,45,0,6}; int i; int sum=0; for(i = 0; a[i]; i++) { sum+=getNewValue(a[i]); } cout<<sum; return 0; }

Answer.) 25

There are a few important points to note in the above program. First of all you should note that we can pass individual array elements to functions as shown in the program above. Secondly, as long as the condition (i.e. the middle expression) in the ‘for’ loop is true (or non-zero), the loop will keep repeating. Thirdly, you should know about static variables and also about the prefix ++ operator. The ‘for loop’ will terminate for the case of a[3] because a[3] is 0. The value of sum will be:

sum = (12/2) + (24/3) + (45/4) = 25 (because of integer division).

4. Q.) What is the use of this function?

int len(char *str) { int length=0; while(*str++!='\0') { length++; } return length; }

Answer.) Returns the number of characters in the string

The use of *str++ might seem confusing. ++ has a higher precedence than * (but it’s better to use parentheses to make it clear- the parentheses have been dropped out here just to make the code appear more complex! Some C++ tests might present similar code fragments). Postfix ++ will increment its operand only after the assignment has been done (i.e. it will occur only in the next statement). Thus when * (the dereferencing operator) is applied, we will check whether the character is a null character or not and then we increment the value of ‘str’ (which means ‘str’ will now point to the next character in the string).

310

Page 312: C++ Basics to Advanced

5. Q.) Can you predict what will happen?

int main( ) { int const max=5; int ar[max]={1,2,3,4,5}; int *ptr=&ar[0]; for (int i=0;i<max;i++) { *ptr=ar[i] + 5; ptr++; }

for (int i=0;i<max;i++) { cout<<endl<<ptr[i]; } }

Answer.) Garbage values

A.) If you are expecting that the output will be 6,7,8,9,10 then you are wrong. Why? ptr was initialized to the address of ar[0]. But within the ‘for’ loop we have kept on incrementing ‘ptr’. Each time the corresponding value at the location is incremented by 5. Once the first loop is complete, ptr is now pointing to the next location beyond ar[4]. Thus in the second loop we are actually displaying garbage values (instead of getting the display of 6,7,8,9,10). If the 2nd loop were:

for (i=0;i<max;i++)

{ cout<<endl<<ar[i]; }

then it would be fine.

6. Q.) Will this compile?

void print(int &x) { cout<<endl<<"Reference"; }

void print(int x) { cout<<endl<<"By value"; }

int main( ) { int y=5; print(y); return 0; }

Answer.) No

Without the statement

print(y);

the code might compile on some compilers. But this statement will produce a compile-time error because the compiler wouldn’t be able to decide which print( ) function to call. Some compilers will flag an error saying “parameter list of print(int) is not sufficiently different from print(int &)”.

And this is logically true since there is no way in which the compiler will know which function to call.

311

Page 313: C++ Basics to Advanced

7. Q.) Is there an error? What would be the output?

class dummy { private: int data; public: int get_data( ) { return data; } };

int main( ) { dummy d1; cout<<d1.get_data( ); return 0; }

Answer.) Output will be a garbage value The code will compile and execute even though there is no constructor explicitly defined for the class dummy. The compiler will provide with a default constructor. But some beginners are tempted into believing that a default constructor will initialize all member data. This is not true. The above program will display some undefined (garbage) value. To initialize member data correctly, the class designer should define a constructor and not rely on the implicitly created default constructor.

8. Q.) Will the following code compile? If it will, what is the output?

int increment(int &x) { x++; return x; }

int main ( ) { int y=5; increment(y)=2; cout<<y; return 0; }

Answer.) Compile Error

A.) The compiler will complain at the statement:

increment(y)=2;

saying that increment(y) is not an L-value. The function increment( ) returns an integer which we can use as an R-value, not an L-value. An L-value is one which can be used on the left side of an assignment expression. This in turn means that an L-value should refer in someway to a memory location where a new value can be stored (after the assignment). Thus in the above code fragment, the statement:

increment(y)=2;

is equivalent to writing:

an-integer-constant=2;

where integer-constant could be anything like 1 or 2 or 3 etc. This is illegal since an integer constant is not a memory location.

312

Page 314: C++ Basics to Advanced

9. Q.) By changing the increment function will it work?

int& increment(int &x) { x++; return x; }

int main ( ) { int y=5; increment(y)=2; cout<<y; return 0; }

Answer.) Output is 2.

A.) This will compile since we are returning a reference (and we aren’t returning a reference to a local variable; so the compiler won’t flag a warning). So how does this work and what is the output?

‘y’ is passed as reference to the function increment( ) within which modifying ‘x’ is the same as modifying ‘y’. Thus the value of ‘y’ is incremented to 3. Then we return a reference to x (which is equivalent to returning a reference to ‘y’). Thus the value 2 gets assigned to ‘y’ and the output will be 2.

The point to note is that when a function returns a reference, it can be used as an L-value.

10. Q.) What is the output?

class bacteria { public: static int count;

public: //multiple use of public void modify( ) { count=count+1; cout<<endl<<"The new count is : "<<count; } };

int bacteria::count=5;

int main( ) { bacteria b; bacteria::count=1; b.modify( ); return 0; }

Answer.) 2

A.) The code will compile (we’ve used ‘public’ twice but this isn’t an error). The output of the code will be 2 (i.e. count will be 2). Since ‘count’ is a public member the statement bacteria::count=1; will modify ‘count’. If ‘count’ were private then the statement would have yielded an error.

313

Page 315: C++ Basics to Advanced

More questions (classes and pointers)

Interview and Viva questions:

Q.) List out the two uses for the & and * operator and also how they differ.

Q.) List out the important characteristics of OOP.

Q.) Differentiate between a class and an object. Give an example.

Q.) What are pointers?

Q.) What is the relationship between arrays and pointers?

Q.) Should we use reference variables or pointers? Can we use references everywhere instead of pointers?

Q.) Why is it better to pass by reference than by value?

Q.) Differentiate between the use of ‘public’ and ‘private’ in classes.

Q.) Is it that we should always make data private and functions public?

Q.) Explain constructors and destructors in classes and why they are needed.

Q.) How many constructors and destructors can a class have? Can we overload destructors?

Q.) What is a default constructor?

Q.) Which method of accessing arrays is more efficient/ faster? (indexing or pointer accessing).

Q.) When we pass 2-D arrays to a function why do we need to specify the 2nd dimension in the function declaration?

Q.) What is a copy constructor? Should we pass the argument to the copy constructor only by reference? Why?

Q.) Does implicit conversion occur only when the class has a single-argument constructor?

314

Page 316: C++ Basics to Advanced

Programs to try out

Q.) Write a program to display the address of an array of integers (the array element values are entered by the user).

Q.)Use pointers to reverse a given array of integers.

Q.)Sort an array in descending order using pointers.

Q.) Write a program to set the value of the entire array to some user-defined value (use an integer array).

Q.) Create a class for complex numbers. It should have two member data (for the real and imaginary parts of a complex number). The class should have functions for calculating the magnitude of the number, displaying the complex number and also for obtaining inputs from the user and setting the values for the real and imaginary parts.

Q.) Create a class called ‘batsman’ which should contain member data for keeping track of his name, highest and lowest score, number of matches etc. There should be a function to obtain these values from the user and another function to display the details.

Q.) Create an employee class which models a real life employee and make use of appropriate class members. Provide parameter and parameter-less constructors for the class. The class should at least consist of the following: employee ID, department, age, salary and experience. Also include a function to calculate bonus for an employee (bonus=10% of salary * experience).

Q.) Create a class called ‘student’. It should have provision for the following:

• Obtaining the name, date of birth and marks in 5 subjects for the student.

• Calculating the current age of the student.

• Calculating the average marks of the student.

Q.) Create a class called ‘time’. This should have 3 member data: hours,minutes and seconds. There should be functions to reset the time, to add user-entered minutes to the time and calculate the new time (for example if the user inputs 70 minutes then the member function should add 1 hour and 10 minutes to the present time). A function should exist to display the current time.

Q.) Write a class to model a 3x3 matrix. This class should have functions to obtain the elements of the matrix, to find the inverse of the matrix and also to find the determinant of the matrix.

315

Page 317: C++ Basics to Advanced

Conquering C++ - Part II/II

Contents - PART II Advanced C++

9.) Operator Overloading

• Overloading a unary operator (with return data type)

• Overloading a binary operator (with arguments)

• Operator overloading restrictions• Operator overloading through

friend functions • Overloading a Unary Operator

using Friend Function • Overloading the Assignment

Operator (=) Returning Reference • Adding Objects with Built-in

(fundamental) data types • Overloading ‘new’ and ‘delete’

operators

10.) Inheritance

• Concept of Inheritance • Access specifiers • Overloading and overriding • Virtual Functions • Pure Virtual Functions and

Abstract classes • Can a Destructor be Virtual? • Pointers to base and derived

classes • Multiple Inheritance (multiple

base classes) • Virtual Base Classes • Relationships between classes-

object composition and private inheritance

• More polymorphism (poly polymorphism!)

12.) Useful Classes and Functions

• Manipulators • Buffering and Flushing Streams • Formatting flags for manipulators• Creating new manipulators

(custom manipulators) • Strings (string objects and

character arrays) • Mathematical calculations • Good programming practices • Typedef • Bitwise Operators

13.) Advanced Topics

• Preprocessor directives • Namespaces and Headers • Multiple file programs • Extern (as a storage and linker

specifier) • Creating Libraries in C++ • Exception Handling • Templates • Casting and cast operators • An introduction to STL (Standard

Template Library)

14.) General Question and Answers

Page 318: C++ Basics to Advanced

11.) Files and Streams

• Need for Streams • Overloading << and >> • Predefined streams • Stream Status and status flags • Reading the stream status • Opening and Closing a file • Writing and Reading a Text file • Accessing other devices (printer

and monitor) through streams • Structures in Files • Padding/Packing of structures • Random Access of Files • Sequential Access and Random

Access Files • Command Line Arguments

Workshop (for units 9-11)

• Quiz-IV • Bonus Questions-IV

(extra programs)

15.) Data Structures

• Linked List • Linked List using STL • Stacks • Queues • A closer look at STL • Sequence Containers (Vectors) • Subscripts and Vectors • More functions on Vectors • Lists • Member functions in Lists • Deque • Associative Containers • Set Container • MultiSet Container • Map Container • Multimap Container • Container Adaptors • Algorithms • Trees • Implementing a Binary Tree

• An intro to analysis of algorithms-I (searching and sorting)

• Analysis of algorithms-II

• Appendix (Working in VC++)

Page 319: C++ Basics to Advanced

Operator Overloading- Intro

The following topics are covered in this section:

• Intro to operator Overloading • Operator overloading with return data type

An Introduction to Operator Overloading

Operator overloading means giving additional meaning to normal operators when they are applied to user defined data types. How will the compiler recognize this? The compiler identifies that it is an overloaded operator by looking at the data type of the operand (i.e. if it is a user defined data type then the compiler knows it is an overloaded operator; if it is a built-in data type then the compiler will do the normal operation).

You have to remember that objects are defined by you (or the programmer). You choose the data that comes under an object. The data type that you use will be in-built data types like integer or character but the object as a whole is user defined. Hence it is not possible to add two objects (even though they belong to the same class) using the + sign. The addition operator is designed to work only on in-built data types like integers, float etc. It cannot add two objects. But using operator overloading you could use the same + sign to add two objects. Operator overloading is a form of polymorphism (for example, the + operator once overloaded in a class can be used in different ways depending on the operands. If the operands are objects then the overloaded + is used. If the operands are fundamental data types then the normal + operation is carried out).

Syntax:

return-data-type operator symbol-of-operator (parameters) { //body of the function }

Example:

void operator ++ ( ) { body of function; }

The word ‘operator’ is a keyword and is necessary for overloading an operator. Let us write a program to overload a unary operator (++)

318

Operator Overloading

Page 320: C++ Basics to Advanced

class timer {

private : int countdown; public : timer ( ) : countdown (100) // Constructor { } int display ( ) { return countdown; } void operator -- ( ) // -- is overloaded operator. { -- countdown; }

}; int main ( ) {

timer c1, c2; cout<<"\nInitial c1 value : "<<c1.display( ); cout<<"\nInitial c2 value : "<<c2.display( ); --c1; // -- is an overloaded operator --c2; // Compiler knows this because c1 and c2 are user defined data --c2; --c2; cout<<"\n\nFinal c1 value : "<<c1.display( ); cout<<"\nFinal c2 value : "<<c2.display( ); return 0;

}

The output would be:

Initial c1 value : 100 Initial c2 value : 100 Final c1 value : 99 Final c2 value : 97

When compiler comes across --c1 it does the following. It knows that c1 is an object which is a user defined data type. But -- can operate only on in-built data types. Now it thinks over and remembers that -- was already overloaded using the operator function within the class timer. So it goes back to the operator function and reads through what is written in the body. No return data type and no arguments. This makes its work a lot easier. It just has to perform one instruction.

--countdown; countdown is an integer data type. The compiler decreases the value of countdown by one and goes back to the original program. All this was done when it encountered the line --c1. Hence the count of c1 only is decreased by one when the compiler reads the line

--c1 Similarly, when the compiler comes to --c2 it decreases the count of c2 by one.

319

Operator Overloading

Page 321: C++ Basics to Advanced

Operator Overloading with return data type

Consider the same example as in the previous section. This time we shall make use of the return data type in the operator function to return something to the main ( ) function.

class timer {

private : int countdown; public : timer ( ) : countdown (100) { } timer (int t) : countdown (t) // Constructor with arguments { } int display ( ) { return countdown; } timer operator -- ( ) { -- countdown; return timer(countdown); //unnamed object created using 2nd constructor }

}; int main ( ) {

timer c1, c2; cout<<"\nInitial c1 value : "<<c1.display( ); cout<<"\nInitial c2 value : "<<c2.display( ); c2=--c1; cout<<"\n\nFinal c1 value : "<<c1.display( ); cout<<"\nFinal c2 value : "<<c2.display( ); return 0;

}

The output is:

Initial c1 value : 100 Initial c2 value : 100 Final c1 value : 99 Final c2 value : 99

In the above program we overload the same unary operator but use a return data type timer. Timer is the name of the class itself. It would seem as if the function is returning a class. But a class is just a template and means nothing without objects. Hence the function is actually returning an object belonging to class ‘timer’.

320

Operator Overloading

Page 322: C++ Basics to Advanced

--countdown; return timer(countdown);

As in the earlier program, the overloaded operator just decreases the ‘countdown’ value by one. It does the same here. We know that ‘timer’ is the name of the class. Within the braces we have written countdown. In the program countdown of c1 was 100 and then it became 99 because of the

--countdown;

statement. Now countdown is 99. Hence the return statement is equal to timer (99). This line creates an object without a name (an unnamed object). The constructor with parameters is made use of to initialize this object. The ‘countdown’ value of the unnamed object is initialized to 99. Now all this is supposed to be returned to the calling function.

c2 = --c1; c2 is an object and hence the value of count of the unnamed object is stored in c2. So c2's count becomes 99.

What do you think will happen if we type:

c2 = c1--;

instead of:

c2 = --c1;

We have overloaded for the prefix -- operator and not the postfix -- operator. If you want to overload the postfix operator what should we do? The compiler should be able to distinguish between the two forms and hence according to C++ norms, you should use an integer argument if you want to overload the postfix form of an unary operator. In this case you should modify the function header as:

timer operator -- (int x)

The integer ‘x’ is just for telling the compiler that this function corresponds to the postfix operator. The same method is used for the unary increment operator ++ also.

In case you don’t overload the postfix operator (but have overloaded the prefix operator), and you use the postfix operator, then your compiler might display a warning message and make use of the prefix overloaded form.

321

Operator Overloading

Page 323: C++ Basics to Advanced

Operator Overloading- II

The following topics are covered in this section:

• Binary Operator Overloading • Restrictions on overloading • Overloading using Friend Functions • Overloading unary operator using Friend Functions • Overloading the assignment operator

Binary Operator Overloading

In the case of a unary operator we generally use no arguments. In the case of a binary operator we need to use one argument.

// Overloading the binary + operator #include <iostream.h> class time {

private: int hours,minutes; public: time( ) { hours=0; minutes=0; } time(int x,int y) { hours=x; minutes=y; } void display( ) { cout<<endl<<hours<<" hours and "<<minutes<<" minutes."; } time operator + (time);

}; time time::operator + (time z) {

int h = hours + z.hours; int m = minutes + z.minutes; while (m>=60) { m = m-60; h = h+1;

322

Operator Overloading

Page 324: C++ Basics to Advanced

} return time(h,m);

} int main( ) {

time t1(2,40); time t2(3,30); time t3; t3 = t1+t2; t3.display( ); return 0;

}

Let's take a closer look at the program. The values for hours and minutes of t1 and t2 objects are initialized using the constructor function. The compiler then reads: t3 = t1 + t2;

Remember: You cannot simply add two objects and assume that the compiler will add up the corresponding member data. Objects are user-defined data types and the operators will operate only on in-built data types.

Thus to operate on user-defined data types, we overload the operator and tell the compiler as to what it should do. On seeing the two operands, the compiler knows that + is overloaded and so it goes to the definition of the operator function. But we have a problem.

+ works on two operands, t1 and t2. It would appear that we need to pass two values to the operator function so that it can add these two values. But the operator function has only one parameter. What does this mean? It means that only one of the values are passed (i.e. only one object is passed). So, out of the two (t1 and t2) how do we know which one is passed? And what about the other object?

The operand to the right of the operator is passed as an argument. Hence the values of hours and minutes of t2 are passed to the operator function. Let's take a look at the first two lines within the operator function. int h = hours + z.hours; int m = minutes + z.minutes; First of all, you know that t2 is passed as an argument. Hence, z actually represents t2 in our case.

323

Operator Overloading

Page 325: C++ Basics to Advanced

In the expression:

int h = hours + z.hours;

what does the hours stand for? This hours is the value of t1's hours. t1 is on the left of the operator and it is not passed as argument. Hence hours and minutes refer to the member data of t1.

In technical terms: When an overloaded operator is invoked, the object on the left of the operator is the object of which the operator is a member and the object on the right must be provided as argument to the operator. Actually the object on the left of the operand is passed to the operator function (remember the ‘this’ pointer which is always present in member functions; we don’t specify it explicitly, it is like a hidden argument). The ‘this’ pointer is the reason that you don’t need to pass any arguments while overloading unary operators.

Is there anything that cannot be overloaded?

Well, almost all operators can be overloaded in C++. You can overload even the [], the comma operator, new, delete etc. But the following operators cannot be overloaded:

• The dot/member operator (.) • The scope resolution operator (::) • .* operator • Ternary (conditional) operator (?) • Sizeof operator

• You cannot create new operators; you can only overload existing C++ operators. You also cannot overload operators to operate differently on in-built data types (for example: you cannot ask the + operator to operate differently on integers).

• The operator precedence and associativity remain the same even when overloaded (they cannot be altered).

• If any operator is overloaded in the base class, then any derived class will also inherit the overloaded function. But if the overloaded operator is the assignment operator (=), this will not be inherited by the child class.

• Whenever you use operator overloading try to retain the original meaning of the operator. For instance when you overload +, overload it in such a way that it will ultimately be adding some data.

Beware: If an operator is a binary operator, then you cannot overload it or convert it into a unary operator.

324

Operator Overloading

Page 326: C++ Basics to Advanced

Overloading using Friend Functions

So far we have overloaded operators using member functions. We can also overload operators using the friend function. Friend functions are not members of the class and they do not have the ‘this’ pointer. Hence when you overload a unary operator you have to pass one argument. When you overload a binary operator you have to pass two arguments. Let’s see that same example that we saw earlier for overloading a binary operator.

class time {

private: int hours,minutes; public: time( ) { hours=0; minutes=0; } time(int x,int y) { hours=x; minutes=y; } void display( ) { cout<<endl<<hours<<" hours and "<<minutes<<" minutes."; } friend time operator + (time, time);

}; time operator + (time y, time z) { int h = y.hours + z.hours; int m = y.minutes + z.minutes; while (m>=60) { m = m-60; h = h+1; } return time(h,m); } int main( ) {

time t1(2,40); time t2(3,30); time t3; t3 = t1+t2; t3.display( ); return 0;

}

325

Operator Overloading

Page 327: C++ Basics to Advanced

The output will be:

6 hours and 10 minutes

Just one point to note here: the operand on the left will be passed as the first argument while the operand on the right will be passed as the second argument.

Overloading a Unary Operator using Friend Function

When you overload a unary operator using a friend function you would have to pass one argument to the friend function.

Beware: When you use friend functions, they will not have the ‘this’ pointer. If you attempt to modify some value of an object passed as argument, then the friend function actually only operates on a copy (it does not act on the original object). This is because it is passed by value (and not as reference).

Thus you would actually have to work on a copy of the object, and then return a newly initialized object having the modified values (we did the same process in using friend function for binary operator overloading). To work directly on the original object, you can make use of reference parameters in the operator overloaded friend function.

//Overloading a unary operator using friend function

#include <iostream.h> class timer {

private : int countdown; public : timer ( ) : countdown (100) { } timer (int t) : countdown (t) { } int display ( ) { return countdown; } friend timer operator -- (timer &x);

}; timer operator --(timer &x)

326

Operator Overloading

Page 328: C++ Basics to Advanced

{ x.countdown=x.countdown-1; return x; } int main ( ) {

timer c1, c2; cout<<"\nInitial c1 value : "<<c1.display( ); cout<<"\nInitial c2 value : "<<c2.display( ); --c2; cout<<"\n\nFinal c1 value : "<<c1.display( ); cout<<"\nFinal c2 value : "<<c2.display( ); return 0;

}

The output will be:

Initial c1 value : 100 Initial c2 value : 100 Final c1 value : 100 Final c2 value : 99

The friend function header specifies that we want it to use reference parameters:

timer operator --(timer &x)

All the operations performed in this function are directly on the original object. Thus we can use the return statement:

return x;

instead of initializing a new object with the same member data values.

Overloading the Assignment Operator (=) and returning reference

There are some other aspects of operator overloading that will be illustrated in this section by overloading the assignment operator. The assignment operator has to set the left operand value to that of the right operand. Thus, when we equate objects we will want the same process to occur. The following program would seem quite adequate for the purpose:

class timer {

private : int countdown; public : timer ( ) : countdown (100) { }

327

Operator Overloading

Page 329: C++ Basics to Advanced

timer (int t) : countdown (t) { } int display ( ) { return countdown; } void operator =(timer &x) { countdown=x.countdown; }

}; int main ( ) {

timer c1, c2(98),c3(94); cout<<"\nInitial c1 value : "<<c1.display( ); cout<<"\nInitial c2 value : "<<c2.display( ); cout<<"\nInitial c3 value : "<<c3.display( ); c1=c2; cout<<"\n\nFinal c1 value : "<<c1.display( ); cout<<"\nFinal c2 value : "<<c2.display( ); cout<<"\nFinal c3 value : "<<c3.display( ); return 0;

}

The output will be:

Initial c1 value : 100 Initial c2 value : 98 Initial c3 value : 94 Final c1 value : 98 Final c2 value : 98 Final c3 value : 94

Actually everything in the above program seems to be perfect. We are making use of the pass by reference method. Instead of:

void operator =(timer &x)

you could write:

void operator =(timer x)

The program will work but a temporary object will be created (i.e. the function will work on a copy of the original object). When the function completes executing, the temporary object will be destroyed.

So, can you think of any flaw with the above program? What will happen if we write:

c1 = c2 = c3;

Try it and you would get an error. The problem is that we have overloaded = in such a way that it will not return anything (it returns void). The compiler will first evaluate:

328

Operator Overloading

Page 330: C++ Basics to Advanced

c2 = c3;

Hence c2 will get the values of c3. But this evaluation does not return anything and the next step for the compiler will be:

c1 = void;

and this is not possible.

Thus you have to overload the assignment operator such that it will return something (it should return an object of course).

class timer {

private : int countdown; public : timer ( ) : countdown (100) { } timer (int t) : countdown (t) { } int display ( ) { return countdown; } timer& operator =(timer &x) { countdown=x.countdown; return *this; }

}; int main ( ) {

timer c1, c2(98),c3(94); cout<<"\nInitial c1 value : "<<c1.display( ); cout<<"\nInitial c2 value : "<<c2.display( ); cout<<"\nInitial c3 value : "<<c3.display( ); c1=c2=c3; cout<<"\n\nFinal c1 value : "<<c1.display( ); cout<<"\nFinal c2 value : "<<c2.display( ); cout<<"\nFinal c3 value : "<<c3.display( ); return 0;

}

329

Operator Overloading

Page 331: C++ Basics to Advanced

The output is:

Initial c1 value : 100 Initial c2 value : 98 Initial c3 value : 94 Final c1 value : 94 Final c2 value : 94 Final c3 value : 94

Within the function parameter we use & to represent a pass by reference. Similarly, here in this program we also return a reference (which is indicated by timer&).

timer& operator =(timer &x)

Now when the compiler encounters:

c1 = c2 = c3;

It will first evaluate:

c2 = c3;

and it will not only assign c3 to c2, but will also return c2 (c2 was the object that invoked the operator member function and ‘this’ refers to c2). The next step will now be:

c1 = c2;

and your program will work perfectly. The overloaded = operator now performs two operations: assignment as well as returning a reference to the object that invoked the overloaded operator function.

Maybe you are wondering, why not return a pointer to type ‘timer’ instead of using references for returning? Again, c2 = c3 will work but the overall expression:

c1 = c2 = c3;

will not work since the expression will reduce to:

c1 = pointer;

and this is wrong!

330

Operator Overloading

Page 332: C++ Basics to Advanced

Operator Overloading- III

The following topics are covered in this section:

• Adding object with fundamental data types • Overloading new and delete operators • Recap

Adding Objects with Built-in (fundamental) data types

So far we’ve seen binary operators operating on two objects. Now we’ll see how to overload a binary operator such that it will add an object along with an integer (which is a fundamental data type).

class timer {

private : int countdown; public : timer ( ) : countdown (100) { } timer (int t) : countdown (t) { } int display ( ) { return countdown; } timer& operator+ (int j) { countdown=countdown+j; return *this; }

}; int main ( ) {

timer c1, c2(98),c3(94); cout<<"\nInitial c1 value : "<<c1.display( ); cout<<"\nInitial c2 value : "<<c2.display( ); cout<<"\nInitial c3 value : "<<c3.display( ); c1+100; cout<<"\n\nFinal c1 value : "<<c1.display( ); cout<<"\nFinal c2 value : "<<c2.display( ); cout<<"\nFinal c3 value : "<<c3.display( ); return 0;

}

331

Operator Overloading

Page 333: C++ Basics to Advanced

The output is:

Initial c1 value : 100 Initial c2 value : 98 Initial c3 value : 94 Final c1 value : 200 Final c2 value : 98 Final c3 value : 94

When the compiler encounters:

c1+100;

it knows that the object c1 is invoking the operator+ member function. This function has an integer as its argument and 100 is passed by value to the integer ‘j’. The rest of the program is as normal.

Another point to note is about the drawback of using the overloaded operator function as a member function. In the above program if you tried:

100 + c1;

it wouldn’t work! Why? Because according to what we’ve learnt in operator overloading, the object on the left of the operator will invoke the overloaded member function. But now the operand on the left of the operator is an integer and the compiler does not know what to do.

The problem is because the operator+ function is a member function of the class and it is passed the ‘this’ pointer. The problem can be avoided if you made use of friend functions. In friend functions the ‘this’ pointer is not passed (and they are not members of the class). So you can specify the order of the arguments when you overload an operator using a friend function.

The program below illustrates the use of friend function to solve this problem:

#include <iostream.h> class timer {

private : int countdown; public : timer ( ) : countdown (100) { } timer (int t) : countdown (t) { } int display ( ) { return countdown; } friend timer& operator+ (int, timer&); friend timer& operator+ (timer&, int);

332

Operator Overloading

Page 334: C++ Basics to Advanced

}; timer& operator+ (int j, timer &x) {

x.countdown=x.countdown+j; timer *p=&x; return *p;

} timer& operator+ (timer &y, int k) {

y.countdown=y.countdown+k; timer *pt=&y; return *pt;

} int main ( ) {

timer c1, c2(98),c3(94); cout<<"\nInitial c1 value : "<<c1.display( ); cout<<"\nInitial c2 value : "<<c2.display( ); cout<<"\nInitial c3 value : "<<c3.display( ); 100+c1; c1+100; cout<<"\n\nFinal c1 value : "<<c1.display( ); cout<<"\nFinal c2 value : "<<c2.display( ); cout<<"\nFinal c3 value : "<<c3.display( ); return 0;

}

The result is:

Initial c1 value : 100 Initial c2 value : 98 Initial c3 value : 94 Final c1 value : 300 Final c2 value : 98 Final c3 value : 94

As you can see, we have made use of two friend functions to overload the + operator so that it can operate with an integer value on either side of the operator. The friend function definition might appear really strange. Why should we write:

timer& operator+ (int j, timer &x) { x.countdown = x.countdown+j; timer *p = &x; return *p; }

Why should we attempt to return a reference rather than doing the straightforward method of returning an object as below:

333

Operator Overloading

Page 335: C++ Basics to Advanced

timer operator+ (int j, timer &x) { x.countdown = x.countdown+j; return x; }

Well, this is quite correct but you have to bear in mind that the copy constructor is invoked each time you return an object from a function (which means that the destructor will be copied exactly to a temporary object bit by bit).

Try it: You can check this idea by creating your destructor function for the class. Whenever you return an object from a function, you will see that the destructor function is invoked whereas if you return a reference to an object, the destructor function will not be invoked.

In the above program this won’t cause any problems because we are not making use of the ‘new’ and ‘delete’ operators. But in case you make use of them then you should take note of this point. The same is the reason for using pass by reference in the above program.

Overloading ‘new’ and ‘delete’ operators

The ‘new’ and ‘delete’ operators are usually sufficient for most programs but there might be cases wherein you want to overload them as well. Some important points are:

• You can overload these operators globally or you can overload them for specific classes. If you overload using member function for a class then we say that you have overloaded only for that specific class. If overloading is done outside a class (i.e. it is not a member function of a class), the overloaded ‘new’ and ‘delete’ will be called anytime you make use of these operators (within classes or outside classes). This is global overloading.

• Be careful if you overload ‘new’ and ‘delete’ globally (because there might be other parts of your program which rely on the normal operation of these operators). Usually programmers do not overload them globally.

• malloc( ) and free ( ) are the C equivalent of ‘new’ and ‘delete’ respectively. To use these functions you will need to include the stdlib.h header file.

The syntax for overloading ‘new’ and ‘delete’ are:

void* operator new (size_t n) { //allocate memory and return the address (a void pointer) }

void operator delete (void *p) { //free the memory space pointer to by the pointer ‘p’ }

334

Operator Overloading

Page 336: C++ Basics to Advanced

You will notice that the parameter for ‘new’ is:

size_t n;

size_t is a numerical data type defined by the system to specify memory size in bytes. For instance if you use ‘new’ to create a character, the value of n (i.e. the argument passed to ‘n’) will be 1. If you pass a ‘double’ quantity then the value of ‘n’ will be 8 (you needn’t worry about this).

• The ‘new’ operator should return a void pointer while the ‘delete’ operator will pass a void pointer as argument.

The program below overloads the ‘new’ and ‘delete’ operators globally.

#include <iostream.h> #include <stdlib.h> void* operator new(size_t n) {

cout<<endl<<"overloaded new"; void *ptr; ptr = malloc(n); return ptr;

} void operator delete(void *p) {

cout<<endl<<"overloaded delete"; free(p);

} int main( ) {

int *p = new int; *p=20; cout<<"\n The value is : "<<*p; //value of 20 delete p; cout<<"\n The value after deleting is : "<<*p; //some invalid value return 0;

}

The output will be:

overloaded new The value is : 20 overloaded delete The value after deleting is : 4325404

335

Operator Overloading

Page 337: C++ Basics to Advanced

We make use of C functions malloc( ) and free ( ) to overload the ‘new’ and ‘delete’ operators in the above program.

• The above overloading of ‘new’ and ‘delete’ will not overload the ‘new’ and ‘delete’ used for arrays. For arrays we make use of the ‘new [ ]’ and ‘delete [ ]’ operator and you have to overload these separately. The syntax is similar but you should specify [ ] to denote arrays. The syntax for overloading ‘new [ ]’ and ‘delete [ ]’ are:

void* operator new [ ] (size_t n) { //allocate memory and return the address (a void pointer) } void operator delete [ ] (void *p) { //free the memory space pointer to by the pointer ‘p’ }

Recap

• Overloading is the process of giving additional meaning to existing operators. • Operator overloading is done so that the various C++ operators can be used on

user-defined data types. • New operators cannot be created using operator overloading. • When overloading is done through member functions, one argument is

automatically passed to the overloading function (through the ‘this’ pointer). • Binary operator overloading member functions should have one parameter while

unary operator overloading member functions don’t need any parameters. • Friend functions can also be used for operator overloading (but in this case the

‘this’ pointer is not passed to the function). • Friend functions to overload unary operators should have one parameter while two

parameters are needed for binary operator overloading through friend functions.

336

Operator Overloading

Page 338: C++ Basics to Advanced

Inheritance- intro

The following topics are covered in this section:

• Introduction • Access Specifiers

Inheritance

Inheritance means getting something from the parent. In C++ also, inheritance means the same. But who is the parent?

Remember that classes and objects are a key feature in C++. When we talk of inheritance in C++, we refer to classes. We will have the parent class and the derived class. The derived class inherits all properties of the parent class. The parent class is also known as the base class. All classes that arise from the base class are known as derived classes. These derived classes inherit all non-private parts of the base class (i.e. there are a few things that cannot be inherited from the base class. This is discussed later).

We have discussed earlier about a general class called ‘bacteria’. We also referred to two other classes called Ecoli and TB. Now, bacteria will form the base class while ecoli and TB will be derived from the bacteria class. In this way they will inherit all the general properties of a bacteria. In addition they will have their own special features (in our case they would have a special member function to describe the unique way in which they move).

You might wonder as to whether the child inherits everything from the parent? The derived class inherits everything that is NOT PRIVATE in the parent class. Remember we used ‘private’ and ‘public’ to restrict access to class members. Now when you declare any member as ‘private’, that member cannot be inherited by any of the children (in other words, private members are not inheritable). Only the ‘public’ members are inheritable.

337

Inheritance

Page 339: C++ Basics to Advanced

When any data is made private in a class, it can be accessed ONLY by member functions of that class. A derived class CANNOT access the private members of its base class. It might seem as if the concept of data encapsulation would be lost if a derived class can access only it’s parent’s public members (because the aim of classes is to restrict access to data and hence we make them public. But if we can’t inherit the private data, what’s the use of inheritance?). C++ provides us with a third access specifier called ‘protected’.

The three access specifiers for class members are:

• private • public • protected

If you declare class members as ‘protected’, then any class derived from the parent can access the protected members but no one from outside can make use of the protected members. In a way protected members are similar to private class members (because both cannot be accessed from outside the class). But both are different because protected members can be accessed by derived classes whereas private members cannot be accessed by derived classes. The use of protected access specifier is very important in inheritance.

Let's take a look at the syntax for making a derived class.

Create the base class as you normally would. Then when you want to create the derived class, use the following syntax:

class d1 : public b1 { //body of d1; };

where d1 is the name of the derived class and b1 is the name of the base class.

Let’s consider a different example for inheritance (instead of bacteria and biology). Consider cars: there are different types of cars in the world. Broadly we could divide them into two categories: normal cars and racing cars. Both of these categories belong to the same family of cars (since all cars will have some properties in common). Though they are common in certain respects they will have their own properties as well. Thus these two categories of cars can be derived from the general class of cars. Again within racing cars we have different manufacturers (and each manufacturer might make use of different specifications). For instance a ‘Ferrari’ and a ‘McLaren’ are not one and the same (even though both are racing cars). Thus let’s model a general class called ‘car’ (which will refer to racing cars). We will provide this class with only two properties: color and speed. From this class, we shall derive two classes called ‘Ferrari’ and ‘McLaren’.

338

Inheritance

Page 340: C++ Basics to Advanced

From the above figure, it can be said that, “an object of type Ferrari is a RacingCar”. This is referred to as “is-a” relationship. The various types of relationships are:

• is a • has a • is implemented in terms of

Remember: Every Ferrari is a car but not every car is a Ferrari.

When designing relationships between classes, ideally, whatever the parent can do the child should be able to do (this is the ‘is-a’ relationship).

// Program to demonstrate inheritance #include <iostream.h> class car {

protected: int speed; char color[20]; public: car( ) { cout<<"\nCreating a Car"; } ~car( ) { cout<<"\nDestroying Car"; } void input( ) { cout<<"\n\nEnter the colour of your car : "; cin>>color; cout<<"\nEnter the top speed : "; cin>>speed; }

};

339

Inheritance

Page 341: C++ Basics to Advanced

class ferrari : public car {

private: int f; public: ferrari( ) { cout<<"\nCreating a Ferrari"; } ~ferrari( ) { cout<<"\nDestroying the Ferrari"; }

}; class mclaren : public car {

private: int m; public: mclaren( ) { cout<<"\nCreating a McLaren"; } ~mclaren( ) { cout<<"\nDestroying the McLaren"; }

}; int main( ) {

ferrari mine; mine.input( ); return 0;

}

Protected data will be inherited by the derived classes. Both the member data (speed and color) of the base class ‘car’ are protected. Thus all classes derived from ‘car’ will also have the property of ‘speed’ and ‘color’. You might be wondering as to how the constructors and destructors will execute and in what order?

The output would be:

Creating a Car Creating a Ferrari Enter the colour of your car : red Enter the top speed : 150 Destroying the Ferrari Destroying Car

340

Inheritance

Page 342: C++ Basics to Advanced

As can be seen, constructors execute from the top to bottom while the destructors execute in the reverse order. It’s like you will first construct a car and then label it as a Ferrari and while destroying you would remove the label Ferrari first and then dismantle the car.

Remember: The constructor of the base class and also the destructor will be invoked.

More on Inheritance (Base-Class Access specifier)

So far we have used the following syntax for inheritance:

class derived-name: public base-name { //body of class; };

where derived-name is the name of the derived class and base-name is the name of the base class. We have already dealt with access specifiers as used for class members. The type of access specifier used determines how the members of a class are accessed (i.e. whether they can be accessed from outside, or whether their access is restricted to class members or whether they can be accessed by derived classes). The 3 types of access specifiers are:

• private • protected • public

These 3 access specifiers can be used to determine how the derived class will access the properties derived from the base class. The general syntax for inheritance is thus as given below:

class name-of-derived-class : access-specifier name-of-base-class { //body of derived class; };

We know that the derived class cannot access the private members of the base class. But what about the protected and public members of the base class? Do they become private members of the derived class? It is for clarifying this that we have to use an access-specifier to specify what we want the inherited members to become.

1. If you use the access specifier ‘public’: All the public members of the base class become public members of the derived class. The protected members become protected members of the derived class.

2. When you use ‘private’ as the access specifier: All public members of base class become private members of derived class. All protected members of base class also become private members of derived class.

3. When the base class is accessed as ‘protected’: All the public and protected members of base class become protected members of derived class.

341

Inheritance

Page 343: C++ Basics to Advanced

Inheritance- II

The following topics are covered in this section:

• Overloading and Overriding • Virtual Functions • Pure Virtual functions • Implementing Polymorphism

Overloading and Overriding

There are a few terms that are repeatedly used in C++ programming; you could call them C++ programming jargon and you’ll find references to these words often. Overloading and overriding belong to that category and it is important to know the difference between these two terms. We have already discussed about function overloading. In overloading, though the function names are the same, the functions will have different types of parameters. Thus depending on the types of arguments, the compiler will correctly choose between the functions when they have the same name.

So, what is overriding? Overriding occurs when the functions have the same name and same return type. You might wonder as to how the compiler will distinguish between functions that have the same name and arguments. Actually in this case the compiler will not be able to decide which function to use while compiling. The choice is made at run-time. Overriding is related to inheritance and is implemented using virtual functions.

The problem

Let’s take a simple problem. We want to write a program that will ask the user as to what they want to create: a general car or a ferrari. Depending on the user’s input we want to display some details of the car. (The example might seem a bit trivial but it should give you a good idea about the problem we are going to face).

We’ll make use of a base class pointer in this program. The topic on pointers and inheritance is discussed later but for the time being just remember that a base class pointer can point to an object of the base class as well an object of the derived class.

#include <iostream.h> class car {

protected: int speed; char color[20]; public: void display( ) { cout<<"\nThis is a general Car"; }

};

342

Inheritance

Page 344: C++ Basics to Advanced

class ferrari : public car {

public: void display( ) { cout<<"\nThis is a Ferrari"; }

}; int main( ) {

char choice; car *ptr; cout<<endl<<"Do you want a ferrari(f)/general car(g)? "; cin>>choice; if (choice=='g') { ptr=new car; } else if (choice=='f') { ptr=new ferrari; } else { cout<<endl<<"Invalid choice- terminating program"; return 1; } (*ptr).display( ); delete ptr; return 0;

} The output if you choose a ‘g’ is:

Do you want a ferrari(f)/general car(g)? g This is a general Car

The output if you choose a ‘f’ is: Do you want a ferrari(f)/general car(g)? f This is a general Car

We have made use of the same function name ‘display’ in both the classes because both perform the same function (the difference is that each one is supposed to display details pertaining to that object). The results are not what we wanted. When a ferrari object is created we wanted the display( ) function of the class ‘ferrari’ to be called (but instead in this program the base class definition has been called). The reason for this is that the compiler decides what has to be called at compile time itself (which actually it shouldn’t do because we will know the user’s input only at run-time). For doing this we need to explicitly tell the compiler to wait till run-time using the keyword ‘virtual’. When a function is declared to be virtual, the decision as to which function to use will be decided at run-time rather than at compile time.

343

Inheritance

Page 345: C++ Basics to Advanced

Virtual Function (late/dynamic binding)

A virtual function is a member function of a base class, which is redefined by a derived class. You need to make use of the keyword ‘virtual’, to make a function virtual. The function is redefined in the derived class to meet the needs of the derived class. The main advantage of virtual function is that they support run-time polymorphism.

#include <iostream.h> class car {

protected: int speed; char color[20]; public: virtual void display( ) { cout<<"\nThis is a general Car"; }

}; class ferrari:public car {

public: void display( ) { cout<<"\nThis is a Ferrari"; }

}; class mclaren:public car {

public: void display( ) { cout<<"\nThis is a McLaren"; }

}; int main( ) {

car *p; car mine; ferrari f; mclaren m; p=&mine; p->display( ); p=&f; p->display( ); p=&m; p->display( ); return 0;

}

344

Inheritance

Page 346: C++ Basics to Advanced

The output would be:

This is a general Car This is a Ferrari This is a McLaren

The function display ( ) is made virtual. Notice that the ‘virtual’ keyword is used only in the base class’ display ( ) function. The derived classes ‘ferrari’ and ‘mclaren’, redefine the display ( ) function.

car *p;

We are creating a pointer to point to ‘car’ (which is a class). Hence you can say that ‘p’ is pointing to an object of type ‘car’. A pointer which points to an object of the base class, can point to an object of a derived class. But the reverse is not possible. The next statement is:

p = &mine;

This assigns the address of the object ‘mine’ to the pointer ‘p’. Remember that ‘p’ is a pointer to ‘car’ type and ‘mine’ is an object of type ‘car’. So there's no problem in this assignment.

Pointers can be used to invoke member functions. Invoking in this way is done by using the following operator: -> (it is a minus followed by the less than sign. It is called the arrow operator). Since ‘p’ is pointing to an object of type ‘car’, the display( ) function of the base class will be executed. If ‘p’ were an object of type ‘car’, we would call the member function using the dot operator:

p.display( );

But since it is a pointer, you have to call it using the arrow operator. The next statement is:

p = &f;

A pointer to object of the parent class can point to an object of a derived class. ‘f’ is an object of the class ‘ferrari’ (which is derived from ‘car’). Therefore, p can point to ‘f’ and the address of ‘f’ is now assigned to p. When the display ( ) function is now invoked through the pointer ‘p’, the compiler will run the derived class’ version of the display ( ) function.

This decision of choosing which function to execute is made at run-time. Hence this is known as run-time polymorphism. In effect you are actually overriding the existing base class display ( ) function. This not overloading because the name of the function, the return data-type and the parameters of the function in the parent as well as the derived class are the same. Another term used is ‘late binding’. In this situation ‘binding’ refers to the decision that has to be made regarding which function should be called (this depends on the type of object pointed to by the pointer and it can be determined only at run-time). This method of using virtual functions helps achieve ‘late binding’ (i.e. the function call is not resolved till the program is run). The opposite of late binding is ‘early binding’ and in this case everything about the function call is known at compile-time itself. All the member functions called through the normal use of the dot operator are examples of early binding. The advantage of early binding is that it will make the program faster (since everything is determined at

345

Inheritance

Page 347: C++ Basics to Advanced

compile-time itself). In ‘late binding’ the decision has to be made at run-time and hence execution of the program could be slower. But ‘late binding’ provides a lot of flexibility. ‘Late binding’ is also sometimes called ‘dynamic binding’ and ‘early binding’ is called ‘static binding’.

What will happen if we don’t redefine a virtual function in the derived class? If you don’t redefine a virtual function in the derived class, the derived class will execute the base class’ function.

Pure Virtual Functions and Abstract Classes

One very useful feature of virtual functions is creating pure virtual functions. When a virtual function is equated to zero, it becomes pure virtual. For example:

#include <iostream.h> class car {

protected: int speed; char color[20]; public: virtual void display( )=0; //Pure virtual Function

}; class ferrari:public car {

public: void display( ) { cout<<"\nThis is a Ferrari"; }

}; class mclaren:public car {

public: void display( ) { cout<<"\nThis is a McLaren"; }

}; int main( ) {

car *p; ferrari f; mclaren m; p=&f; p->display( ); p=&m; p->display( ); return 0;

}

346

Inheritance

Page 348: C++ Basics to Advanced

The output is:

This is a Ferrari This is a McLaren

The advantages of pure virtual functions are:

• You have to redefine the virtual function in your derived class. • You cannot create an object from a class having a pure virtual function. Such classes

are called as abstract classes. In the above example we cannot create an object of the type ‘car’.

• If you don’t define your virtual function in your derived class, then the derived class will also become an abstract class (and you cannot create any objects of this as well).

There are cases where you might not want to create objects belonging to the base class and you might feel that a particular function would make no sense in the base class. In all these cases you can make use of pure virtual functions in the base class.

If you go back to the example of the class ‘bacteria’, it wouldn’t make sense to create a concrete object belonging to the class ‘bacteria’. You will want to create particular strains of bacteria but not general bacteria objects and in this case the bacteria class can be made abstract so that no one can ever create an object of type bacteria (the users of your class can thus only create objects belonging to classes derived from ‘bacteria’).

Implementing Polymorphism

Polymorphism has already been explained in the Chapter on classes. Polymorphism is simply giving different meanings to the same name (in C++ this can be a function or an operator). C++ supports compile time and run-time polymorphism. Polymorphism tends to be associated frequently only with virtual functions (late/dynamic binding). But C++ implements polymorphism in a number of ways. They are through the use of:

• Operator overloading • Function Overloading • Virtual Functions • Templates

We have already seen three of these methods. Templates will be described in Chapter 13. It has to be noted that run-time polymorphism is implemented only through the use of virtual functions. Polymorphism that is implemented through mechanisms other than virtual functions (like operator and function overloading) is also called "ad hoc polymorphism".

347

Inheritance

Page 349: C++ Basics to Advanced

Inheritance- III

The following topics are covered in this section:

• Virtual Destructor • Pointers to base and derived classes

Can a Destructor be Virtual?

We’ve seen about classes and we’ve also dealt with inheritance. But there is one case that we haven’t seen as yet. Let’s go back to the class called ‘car’ and let’s assume that we have one derived class from ‘car’ called ‘ferrari’. If you create an instance of ferrari, then when the ferrari object is destroyed this will invoke 2 destructors (the ‘ferrari’ class destructor and the ‘car’ class destructor).

Suppose we create an object of ‘ferrari’ using the ‘new’ operator and the pointer is of the base class’ type (i.e. the pointer is of type ‘car’). What do you think will happen? Just see the coding below so that you get an idea of the problem:

#include <iostream.h> class car {

private: int speed; char color[20]; public: ~car( ) { cout<<"\nDestroying the Car"; }

}; class ferrari:public car {

public: ferrari( ) { cout<<"Creating a Ferrari"; } ~ferrari( ) { cout<<"\nDestroying the Ferrari"; }

}; int main( ) {

car *p = new ferrari; delete p; return 0;

}

348

Inheritance

Page 350: C++ Basics to Advanced

You can be sure that a new ferrari object has been created. The question is: does the ferrari object really get destroyed?

The output for the above program would be:

Creating a Ferrar Destroying the Car

Surprised? The ferrari object hasn’t been destroyed though the object was created. The problem is that since we are using a pointer to the base class the compiler tends to only invoke the base destructor when you delete it. How to overcome this problem? This is where we have to make the destructor virtual. By making the destructor virtual, the correct destructor will be called in the program. A destructor is just like any other member function; so to make it virtual just precede the base class destructor by the keyword ‘virtual’. Check out this modified program:

class car {

private: int speed; char color[20]; public: virtual ~car( ) { cout<<"\nDestroying the Car"; }

}; class ferrari:public car {

public: ferrari( ) { cout<<"Creating a Ferrari"; } ~ferrari( ) { cout<<"\nDestroying the Ferrari"; }

}; int main( ) {

car *p = new ferrari; delete p; return 0;

} The output would now be:

Creating a Ferrari Destroying the Ferrari Destroying the Car

This is what we call virtual destructor in C++.

349

Inheritance

Page 351: C++ Basics to Advanced

Pointers to base and derived classes

“Every Ferrari is a car but not every car is a Ferrari”.

We’ve seen how to create pointers to objects and how to use them to access member functions. Now the question arises what will happen if we create a pointer to an object of base class and then point to an object of a derived class? What if we do the reverse?

Upcasting: a derived class being treated like the base class (going up the hierarchy).

Downcasting: a base class being treated like a derived class (going down the hierarchy).

Consider the following example:

class car { protected: int speed; public: car( ) { speed=100; } void display( ) { cout<<"\nSpeed of general car is : "<<speed; } }; class ferrari:public car { public: ferrari( ) { speed=200; } void display( ) { cout<<"\nSpeed of ferrari is : "<<speed; } void change( ) { speed=speed + 100; } };

350

Inheritance

Page 352: C++ Basics to Advanced

int main( ) { car *cp; car c; ferrari f; cp=&c; cp->display( ); // Speed of car is : 100 cp=&f; //pointing to derived object cp->display( ); // Speed of car is : 200 cp->change( ); //ERROR – change ( ) not present in class ‘car’ return 0; }

Compiling the code throws an error on the line:

cp->change( );

The reason for this error is that ‘cp’ is a pointer to the base class. The base class ‘car’ does not have a function change ( ) and so even though ‘cp’ is now pointing to a derived object, it cannot call the change ( ) function of the derived class.

Remember: A base class pointer cannot access a member which is not present in the base class (even though it points to a derived type). We’ll take a look at the reason for this once we learn about virtual functions.

The term ‘upcasting’ is used when we consider a derived class object as a base class object (we treat a Ferrari as a car; or a teacher as an employee; or a student as a person etc.). Since we are going up the inheritance hierarchy tree (from derived to base), we call this upcasting. This is a very useful feature because any function that operates on the base class will be capable of operating on the derived class as well. Upcasting is what you will come across when we discuss polymorphism using virtual functions. The drawback is that upcasting can lead to object slicing (we’ll take a look at this later).

The opposite of upcasting is downcasting and it is dangerous. It is dangerous to consider every employee as a teacher or every person as a student because the base class will not have the specializations of the derived class. Downcasting would’ve been acceptable if every person can be considered a student – in that case every person will have properties like student ID number, courses opted for etc. But this isn’t correct because every person is not a student. A person who isn’t a student will not have these attributes. So if we have a member function in class student to display these attributes, then invoking this function on an object of type person will lead to unpredictable results. Thus downcasting is dangerous.

If we comment out the code:

cp->change( );

and then compile and execute the above code, we’ll get an interesting output:

351

Inheritance

Page 353: C++ Basics to Advanced

Speed of general car is : 100 Speed of general car is : 200

What’s wrong? It’s actually the base class’ display( ) function which has been called twice; the derived class’ display( ) function has never been called. If it were called the output should have been:

Speed of general car is : 100 Speed of ferrari is : 200

Thus we can come to the conclusion:

cp=&f; cp->display( );

isn’t performing what we expect. If ‘cp’ is pointing to the ferrari object then it should have used the display( ) function present in the class ferrari!

Another doubt that you might have is: “How is the value of speed correctly displayed in the output?”

Speed of general car is : 100

Speed of general car is : 200

If the pointer ‘cp’ were still pointing to the base class object then it should have displayed:

Speed of general car is : 100

Speed of general car is : 100

But since we have a display of the value 200, it indicates that ‘cp’ does really point to the ferrari object after the statement:

cp=&f;

The only problem is that the wrong display function is being called. The function is wrong but member data is correct!

Two parts to this answer:

1.) The concept of early binding decides what definition to be called – the compiler decides at compile time what function definition to be used. The compiler thinks like this: “I don’t see any instruction for late binding and cp is a pointer to type car. So I shall use the display( ) function defined in the base class car”. More on this in the section on virtual functions that follows.

2.) The data is correct because of the way objects are stored in memory. The relative position of the data ‘speed’ in the base class and derived class is the same; so since we changed the address of cp to ‘f’, the member data access will be that of the derived object.

352

Inheritance

Page 354: C++ Basics to Advanced

Inheritance- IV

The following topics are covered in this section: • Multiple Inheritance • Virtual Base Class

Multiple Inheritance

When we have more than one base class for a derived class, we call it as multiple inheritance. So far we have only seen the case where a derived class has one base class. In this section we will deal with the features and problems associated with multiple inheritance. Some programmers advise against the use of multiple inheritance. In fact Java does not permit multiple inheritance.

For a basic explanation about multiple inheritance, assume that you have created two classes called ‘bacteria’ and ‘virus’ to model the two different life forms. Now, maybe you discover a new life form that has the features of both bacteria and virus. Instead of creating a new class with all the properties again you can simply derive a new class from both bacteria and virus (hence bacteria and virus will be the two base classes).

Let’s call the derived class as ‘crossbreed’. The program is written below:

class bacteria { protected:

int x; public: bacteria( ) { cout<<endl<<"Bacteria constructor"; x=1; } void disp( ) { cout<<endl<<"Value of x for bacteria is : "<<x; }

}; class virus { protected: int x; public: virus( ) { cout<<endl<<"Virus constructor"; x=100; } void disp( ) { cout<<endl<<"Value of x for virus is : "<<x; }};

353

Inheritance

Page 355: C++ Basics to Advanced

class crossbreed:public bacteria, public virus {

public: crossbreed( ) { bacteria::x=10; cout<<endl<<"This is the crossbreed of bacteria and virus"; }` void disp( ) { cout<<endl<<"This is crossbreed disp function."; }

}; int main( ) {

crossbreed bv; bv.bacteria::disp( ); bv.disp( ); return 0;

}

The output will be:

Bacteria constructor Virus constructor This is the crossbreed of bacteria and virus Value of x for bacteria is : 10 This is crossbreed disp function.

The main points to be noted are:

1. Each of the three classes have their own void disp ( ) function. The two base classes have their own protected member data ‘x’. Hence the derived class ‘crossbreed’ will have access to both the member data. To specifically access one of the member data, you have to make use of the scope resolution operator to specify which class’ member data ‘x’ you want to access. For example: bacteria::x=10;

2. Take note of the order of execution of the constructors. The bacteria class constructor is executed first, followed by virus and finally the derived class constructor is executed. Why is it that bacteria constructor executes first before virus? This is because we have specified the class bacteria first in the derived class: class crossbreed:public bacteria, public virus

3. Destructors will be invoked in exactly the reverse order. 4. Both base classes have the function disp ( ) and to call one of them using an object of

the derived class we have to use the scope resolution operator. bv.bacteria::disp( );

5. You can make use of virtual functions to override the base class functions by declaring the disp ( ) function as virtual in both the base classes. Let’s assume that the disp ( ) function in both bacteria and virus class are made virtual. Let’s also assume that we haven’t written a disp ( ) function for the derived class crossbreed. Now if you create an object of type crossbreed and try to execute the disp ( ) function of the crossbreed class, what will happen? If crossbreed were derived from one base class

354

Inheritance

Page 356: C++ Basics to Advanced

then the base class virtual function would have been executed. But in this case there are two base classes and the compiler will produce an error (because it won’t know which base class function to execute). This is one of the problems with multiple inheritance.

Virtual Base Class

There is another problem associated with multiple inheritance. There is a good chance that your derived class can have more than one copy of the base class. Consider three classes as shown in the figure below:

The following diagram is better suited to explain the problem inherent in multiple inheritance.

The problem with the above form of inheritance is that both base1 and base2 will have a copy of the class ‘mainbase’. When the class ‘derived’ is obtained from these two classes, it will end up with two copies of ‘mainbase’. If mainbase had a member data called ‘count’, then ‘derived’ class cannot access the member ‘count’ by saying:

count=1;

The ‘derived’ class has to specify which copy of ‘count’ it wants to access using the scope resolution operator.

base1 base2

derived

mainbase mainbase

355

Inheritance

Page 357: C++ Basics to Advanced

base1::count=1;

Mostly you wouldn’t want such a case and so C++ introduces virtual base class to prevent the ‘derived’ class from obtaining two copies of the same class. You can specify in the derived class that the base classes are ‘virtual’. Check out the example below:

#include <iostream.h> class mainbase {

protected: int count; public: mainbase( ) { count=0; }

}; class base1 : virtual public mainbase { }; class base2 : virtual public mainbase { }; class derived : public base1, public base2 {

public: void set( ) { count=1; cout<<endl<<"The count is : "<<count; }

}; int main( ) {

derived d; d.set( ); return 0;

}

The output is:

The count is : 1

Now there is no problem when referring to the variable ‘count’. Though the two classes ‘base1’ and ‘base2’ are inheriting ‘mainbase’ as virtual, both of them will individually have their own copy of ‘count’. The ‘virtual’ effect occurs only when some other class uses these two classes as its base class. In that case that derived class will have only one copy of ‘mainbase’.

356

Inheritance

Page 358: C++ Basics to Advanced

Alternative Representation of Inheritance:

In all the earlier figures for inheritance we have used a downward pointing arrow to denote inheritance. The following representation is also used for inheritance:

In this case the arrow points from the subclass to the superclass. This also means that the class or RacingCars is inherited (or derived) from the GeneralCars class. Do not get confused with the two methods of representing inheritance.

357

Inheritance

Page 359: C++ Basics to Advanced

Inheritance- V

The following topics are covered in this section:

• Relationships between classes (isa, hasa, is implemented in terms of) • Reinforcing OOP concepts • Recap

Relationships between Classes (Object composition and private inheritance):

Let’s take the following scenario:

• We’ve modeled an engine as a class.

• We’ve modeled a wheel as another class.

• Engine and wheel are not related to each other (that’s quite obvious).

Now we want to model a class called car. Let’s say our first requirement is that a car has an engine. We already have an engine class but how should we use it within the class car? We might be tempted to do the following:

class engine {//engine class specifications };

class car: public engine {//define this class };

Now we have established a relationship between the car and the engine. Next we need to relate wheel with car. After all, a car has wheels. So we might be tempted to do the following:

class car: public engine, public wheel { //define this class };

That seems good. “But wait a minute”, you say. “A car has 4 wheels. How do we represent that in this relationship?” That’s a very good observation and leads us to the fact that our modeling has a major flaw. The way we’ve created the relation between car and engine was wrong in the first place.

Why? Let’s go back to the beginning of this chapter where we said: “A Ferrari is a car”. Then we proceeded to derive Ferrari from a car because a Ferrari is a highly specialized version of a car. Or another example is the class ‘employee’ and ‘accountant’. An accountant is an employee (a specialized employee trained in accounting). The key words are “is a”.

358

Inheritance

Page 360: C++ Basics to Advanced

If we coded:

class accountant: public employee

then it is equivalent to the statement “An accountant is an employee”.

So when we coded:

class car: public engine

this actually means that “a car is an engine!”.

Voila! That’s the mistake; a car isn’t an engine. A car has an engine. Similarly a car has wheels (a car isn’t a wheel).

Sometimes we might be able to get the desired functionality in the wrong way. If our car didn’t have wheels then

class car: public engine

might still be sufficient for us. After all, a car has only one engine. But again, the means of achieving the result is wrong and it can lead to disastrous side effects. It would also mislead anyone who reads your code later (they would assume that a car is an engine).

So, in this scenario public inheritance won’t work. The ‘has a’ relationship is called composition. A car is composed of an engine, 4 wheels, a steering wheel etc. Or in other words, a car has an engine; a car has 4 wheels; a car has a steering wheel and so on.

In C++ this “has a” relationship can be expressed using object containment (or object composition). It’s quite simple:

class car { private: wheel bridgestone[4]; engine honda; };

Well, wasn’t that easy! We are able to model the real world car just as we desired.

Note: The relationship between classes (‘is a’, ‘has a’ etc.) are not something specific to C++. They are part of the Object Oriented principles. So if you happen to read any books/ tutorials on object oriented programming you are bound come across these terms.

In object oriented terminology, we have different forms of composition. They are called composition, association and aggregation. They all have subtle variations but as far as C++ is concerned you needn’t worry about them. The main idea is the concept of whole and part. In our example the car is the whole entity and the parts are the engine, wheel etc. The whole entity is composed of many parts.

359

Inheritance

Page 361: C++ Basics to Advanced

Let’s take an example to illustrate object composition:

class engine { private: //status denotes whether engine is ON or OFF //width specifies one dimension of the engine int status; int width;

public: engine(int wd=5) { status=0; width=wd; cout<<endl<<"Engine created with width:"<<width; }

~engine( ) { cout<<endl<<"Engine destroyed"; }

void start( ) { cout<<endl<<"Starting the engine"; status=1; }

};

class car { private: engine honda; int speed; public: car( ):honda(20) {}

~car( ) { cout<<endl<<"Car destroyed"; }

void start( ) { cout<<endl<<"Starting the car"; honda.start( ); }

};

int main( ) { car mycar; mycar.start( ); return 0; }

360

Inheritance

Page 362: C++ Basics to Advanced

The output is:

Engine created with width:20 Starting the car Starting the engine Car destroyed Engine destroyed

A few points to note in the above example:

1.) car( ):honda(20) {}

To initialize the engine object honda, we’ve made use of an initializer list in the class car. This is one use of an initializer list (the same cannot be implemented in a constructor body).

2.) The constructor and destructor of the class engine are invoked automatically upon creating a car object.

3.) The engine object honda cannot be directly accessed by the user of the class car (because it is lying inside the private region of car). Of course the user can create a stand-alone engine but cannot manipulate the engine present in a car.

In our example, we want to provide an interface for starting the car (not for starting the engine). If we permitted that (by making engine honda public), then the user can start the engine and then start the car (which is not what we want). Thus:

mycar.honda.start( ); //is now illegal mycar.start( ); //is the only way to start the car (and engine)

If you remember, there were 3 relationships listed in the beginning of this chapter:

1. is a

2. has a

3. is implemented in terms of

The first 2 relations should be clear to you by now. We’ve encountered the following statement a couple of times already:

• Every Ferrari is a car (but not every car is a Ferrari).

Some similar statements would be:

• Every teacher is an employee (but not every employee need be a teacher).

• Every student is a person (but not every person is a student)

The words “is a” are important in these statements. When we establish an “is a” relationship between 2 objects it means that we should inherit from the base class using the public access specifier.

361

Inheritance

Page 363: C++ Basics to Advanced

Ex: class employee { //class definition };

class teacher: public employee { //class definition };

According to the above relationship, whatever an employee can do a teacher can also do (that is the basis of an ‘is-a’ relationship). Or as far as the programming language is concerned, a function which takes an employee as argument can take a teacher as argument (but a function taking a teacher as argument can’t take an employee as argument). Every teacher is an employee but not every employee is a teacher. Thus public inheritance helps incorporate the ‘is-a’ relationship in C++.

We also know that instead of public inheritance, we can inherit another class as private or protected (called private inheritance and protected inheritance).

So, what does the following mean?

class employee { //class definition }; class teacher: private employee { //class definition };

Going back to our example about an engine and a car, we decided not to use public inheritance because it gave the meaning: a car is an engine. But what about private/protected inheritance?

The following examples illustrate some of the difference between public and private inheritance:

1.) A room isn’t a door.

class door {};

class room:private door {};

void common_func(door d) {}

int main( ) { door d1; room r1;

362

Inheritance

Page 364: C++ Basics to Advanced

common_func(d1); common_func(r1); return 0; }

If the derived class were inherited publicly then the function call:

common_func(r1);

will work fine because of the “is a” relationship. But in our program, the compiler complains:

conversion from 'class room *' to 'const class door &' exists, but is inaccessible

When we use private inheritance, everything in the base class becomes private in the derived class and so the conversion from derived to base isn’t possible.

In an “is a” relationship, any function which operates on the base class can operate on the derived class (or in other words whatever a car can do, a Ferrari can also do). But in private inheritance this doesn’t hold true.

2.) Another difference is that the public members of the base class now become private in the derived class. Now a user cannot open the door twice!

class door { public: void open( ) { cout<<endl<<"Door opened"; } };

class room:public door //Public inheritance { public: void open( ) { door::open( ); cout<<endl<<"Room opened"; } };

int main( ) { room r1; r1.open( ); r1.door::open( ); //permitted. Not an error return 0; }

The output is:

Door opened Room opened Door opened

363

Inheritance

Page 365: C++ Basics to Advanced

Actually, when the room is opened it implies that the door has been opened. And this is why the function open( ) in the class ‘room’ has been defined as:

void open( ) { door::open( ); cout<<endl<<"Room opened"; }

Internally the open( ) function of class ‘door’ is called.

But with public inheritance the user isn’t prevented from doing the following:

r1.open( ); r1.door::open( ); //permitted. Not an error

The user opened the room (which in turn led to opening of the door) and then the user again opened the door explicitly. But we wouldn’t want this to happen. If private inheritance were used, this would cause a compile-time error; since door::open( ) would then be a private function and private functions cannot be directly accessed by objects.

This leads us to the thought that private inheritance is similar to object composition. Some of their features are:

• In both cases we’ll have only one instance of the other object (i.e. a room will have only one door and a car will have only one engine).

• But in object composition we can create a room with multiple doors (this is not possible in private inheritance)

• In both cases we can prevent the user from peforming actions such as ‘opening the door twice, starting the engine twice etc’.

• In both cases we don’t establish the “is-a” relationship.

There is another significant difference between private inheritance and object composition. In private inheritance the derived class can access all the protected members of the base class.

Object Composition (ERROR) Private Inheritance (permitted)

class door { protected: void shut( ) { cout<<endl<<"Door closed"; } }; class room { private: door d1;

class door { protected: void shut( ) { cout<<endl<<"Door closed"; } }; class room:private door { public: void close( ) {

364

Inheritance

Page 366: C++ Basics to Advanced

public: void close( ) { cout<<endl<<"Closing the room"; d1.shut( ); } };

cout<<endl<<"Closing the room"; door::shut( ); } };

When we inherit a class privately, the protected members (data and functions) of the base class become private in the derived class. The public functions of the derived class can invoke these functions (which were inherited from the base class).

But in the case of object composition, this is not possible. The class ‘room’, can only access the public parts of class ‘door’.

The private inheritance relationship is termed “is implemented in terms of”. Protected inheritance is similar to private inheritance except that another derived class will inherit the protected members of this class.

Ex: class base {};

class derived1:protected base {};

class derived2:private derived1 {};

The class ‘derived1’ can access the protected members of the class ‘base’. The class ‘derived2’ can access the protected members of class derived1. But any class derived from ‘derived2’ will not be able to access the protected members inherited from ‘derived1’ (because derived2 privately inherited derived1).

Reinforcing the OOP concepts:

Let’s just take a quick look at the various OOP concepts in brief.

Whenever you are faced with a problem (to develop an application) you should first identify the problem. The necessary details have to be separated out from the unnecessary details. The abstract model (which is a class in C++) has to be modeled based on the relevant details. Let us suppose that someone puts forth the following requirement:

“I want to have a program to keep track of all the cars I own. I’m interested in knowing the colour and top speed of the car…”

In real life cars have many properties but in this particular problem we are only concerned with two properties: top speed and colour. These are the relevant details (relevant for our requirement) which are required to construct an abstract model. For some other problem it may be necessary to include other properties like type of fuel, dimensions etc. Abstraction is a way of looking at the same real-life entity from different perspectives. Depending on the requirements we create our own abstract view of the problem.

365

Inheritance

Page 367: C++ Basics to Advanced

Encapsulation means hiding implementation details while providing a consistent interface to the user. In other words, no matter how you modify the implementation your interface should remain the same (i.e. the user will be unaffected by changes in the implementation). It’s similar to our telephone connection. The interface is the socket where we simply plug in the telephone jack and the implementation is the media carrying our voice to the telephone exchange (the implementation is hidden from us. As users we wouldn’t know if the media were changed from copper cables to fibre optic cables. Such changes do not affect the user since the user will still need to simply plug the jack into the telephone socket).

Inheritance is the process of establishing relationships between classes. It helps in reuse of code rather than rewriting the same code. The 3 major relationships are:

• Is-a • Is-implemented-in-terms-of • Has-a

A fourth relation termed ‘a-kind-of’ is basically an ‘is-a’ relationship. If you really want to distinguish between the two, we could say that ‘is-a-kind-of’ relationship is at the class level while ‘is-a’ relationship is at the object level.

In C++ we have the option of deciding how we want to inherit the parent (as public, private or protected). When a relationship is of the type ‘is-a’, we will inherit the parent class using the ‘public’ access specifier. In this case the relationship should be such that whatever action can be performed on the base class is permissible on the subclass also.

In other cases you will have to inherit either as ‘private’ or ‘protected’ (depending on whether the current subclass will be further used as a parent class or not) - this represents the ‘is-implemented-in-terms-of’ relationship.

Object composition represents the ‘has-a’ relationship.

Recap

• Inheritance: This is the property by which a derived class can inherit features of a parent class.

• Parent class: This is also referred to as the ‘base class’ or the ‘super class’. • Derived class: This is also called as the ‘child class’ or the ‘subclass’. • Inheritance helps in reuse of code (instead of rewriting existing code). • The ‘protected’ access-specifier (used in inheritance) is less restrictive than ‘private’

but more restrictive than ‘public’. • A virtual function is a member function of the base class but it is redefined in the

derived classes. • Run-time polymorphism is implemented through the use of virtual functions. • A virtual function that is equate to zero is a pure virtual function. • An abstract class will have at least one pure virtual function. • You cannot create objects of an abstract class. • When a base class pointer is used to access a derived object, you can make use of

virtual destructors to invoke the correct destructor. • Multiple Inheritance: If a class inherits from more than one parent class then we refer

to this as multiple inheritance (or MI). • Virtual base classes are used in multiple inheritance to prevent the derived class from

having more than one copy of a base class.

366

Inheritance

Page 368: C++ Basics to Advanced

More Polymorphism (Poly polymorphism!)

Students generally aren’t able to appreciate the use of polymorphism in programming. So let’s revisit this topic. We’ll start with the problem, assuming that we don’t have inheritance in C++:

Let’s say that we are creating a game similar to Microsoft’s Age of Empires. The concept of the game is that a player should build an empire and destroy all other empires. An empire consists of different characters/units - villagers, archers, horsemen, cavalry etc. Villagers are needed to collect resources and resources (like food, wood etc.) are needed to create units. Any game will run within a window (a gaming window - i.e. the entire game program will run within this window).

Our requirement is that if at any point of time, the player minimizes the gaming window, then the game should pause, the window should minimize into the taskbar, the desktop should appear and the user can work on other things. To resume the game, the user can click on the taskbar. On clicking, the gaming window should maximize and all the characters which were present at the time of minimizing should be redrawn on the screen.

Initially while developing the game we decide to work with only two types of characters: villagers and archers. So we would design two classes to represent them. In this section we shall concentrate on our requirement (which is redrawing the characters on the screen as they were at the time of minimizing the window; let’s ignore other aspects of the game). We should provide a draw( ) member function in each of these classes (for drawing the villager or archer on screen; the draw( ) function varies in the two classes because villagers and archers are represented differently in our game).

Every villager and archer created in the course of the game has to be contained within the gaming window (the player can just click on a villager icon to create a new villager and on an archer icon to create a new archer). If the player creates 10 villagers then we should draw these 10 villagers on the screen. The draw( ) function will serve this purpose.

Our gaming window will be another class (all objects created in the game, have to be placed within this gaming window; the window will have properties like height, width etc.; the window can be minimized, restored and closed - these would be some of the member functions of this window class).

Our window class also needs to keep track of the various villager and archer objects created. Thus to store these objects, we can create an array to hold villagers and another one to hold archers (in reality we won’t use arrays since they are of a fixed size and we can’t predict how many villagers or archers would be created. We’ll take a look at different data structures in a later chapter but for the time being let’s assume we use arrays for this purpose).

The reason we need to keep track of the objects created is simple; when the restore( ) function is invoked (it gets invoked when the user restores the gaming screen after it has been minimized), all the objects present in the window need to be redrawn. When the gaming window is minimized and then restored it, we would need to redraw the villagers again. If there were 5 archers then these would also have to be redrawn within the gaming window.

367

Inheritance

Page 369: C++ Basics to Advanced

So each time a new villager or archer is created we’ll store the address of the new object in our villager or archer array (i.e. we’ll store a pointer; we can also have an array to store the archer and villager objects itself but this would lead to presence of multiple copies of our object in memory- so instead of the object we store the address of the object in the arrays). In our restore( ) function we will cycle through each of these two arrays; in each cycle we’ll retrieve the address of the object and invoke the draw( ) function for that object. In this way we’d redraw all the characters which were present on the screen just before the player minimized the window.

Note: The villager and archer class would have many other properties (like location: x and y coordinates, which will be required to draw the object at the correct position) but to keep the example simple we won’t consider them.

Our restore function might be something like this:

void restore( ) {

Perform till end of villager array { varr[i]->draw( ); }

Perform till end of archer array { aarr[i]->draw( ); }

}

where ‘varr’ is the array containing the addresses of all the villager objects and ‘aarr’ is the array containing the addresses of all the archer objects (instead of the address we could also store the entire object in the array but this would consume a lot of space).

To populate the villager and archer array we’ll need to have another two functions in the window class:

add_archer(archer *); add_villager(villager *);

368

Inheritance

Page 370: C++ Basics to Advanced

Every time a new villager or archer is created we’ll call the appropriate function to populate the ‘varr’ or ‘aarr’ arrays.

The program flow seems satisfactory:

1. User can create archers or villagers. 2. If an archer is created then the function add_villager is called to add the address of the

new villager object to the array ‘varr’. 3. Similarly if a villager is created then add_archer is called and ‘aarr’ is populated. 4. In both cases their respective draw( ) functions are called to draw the character on the

screen. 5. If the player minimizes the window and then restores it, the restore( ) function of the

class ‘window’ would be called. This in turn will cycle through both arrays and call the appropriate draw( ) functions.

Everything is fine except for the problems we’ll land into later.

1.) Both archers and villagers have some properties in common (example: life). We are replicating the same chunk of code repeatedly.

2.) If we were to add a new character, say swordsmen, then we need to create another array to hold all our swordsmen. We would also need to modify the restore ( ) function in our window class to cycle through this new array. Imagine the number of arrays required later when we introduce ten new characters in the game!

Ah, the first problem is simple to solve. We need to use inheritance. An archer is a person and a villager is also a person. And every person has an attribute ‘life’; fairly simple.

So we convert our initial design into the following:

But what about our second problem?

One option might seem to be something on the following line of thought:

Declare a dummy draw( ) function in the class ‘person’; use a single array called ‘parr’ to store villager and archer object addresses and invoke the draw( ) function.

369

Inheritance

Page 371: C++ Basics to Advanced

//the base class person

class person { protected: int life; };

//The window class member functions restore( ) and add( ) would be:

void restore( ) { Perform till end of person array { parr[i]->draw( ); } }

void add_person(person *p) { add ‘p’ to parr }

//In the main( ) function we would add archers and villagers to parr

window gaming_window; archer a1; gaming_window.add_person(&a1); villager v1; gaming_window.add_person(&v1); gaming_window.restore( );

The statement:

gaming_window.add_person(&a1);

is correct. The add_person( ) function requires address of a person but we are passing the address of an archer. Since an archer is a type of person (relationship established through inheritance), the function call is correct.

Try compiling the code and you would get a compile time error flagging the statement:

parr[i]->draw( );

The problem is that parr is an array supposed to hold the address of person objects. A villager is a person and so ‘parr’ can hold this type of an object. But the class person doesn’t have a member function draw( ). Obviously a base class cannot be expected to know the functions present in the derived classes and so the compiler complains saying “person class does not have a member function draw( )”.

370

Inheritance

Page 372: C++ Basics to Advanced

To correct this you might modify the person class as:

class person { protected: int life;

public: void draw( ) {cout<<"a person";} };

Now you won’t get a compile error and the code will run. But no matter what type of character you create, the call:

parr[i]->draw( ); will always print: Drawing a person.

Villagers and archers will never appear! Why? This is because we never specified that the code should examine the type of object and then call the appropriate draw( ) function. As far as our program is concerned, it sees parr as an array holding address of persons and thus when we try to call a member function through this address it will only call the member function present in class person (and not the ones present in the derived class).

So now we need to figure out a way to examine what sort of a object we’re holding in ‘parr’ and then call the correct darw( ) function. To examine the type of object, we need to store some information in the class which will indicate the object type. An extra member data would serve the purpose well. Our person class could be defined as:

class person { protected: int life;

public: int obj_type; void draw( ) {cout<<"a person";} };

The member data ‘obj_type’ will hold different values depending on whether the object is an archer or a villager. For example we might decide that an obj_type of 1 denotes a villager and value of 2 denotes an archer.

371

Inheritance

Page 373: C++ Basics to Advanced

The class villager would now be:

class villager:public person { public: villager(int x=100) { obj_type=1; life=x; } void draw( ) {cout<<"\nDrawing villager:"<<life;} };

Every villager created would have obj_type as 1. Similarly in our archer class we would code: obj_type=2 in the constructor. Thus if at any point of time we want to check whether an object is of type archer or villager, we will simply check the value of obj_type.

Our modified restore( ) function in the window class would be as below. Now we’ve resolved the problem of determining what type of object address we have stored in our ‘parr’ array.

void restore( ) { Fetch object addresses stored in ‘parr’ array until end of array is reached { switch(parr[i]->obj_type) //check if it’s a villager or archer { case 1: ((villager*)parr[i])->draw( ); break;

case 2: ((archer*)parr[i])->draw( ); break; } } }

void add_person(person *p) { add ‘p’ to parr }

To ensure that the right version of draw( ) is called we perform casting (i.e. forcing the compiler to treat a pointer to a person as a pointer to a villager/archer). If obj_type is 1 then we force the pointer parr[i] to act like a pointer to a villager. Thus when we now call draw( ) using this pointer, we are actually telling the compiler to call the draw( ) member function of the class ‘villager’.

But the drawbacks of this method are quite obvious. Casting itself is not a practice encouraged in programming (because by casting we are attempting to modify the type of an object temporarily and are skipping the type-checking operation performed by the compiler). It also clutters the code with lots of parentheses making the code appear complicated as well. And the biggest problem occurs when we want to add new characters to our game. If a swordsman has to be introduced then:

372

Inheritance

Page 374: C++ Basics to Advanced

• all objects of type swordsman should have a particular obj_type (maybe value 3).

• We should tamper with the restore function in the window class and we will introduce more casting in the restore( ) function.

• If there are other places where we examine obj_type then we should ensure that we modify the code in all these places to handle obj_type of 3.

Obviously we need some cleaner method of achieving this functionality. You might have noticed something interesting, “We are expecting our objects to respond differently to the same message!” The message we send is draw( ) and the drawing of villagers and archers are different.

Our problems would be solved by run-time polymorphism using virtual functions. By using the keyword virtual the compiler puts that extra bit of code so that the type of the object is examined at run-time and the correct implementation is of draw( ) is called. We needn’t worry about distinguishing between different types of objects and we needn’t use an extra member data like ‘obj_type’. We also get to avoid all the casting complications because now the compiler will insert its own code so that the type of the object is determined at run-time and the correct version of the draw( ) function is called. Now we also needn’t worry about new characters being added into the game. Even if we were to introduce our swordsman, he would be derived from person (a swordsman is also a person) and thus the code of the window class needn’t be tampered with. Our code in restore( ) function will be:

parr[i]->draw( );

373

Inheritance

Page 375: C++ Basics to Advanced

Streams and Files - Intro

The following topics are covered in this section:

• Intro to streams • Overloading << and >> • Predefined Stream Objects

Introduction to Streams

You might have heard of I/O. It stands for Input-Output. Before dealing directly with files we shall first take a look at streams. Using C++ you can access a device through an intermediate medium. The intermediate medium is called a stream. The device that you access is known as file.

Don't mistake the term file as the normal computer file. A normal file (a text file or a word document) is referred to as disk file. When dealing with streams, the term file takes a broader meaning. It includes disk files, tape drives, the terminal, printers and other devices. C++ is designed to interact with these devices and this can be accomplished using streams.

Each device is unique in its own way. The C++ file system transforms each device into a stream. All streams will behave similarly though they provide access to different devices (or files). Hence streams are mostly device-independent. The function used to write to a disk file can be used to write to the monitor screen as well. Hence all streams are similar but all files are not.

Each stream is associated with a specific file by using the open operation. Once you open a file (not just disk file – it could even be a printer), information can be exchanged between the file and your C++ program. This exchange is done through the medium of streams. A stream is an object and there are different kinds of general I/O streams:

• Input stream (belongs to class a class called ‘istream’) – cin is an object of this stream and handles input.

• Output stream (from the class called ‘ostream’) – cout is an object of this stream and handles output.

• Stream for both input and output (obtained from the class called ‘iostream’)- handles both input and output.

These are general I/O streams and they are not specifically for disk file operations. We shall deal with file I/O later.

The iostream class has two overloaded operators:

• >> (extractor) • << (inserter)

These operators (which are actually bitwise shifting operators) are already overloaded to work with fundamental data types and a programmer can also overload them to work with objects (user-defined data).

374

Files & Streams - C++

Page 376: C++ Basics to Advanced

Overloading insertion and extraction operators

The insertion (or output operator <<) and extraction operators (or input operator >>) are binary operators that are already overloaded to operate on built-in data types. The insertion operator will insert data into a stream while the extraction operator is used to extract data from a particular stream. Suppose we had a class ‘car’ and we created an object called ‘ford’, wouldn’t it be wonderful if we could just type:

cout<<ford;

and the program should display the values of the object ford. Of course we can make use of a member function to display this (in fact that is what we’ve been doing so far) but you can overload the << operator to perform such operations.

If you notice the statement, one operand is an object and one operand is a stream. ‘cout’ is also an object but it won’t be belonging to the class that we create. The << will be overloaded in our class but since it is in the right hand side, it won’t be able to invoke the operator overloading function. This problem can be solved by using friend functions for overloading the << and >> operators. We have seen this in the chapter on operator overloading but since we are dealing with streams, one of the arguments to the friend function should be a stream.

#include <iostream.h> class car {

private: int speed; char color[20]; public: void set( ) { cout<<"\nEnter the speed: "; cin>>speed; cout<<"\nEnter the colour: "; cin>>color; } friend ostream& operator<<(ostream&, car&);

}; ostream& operator<<(ostream &stream, car &ob) {

cout<<"\nThe speed is: "; stream<<ob.speed; cout<<"\nThe colour is: "; stream<<ob.color; return stream;

} int main( ) {

car ford; ford.set( ); cout<<ford; //overloaded operator function is invoked. return 0;

}

375

Files & Streams - C++

Page 377: C++ Basics to Advanced

The output from the above program will be:

Enter the speed: 280 Enter the colour: red The speed is: 280 The color is: red

You just have to remember that you will need to use a stream as one of the parameters in the operator overloading function. By the way, cout is an object of type ‘ostream’. ‘cin’ is an object of type ‘istream’. In the above program you could write:

stream<<"\nThe speed is: "<<ob.speed;

instead of:

cout<<"\nThe speed is: "; stream<<ob.speed;

because here ‘stream’ is going to correspond to ‘cout’.

Wouldn’t it be even more wonderful if we could say:

cin>>ford;

instead of creating a member function called ‘set’ for this purpose? This can be achieved by overloading the input operator >> as shown below:

#include <iostream.h> class car {

private: int speed; char color[20]; public: void set( ) { cout<<"\nEnter the speed: "; cin>>speed; cout<<"\nEnter the colour: "; cin>>color; } friend ostream& operator<<(ostream&,car&); friend istream& operator>>(istream&,car&);

}; ostream& operator<<(ostream &stream,car &ob) {

cout<<"\nThe speed is: "; stream<<ob.speed; cout<<"\nThe colour is: "; stream<<ob.color; return stream;

376

Files & Streams - C++

Page 378: C++ Basics to Advanced

} istream& operator>>(istream &str,car &x) {

cout<<"\nEnter the speed: "; str>>x.speed; cout<<"\nEnter the colour: "; str>>x.color; return str;

} int main( ) {

car ford; cin>>ford; cout<<ford; return 0;

}

The output from the above program will be:

Enter the speed: 300 Enter the colour: blue The speed is: 300 The colour is: blue

As you would have noticed, in the above program we have overloaded the insertion as well as extraction operator. In this way you can make use of >> and << to work on your objects.

Reference: There exists a bug in VC++ compiler when using friend functions to overload the << and >> operators. Refer to question 9 in Chapter 14 for details.

Pre-defined Stream objects

In C++ there are 4 streams which are already defined. These streams are opened when you run a C++ program. They are:

Stream Name Used for Linked to

cin Standard input Keyboard

cout Standard ouput Monitor

cerr Standard error Monitor

clog Buffered error display Monitor

• cin- is an object of the class ‘istream’ and is connected to the standard input device (which is usually the keyboard).

377

Files & Streams - C++

Page 379: C++ Basics to Advanced

• cout- is an object of the class ‘ostream’ and is connected to the standard output device (which is usually the monitor).

• cerr- is an object of type ‘ostream’ and is connected to the standard error device. Output through ‘cerr’ is unbuffered (i.e. the output will appear immediately on the screen). It is used to inform the user about some error that has occurred.

• clog- is similar to the ‘cerr’ object but ‘clog’ is buffered (i.e. output will be held in the buffer till the buffer becomes full or till the buffer is flushed).

Buffering and flushing will be dealt with in the next chapter.

The standard streams described above can be redirected to other devices or files. The ‘cerr’ object is similar to the ‘cout’ object but the difference is that even if ‘cerr’ is redirected by the user to some other device the error message will be displayed on the console (i.e. on the monitor screen).

378

Files & Streams - C++

Page 380: C++ Basics to Advanced

Streams and Files - II

The following topics are covered in this section:

• Stream Status • Reading stream status • Opening files • Closing files • Errors while opening

Stream Status

To determine the present state of a stream, there are four bits (or flags) associated with every stream. They are:

• ios::goodbit • ios::eofbit • ios::failbit • ios::badbit

Stream Status Flag Purpose

badbit Set when a fatal error occurs.

eofbit Set when the end of the stream is encountered (if stream relates to a file then it is set when end of file encountered)

failbit Set when non-fatal error occurs (for example when invalid data is stored)

goodbit It is set if there are no errors.

Let’s see a very simple illustration of how you can make use of these bits. Let us create a class called ‘date’ which will be used to obtain the date from the user in the following pattern: DD-MM-YEAR

To achieve this we shall overload the >> operator.

#include <iostream.h> class date {

private: int day,month,year; public: date( ) //constructor {

379

Files & Streams - C++

Page 381: C++ Basics to Advanced

day=month=year=0; } void disp( ) { cout<<endl<<"The date is : "; cout<<day<<"-"<<month<<"-"<<year; } friend istream& operator>>(istream&, date&);

}; istream& operator>>(istream &istr, date &dt) {

char dash; cout<<endl<<"Enter the date (separated by - ): "; istr>>dt.day; istr>>dash; if (dash != '-') { istr.setstate(ios::failbit); //setting stream status } istr>>dt.month; istr>>dash; if (dash != '-') { istr.setstate(ios::failbit); //setting stream status } istr>>dt.year; if (istr.fail( )) //checking stream status { cout<<endl<<"Error in the format of the date entered!"; } return istr;

} int main( ) {

date d; cin>>d; d.disp( ); return 0;

}

The output if the input is typed correctly is:

Enter the date (separated by - ): 11-10-1981 The date is : 11-10-1981

The output if the input is typed incorrectly is as follows:

Enter the date (separated by - ): 11/10-1980 Error in the format of the date entered! The date is : 11-0-0

380

Files & Streams - C++

Page 382: C++ Basics to Advanced

The function used to set the ‘failbit’ is: setstate( )

(Some compilers might not support this function). We have set the ‘failbit’ when the separator used by the user is something other than the ‘-’ character. As soon as the ‘failbit’ is set all further stream operations are ignored till the error is corrected (which is the reason why even ‘1980’ is not accepted by the program). The function:

istr.fail( )

is used to read the status of a stream. It will be discussed in the next section.

Reading the status of a stream:

We have learnt about the stream status flags and we have even discussed as to how the flags could be set. To identify which status bit has been set we can make use of corresponding member functions.

Function Stream Status Flag checked

int bad ( ) badbit

int eof ( ) eofbit

int fail ( ) failbit

int good ( ) goodbit

‘cin’ is a stream and even its ‘failbit’ can get set as shown in the program below:

#include <iostream.h> int main( ) {

short int num; cout<<"Enter a short integer : "; cin>>num; if (cin.fail( )) { cout<<endl<<"Error in input"; cout<<endl<<"The number stored is :"<<num; } else { cout<<endl<< "Valid value entered."; } return 0;

}

381

Files & Streams - C++

Page 383: C++ Basics to Advanced

The output for a value that exceeds the short integer range will be:

Enter a short integer : 56432 Error in input The number stored is :32767

The fail ( ) function will return a non-zero value if the number entered by the user exceeds the limit of a short integer. If this is the case then the ‘if’ condition is satisfied and the loop is executed.

Usually we will not be using the stream status flags as shown in the above case. The same concept will be applied for files (because files are also accessed via streams and all the points discussed here are relevant for files).

Remember: Once any of the error bits are set, they will continue to remain set till they are cleared.

For example:

#include <iostream.h> int main( ) {

short int num; int num2; cout<<"Enter a short integer : "; cin>>num; if ( cin.fail( ) ) { cout<<endl<<"Error in input"; cout<<endl<<"The number stored is :"<<num; } cout<<endl<<endl<<"Enter an integer:"; cin>>num2; cout<<endl<<"The failbit value is : "<<cin.fail( ); return 0;

}

The output if a proper value is entered is:

Enter a short integer : 20 Enter an integer:24 The failbit value is : 0

The output for a higher value is:

Enter a short integer : 45345 Error in input The number stored is :32767 Enter an integer: The failbit value is : 2

382

Files & Streams - C++

Page 384: C++ Basics to Advanced

In the second case, the ‘failbit’ for the stream ‘cin’ is set to 2 (because a very high number was entered initially). This bit hasn’t been reset and thus ‘cin’ cannot be used to obtain the value for the second integer (the program will not allow the user to enter a value because the ‘cin’ stream has an error).

The modified program will be:

#include <iostream.h> int main( ) {

short int num; int num2; cout<<"Enter a short integer : "; cin>>num; if ( cin.fail( ) ) { cout<<endl<<"Error in input"; cout<<endl<<"The number stored is :"<<num; } cin.clear( ); cout<<endl<<"All error flags cleared."; cout<<endl<<endl<<"Enter an integer:"; cin>>num2; cout<<endl<<"The failbit value is : "<<cin.fail( ); return 0;

}

The output is:

Enter a short integer : 1232345 Error in input The number stored is :32767 All error flags cleared. Enter an integer:2 The failbit value is : 0

If no argument is specified for clear ( ) function then all the error bits are cleared. After the clear ( ) fucntion is executed, good ( ) function will return a TRUE value (because there are no errors now). Suppose you want to set the ‘failbit’ and reset the other bits, you can type:

stream-name.clear(ios::failbit);

This will reset all the error bits and will set the ‘failbit’. Similarly you can set other bits also.

Opening and Closing a ‘File’

So far we have dealt with standard I/O. Next we shall deal with file I/O and to access any device you have to make use of another header file: ‘fstream.h’. This header file has a number of classes already defined. To access a file you have to have a stream. We have already come across the classes istream, ostream and iostream. From these classes, 3 more

383

Files & Streams - C++

Page 385: C++ Basics to Advanced

classes are derived (they are ofstream, ifstream, and fstream) and these classes are specifically useful for streams used in ‘file’ operations. The hierarchy of classes will be as shown in the figure.

As can be seen, the ‘ifstream’, ‘ofstream’ and ‘fstream’ classes are derived from the ‘istream’, ‘ostream’ and ‘iostream’ classes. These are file streams that are derived from the general I/O streams that we have seen earlier.

ifstream in; // file stream named ‘in’ created for handling input ofstream out; // file stream named ‘out’ created for handling output fstream both; // file stream named ‘both’- can handle both input and output

Once you've created a stream, you can use the open ( ) function to associate it to a ‘file’. To associate the stream to a disk file, we should specify the name of the disk file. The open ( ) function is a member available in all the three classes. It can be used as follows:

out.open("text.txt") ; // Opens a file called text.txt for output.

Remember: When you say that a file is opened for output, it actually means that now you can write data to the file. When a file is opened for input (i.e. using ifstream), the data in the file can be displayed on the screen.

What if there already is a file called text.txt. When using the output stream (ofstream), the stream will create a new file text.txt. Whatever content was there in the original text.txt gets deleted and you can write new data to text.txt.

The 3 classes (ofstream, ifstream, fstream) have a constructor function that makes it easier to open a file. Example:

ofstream out("text.txt");

This creates an object out for output and opens the file text.txt in one single statement. Here we don’t make use of the open ( ) function.

384

Files & Streams - C++

Page 386: C++ Basics to Advanced

Closing a File

Anything that you open has to be closed. The member function for closing is close ( ). Since you do all I/O through the stream, you have to close the stream as follows:

stream-name.close( );

Actually you can link this back to the object and classes concept. ‘stream-name’ is an object and close is a member function of the ofstream class. Hence by saying

stream-name.close( );

you're actually invoking the member function close( ). A stream is associated to a device when using the open function. When the close ( ) function is used, the stream is disassociated from the device.

Can Errors occur while opening a file?

When you open a file using ofstream (which means for output), you can write data to the file. You could say it's an input to the file. Hence open for output means actually for input.

When a file is opened for reading, you will make use of the ifstream as follows:

ifstream instr("test.txt"); // The file test.txt is opened for reading

Beware: Many beginners confuse between the use of ‘ifstream’ and ‘ofstream’ objects. Be clear as to what you want to use.

When you want to read a file, it implies that the file is already present in your directory. What will happen if we attempt to open a file that is not present?

This will cause an error and you should provide the necessary coding to deal with this situation. When you open a file for writing data, the stream will create the file even if it doesn't exist. But if you attempt to open a file for reading data and if it isn't present in the computer, this will cause an error. You should always check whether an error has resulted while using the open operation as follows (‘instr’ is an object of type ifstream):

if ( ! instr ) { cout<< "The file cannot be opened"; }

if ( ! instr ) stands for : ‘if not instr’ (that means ‘if instr not open’) then do what is said in the body of the if statement.

385

Files & Streams - C++

Page 387: C++ Basics to Advanced

Streams and Files - III

The following topics are covered in this section:

• Modes of opening files • Binary Files • Writing to files • Reading from files • Accessing other devices • Using get( ) and put( )

Modes of Opening Files

When you open a file (for writing or reading), there are 6 modes that you can use to specify how you want the file to be opened. The usually used modes are:

ios::app ios::ate ios::binary ios::in ios::out ios::trunc

• Including ios::app causes all the output (or whatever you write) to be appended to the end of the file. This mode can be used only for files opened for output.

• ios::in specifies that the file is capable of input only. ios::out specifies that the file is capable of output only.

• ios::binary causes the file to be opened in binary mode. • ios::trunc causes contents of pre-existing file by same name to be destroyed.

So far we've seen the file opening as follows:

ofstream out; out.open("test"); // This means test is opened in normal mode.

By default this is the same as

out.open("test", ios::out); // opened specifically for output We can combine two modes using the bitwise OR operator:

out.open("test", ios::out | ios::binary );

This will open a stream in output and in binary mode.

The different methods for opening a file are summarized in the table below:

386

Files & Streams - C++

Page 388: C++ Basics to Advanced

Opening Mode Function

ios::in File opened in input mode (for ifstream objects)

ios::out File opened in output mode (for ofstream objects)

ios::app Append to the file. All output operations (like writing to file) are done at the end of the file.

ios::trunc Delete file if it exists and create a new file.

ios::ate Open file and go to the end of the file.

ios::binary Open file in binary mode (by default files are opened in text mode).

ios::nocreate Only if file exists will it open

ios::noreplace The file should not exist (otherwise open fails).

An example is shown below:

#include <iostream.h> #include <fstream.h>

int main( ) { ifstream istr("test.txt",ios::in| ios::nocreate); //error if file not present if (istr.fail( ) ) { cout<<"failed to open - no such file"; } return 0; }

The fail( ) function is inherited by the ofstream, fstream and ifstream classes. Hence we can check whether the open( ) operation was successful or not using this function.

‘fstream’ objects can be used to operate in either input or output modes.

387

Files & Streams - C++

Page 389: C++ Basics to Advanced

What are Binary Files?

Reading and writing formatted text is easy and is always used for displaying data to the user. But sometimes you may need to work with unformatted data (i.e. in binary or raw form) and not as text. When we refer to formatted text it means that the information has been processed to make the output understandable (like using the ASCII table for conversion). If you want to perform binary operations on a file, you should open it in binary mode. If a file is opened in text mode (or normal mode) and you make use of these functions (that are intended for binary mode), the functions will work but some character translations will occur. This might seem to be really confusing.

Basically there are two types of files: binary files and text files. Binary files can contain any data representation format. Text files contain only alphanumeric data as represented by the compiler's character set (for example ASCII). By default, all files are opened in text mode unless you specify ios::binary. When in text mode, the file is considered as containing a number of lines each ending with a "\n". As a binary file, it is considered simply as a sequence of bytes. Unless, the OS maintains different file formats for a binary file and for a text file, these modes of opening are irrelevant. But is always advisable to use the binary mode while opening files (even text files). An example of how character translation can occur is while using the ‘\n’ (newline character). In a binary file this will be stored as a single character. If the OS distinguishes between text and binary files, then when you store a ‘\n’ in a text file it will be stored as two characters. Certain functions used in file I/O will work only in binary files; so it is better to always work on binary files.

Another simpler example is the case of storing numbers in a file. Let’s say we have:

int x = 124;

If you write ‘x’ to a file in text mode then the program will convert 124 into it’s equivalent ASCII form (i.e. it will take each digit separately, convert the digit to the ASCII value and then store the ASCII value in the file). If you read this file using Notepad you’ll see the number 124 displayed on the screen.

Suppose you store the same number to a file in binary mode, the program will convert 124 directly into its binary value and store this binary value in the file. Now if you try to read the file in Notepad you’ll see some weird character on your screen (because Notepad will apply ASCII decoding since it thinks that everything you open in Notepad is stored in ASCII format).

388

Files & Streams - C++

Page 390: C++ Basics to Advanced

Writing to a File

The << operator can be used to write to a file and this is the easiest method of writing to a file.

#include <iostream.h> #include <fstream.h>

int main ( ) { ofstream out("trial.txt", ios::out); // Create a stream for output linked to test.txt out<<"First "<<1<<endl; out<<"Second "<<2<<endl; out.close( ); return 0; }

In the above program, instead of writing to the screen, we want to write to a particular file. So we create a stream called ‘out’ for output and open the file trial.txt. It is better to clearly specify the method of opening the file (ios::out).

out<<"First "<<1<<endl;

does the following things: it writes the word ‘First’ and the number ‘1’ to the stream ‘out’. Remember that ‘out’ has been linked to the file ‘trial.txt’. Hence ‘First’ and ‘1’ are actually written to the file. Similarly ‘Second’ and ‘2’ are written on the next line of the file because of the endl keyword. ‘Endl’ will lead to a newline character being written at the end of the line. Finally we close the stream.

Reading a File

In a similar way we can use the >> operator to read data from a file. The file created in the earlier program can be read using this program.

# include <iostream.h> # include <fstream.h> int main ( ) { ifstream in("trial.txt", ios::in); if(!in) // If file doesn't exist then the program will quit. { cout<<"File cannot be opened"; return 1; }

char string[15]; int num; in>>string>>num; cout<<string<<num<<endl; in>>string>>num; cout<<string<<num<<endl;

389

Files & Streams - C++

Page 391: C++ Basics to Advanced

in.close( ); return 0; }

>> is the extraction operator. As you know, it is used for obtaining an input from the keyboard along with ‘cin’ (console in). Suppose you declare an integer ‘x’, you can get the value from the user using the statement:

cin>> x ;

What the user types is stored in the variable ‘x’. Similarly in the above program what the code:

in>>string>>num;

reads from the stream called ‘in’. It will store what it reads in the variables ‘string’ and ‘num’. The process is as follows:

The file is opened. The system reads the file (trial.txt which has been opened). It reads the first 15 characters. The first word in the file is ‘First’. After this it encounters a blank space. Hence the array ‘string’ is terminated (because the >> operator will not save whitespaces). The word ‘First’ is stored in the character array ‘string’. Next comes a number in the file. This is stored in the variable ‘num’ that we have declared.

The next statement is for displaying whatever was read from the file. After this remember that the program has reached the end of the first line of the file. It now automatically moves to the second line of the file. Hence when we repeat the statement:

in>>string>>num ;

and the program will read the second line of the file. The process just repeats and the word ‘Second’ and ‘2’ are now stored in ‘string’ and ‘num’.

Remember: The << and >> operator will write the data to a file in text format irrespective of whether you open the file in text or binary format. To check this try to write an integer to the file and then open the file in Notepad (if it were written in binary format you would not see the same number on the screen). To handle files in binary format we need to use other functions like write( ), get( ), put( ) etc., which will be discussed later.

Accessing other Devices (printer and monitor) through streams

All streams work similarly even though they are connected to different devices (like files, printer or the monitor). Every stream is associated to a device (or a ‘file’) using the open function.

390

Files & Streams - C++

Page 392: C++ Basics to Advanced

//program to print in the printer as well as on the screen # include <iostream.h> #include <fstream.h> int main( ) { ofstream monitor; ofstream print; print.open("LPT1"); print<<"Hi This is the printer."; monitor.open("CON"); monitor<<endl<<"This is the monitor."; monitor.close( ); print.close( ); return 0; }

The output (in your monitor display) will be:

This is the monitor.

Your printer (if it is connected to the system) will print:

Hi This is the printer.

In the above program, we create two streams named ‘monitor’ and ‘print’. The open function links these streams to the CON (the console which is your monitor) and LPT1 (which is the computer port where your printer will be connected). Hence instead of linking the streams to a disk file we have linked it to the monitor and printer. But the mechanism for information flow is the same as for disk file streams.

print<<"Hi This is the printer.";

The information transfer follows the direction of the arrows and hence it flows into the stream called ‘print’. Since this is associated to the printer, the sentence will be printed in your printer.

Get ( )and Put ( )

In the earlier example program to read and write into files we knew what data was present in the file (i.e. we knew that there was a string followed by an integer and hence it was easy to read the data from the file). Usually you won’t know what is stored in a file and this is where the get ( ) and put ( ) functions will be handy. get and put functions read and write unformatted data. Both these functions operate on single characters only. fet ( ) is used to read a character from a stream while put ( ) is used to write a character.

391

Files & Streams - C++

Page 393: C++ Basics to Advanced

// Program to create a file, write to it and then display the contents of the file

#include <iostream.h> #include <fstream.h> int main( ) { ofstream out("c:/test.txt",ios::out | ios::binary); char letter; cout<<"\nEnter what you want to store: "; cin>>letter;

while (letter != '*') { out.put(letter); cin>>letter; } out.close( );

//Now we want to read the file cout<<"\nThe contents of the file are: "; char ch; ifstream in("c:/test.txt", ios::in | ios::binary);

while (in) { in.get(ch); if (in) { cout<<ch; } }

in.close( ); return 0; }

The output is:

Enter what you want to store: check* The contents of the file are: check

The function put( ) puts a character into the stream (actually into the file) and get ( ) gets a character from the stream. put ( ) and get ( ) cannot be used individually on their own. They are member functions of the stream classes and hence they have to be called using objects belonging to those classes. In the above program till an asterisk (*) is entered by the user, all the characters will be stored into the file. Let us examine some of the main lines in the program:

while (in)

392

Files & Streams - C++

Page 394: C++ Basics to Advanced

This condition becomes false only when the end of file is reached. So till the end of file is reached we keep getting the characters one by one and then displaying them one by one on the screen.

Assume that we have stored the word ‘check’ in the file test.txt. When the compiler opens the file for reading, it has a small bookmarker that points to the beginning of the file. The bookmarker points at the letter ‘c’. It gets the letter ‘c’ (using the get(ch) statement) and then displays it on the screen. The compiler knows it still hasn't reached the end of file. So, the next time it goes through the while loop, the bookmarker has moved to the next character (i.e to ‘h’). The process repeats till the end of file is reached.

Remember: You cannot save spaces (blank spaces) using put and get functions. Try it in the above program and see what happens!

If in the above program the input were as below would yield:

Enter what you want to store: A Fine Morning* The contents of the file are: AFineMorning

As you can see, the whitespaces are not stored. The put( ) will basically ignore whitespaces. One more point to note in the above program:

ofstream out("c:/test.txt",ios::out | ios::binary);

Usually we refer to files by saying c:\test.txt but here we have used:

c:/test.txt

In C++ you should always use this slash within the double quotes whenever you are referring to directory/drive paths. Let’s suppose that we use the normal slash, then the statement would become:

ofstream out("c:\test.txt",ios::out | ios::binary);

If you notice, within the double quotes the compiler will encounter a ‘\t’ and this actually corresponds to an escape sequence. Hence the compiler will not go to the C drive itself.

Another alternative to c:/test.txt is to use double slashes as below:

ofstream out("c:\\test.txt",ios::out | ios::binary);

This method is also accepted in C++ programming.

Beware: Make use of forward slash (c:/test.txt) or double backslash (c:\\test.txt) instead of single backslash (c:\test.txt) when referring to file locations in C++.

393

Files & Streams - C++

Page 395: C++ Basics to Advanced

Streams and Files - III

The following topics are covered in this section:

• Writing structures to files • Padding/packing of structures • Detecting end of file

Writing a structure to a file

The read ( ) and write ( ) functions are another way to read and write blocks of binary data. This is particularly useful in writing and reading structures to files. The read and write ( ) function syntaxes are very similar to each other. But I think you'll find the arguments a bit hard to understand. The syntax is as follows:

objectname.read(char * buf, int n); objectname.write(const char * buf, int n);

The syntax might appear to be a bit weird. Let's go one by one. First of all, read ( ) and write ( ) functions have to be called by an object which belongs to one of the streams. You can't call them without an object (in this case the object is a stream).

The read ( ) function will read ‘n’ characters (or ‘n’ bytes) from the invoking stream and puts them in the buffer pointed to by ‘buf’. The write ( ) function will write ‘n’ characters (or ‘n’ bytes) to the invoking stream from the buffer (buf). ‘n’ is basically an integer that denotes the size in bytes.

(char *) buffer : This tells the compiler the starting memory location. If you are using the write function then this denotes the starting point for copying data into the file. The function needs the first argument to be a pointer to a character and that’s why we use casting (casting is discussed in a later chapter). Basically when you pass the first argument to the read/write( ) functions, you should make that argument look like a pointer to a character.

int n : Here we specify the number of bytes we want to write or read (usually we make use of the sizeof operator to determine the size of what we want to write/read because you can’t expect the programmer to remember the sizes).

Beware: The read and write( ) functions write and read binary data (not text format).

Remember: These two functions are very useful in reading/writing an entire array in binary format to a file.

// Program to create a file, write to it and then display the contents of the file

#include <iostream.h> #include <fstream.h>

struct email { char name[20];

394

Files & Streams - C++

Page 396: C++ Basics to Advanced

char id[20]; };

int main ( ) { email user; email check; //user, check are structure variables. cout<<"Enter a name: "; cin>>user.name; cout<<"Enter the email address : "; cin>>user.id; //get values for elements of user ofstream out("c:/email.txt", ios::out | ios::binary); //Open the file test.txt out.write( (char *) &user, sizeof (struct email) ); out.close( );

cout<<endl<<"Contents of file are : ";

ifstream in ("c:/email.txt", ios::in | ios::binary); in.read((char *) &check, sizeof(struct email)); //read the structures cout<<endl<<check.name; cout<<endl<<check.id; in.close( ); return 0; }

The output is:

Enter a name: ajay Enter the email address : [email protected] Contents of file are : ajay [email protected]

The program upto the write ( ) function is normal. We open a stream called out for writing the structure to the file.

out.write((char *) &user, sizeof (struct email));

Compare the above line with the syntax of the write ( ) function. You'll notice that instead of ‘buf’ we've used ‘&user’. This is because we want to save the structure variable ‘user’ into the file. So we point to the address of ‘user’. Then instead of ‘n’ we've used:

sizeof (struct email)

In the general syntax, ‘n’ refers to the number of bytes you want to write. In this case we want to write as many bytes as the structure will occupy. Instead of specifying some fixed number, it's better to use the ‘sizeof’ operator to find the number of bytes. The name of the structure is ‘email’. So by saying

sizeof (struct email)

395

Files & Streams - C++

Page 397: C++ Basics to Advanced

the compiler will find out how many bytes the structure ‘email’ occupies. This value is the same as that of ‘user’ (since ‘user’ is a variable of structure ‘email’).

Next we create a stream to read the contents of the file test.txt (just to see whether the structure was saved in the file).

in.read((char *) &check, sizeof(struct email));

The syntax is same as that of write ( ) function. Except that we've made use of another structure variable namely: ‘check’. This is also a variable belonging to structure type ‘email’ except that we haven't obtained any values for check.name and check.num

Now, we will read ‘sizeof (struct email)’ bytes and store it in the structure variable ‘check’ and then print the values of check.name and check.num. Instead of using ‘check’, you can also use the structure variable ‘user’. A different structure variable is used for the two purposes to demonstrate clearly that we are really writing and reading from the file.

Remember: It is always a good idea to store structures in binary-format rather than text-format (since they contain a mixture of data types and you wouldn’t want conversion of data).

Padding/Packing of Structures

What do you think is the space occupied by the structure variable ‘t’ below:

struct test { long int num; // long int is 4 bytes char a; //char is 1 byte }t;

The actual size is 5 bytes but the compiler might do padding to the structure and make the size of the structure 8 bytes. Why? Some compilers prefer to uniformly allocate memory space to the structure members (i.e. each structure member is given a fixed length). In the above structure ‘test’ the compiler notes that ‘long int’ is the maximum element size (of 4 bytes) and so it prefers to allocate memory space in increments of 4 bytes. Thus if ‘num’ occupies memory from byte number 1 to byte 4, then ‘char a’ will occupy memory from byte number 5 to byte 8 (even though ‘char’ requires only 1 byte). In this case we say that the packing is 4 bytes (because each member is packed to 4 bytes). Why does the compiler do packing?

Consider the structure:

struct test { short int s; // 2 bytes char c; // 1 byte double d; // 8 bytes long int i; // 4 bytes }

396

Files & Streams - C++

Page 398: C++ Basics to Advanced

The structure ‘test’ will now have packing of 8 bytes (because a ‘double’ occupies 8 bytes). Let us assume that the compiler allocates memory starting from byte 0.

The allocation will be: [2+1+(5)] + [8] + [4 + (4)] = 24 bytes

The number within ( ) denotes the number of extra bytes added for the sake of uniformity (the padded bytes).

‘s’ and ‘c’ (both combined occupy less than 8 bytes) are put together in one field. Since ‘d’ cannot be accommodated within the same 8-byte field, five bytes are padded to the first field. ‘d’ will occupy the next 8-byte field. No padding of bytes is required here. The last field is occupied by the integer ‘i’ and it occupies only 4 bytes. So another 4 bytes are padded here to make this field 8 bytes long.

Thus the starting location of each member (in terms of bytes) when 8-byte packing is used is: 0, 2, 8, 16 (a total memory space of 24 bytes is needed).

Suppose we didn’t use padding (i.e. if packing is 1 byte), the positions will be: 0, 2, 3, 11 (a total of 15 bytes)

If we use packing of 4 bytes, the positions will be: 0, 2, 4, 12 (a total of 16 bytes)

As you might have noticed, when we use packing, all the members will be at multiples of 8 or 4 (depending on the packing used) and such an alignment is faster to access. A 32-bit processor would be able to access members at 32 bit boundaries faster. If there is no packing the elements are at varying positions (i.e. they are not uniformly placed in memory). Though more space is occupied by padding, the speed of the program can be improved using padding. To take advantage of this fact some compilers implement padding whenever they deal with structures.

There is no need to worry about padding if you are developing software entirely in a single compiler. The problem arises when you write two programs (one for writing structures and another for reading the file) on two different compilers. If both compilers implement packing of bytes then there won’t be a problem. For example: Turbo C++ does not use padding while VC++ uses padding. So, if you write a program to write structures to files in VC++, the program will use padding to store the data in the file. While reading the file using a TC++ program, the compiler does not know about packing and so when you use the read ( ) function, you will get strange results. An example to highlight this problem is given below.

//Program to write structures to a file using a compiler which implements padding #include <iostream.h> #include <fstream.h> struct test { char a,b,c; long int num; }t; //by default each member is packed to 4 bytes. int main( ) { t.a='a';

397

Files & Streams - C++

Page 399: C++ Basics to Advanced

t.b='b'; t.c='c'; t.num=456789; ofstream str("c:\\text.txt"); str.write((char *)&t, sizeof(t)); //it will write a structure of 8 bytes in the file str.close( ); return 0; }

This program writes the structure to a file (and it uses padding). Let us suppose that we write another program to read this file in a compiler which doesn’t support padding.

//Program to read structures using a compiler which does not know padding

#include <iostream.h> #include <fstream.h> struct test { char a,b,c; long int num; }t; //compiler assumes ‘t’ as occupying 7 bytes

int main( ) { ifstream str("c:\\text.txt"); str.read((char *)&t, sizeof(t)); //it reads only 7 bytes. cout<<t.a<<t.b<<t.c<<t.num; str.close( ); return 0; }

The output will be:

abc116937984

The actual contents stored in the file was: abc456789. This happens because the program attempts to read without considering padding, it tends to read the wrong bytes.

To avoid such situations compilers provide a preprocessor directive called ‘pragma pack( )’. Using this directive we can specify how many packing bytes we want the compiler to use. The syntax is:

#pragma pack(n)

where ‘n’ represents the number of bytes for packing.

For example:

#pragma pack(8)

will pack each structure member to 8 bytes while

398

Files & Streams - C++

Page 400: C++ Basics to Advanced

#pragma pack(1)

is the same as using no padding.

Remember: Compilers that do not use padding (like TC++) will not support the #pragma pack ( ) directive. These compilers always use a packing of 1 and this cannot be changed.

Thus if programmers feel that they will be working across different compilers, they prefer to specify the directive:

#pragma pack(1)

which instructs the compiler to treat the members just as they are (no padding). This directive has to be added along with the #include directive (outside the main ( ) function).

Thus:

#pragma pack(1) struct test { short int s; // 2 bytes char c; // 1 byte double d; // 8 bytes long int i; // 4 bytes };

will cause any variable of type ‘test’ to occupy only 15 bytes.

Remember: Padding is a problem mainly when you are working with storing data in files and when you are using two compilers.

End of File

There exists a member function that you can use to identify whether the end of file (EOF) has been reached or not.

int eof( );

This function will return true if the end of file has been reached.

You could test for EOF using something similar to the following:

while( !in.eof( ) ) { //body of while loop }

where ‘in’ is an input stream.

399

Files & Streams - C++

Page 401: C++ Basics to Advanced

Streams and Files - V

The following topics are covered in this section:

• File Pointers • Sequential and Random Access • Command Line Arguments • Recap

Random Access of Files (File Pointers)

Using file streams, we can randomly access binary files. By random access, you can go to any position in the file as you wish (instead of going in a sequential order from the first character to the last). Earlier in this chapter we discussed about a bookmarker that will keep moving as you keep reading a file. This bookmarker will move sequentially but you can also make it move randomly using some functions. Technically this bookmarker is a file pointer and it determines as to where to write the next character (or from where to read the next character). We have seen that file streams can be created for input (ifstream) or for output (ofstream). For ifstream the pointer is called as ‘get’ pointer and for ofstream the pointer is called as ‘put’ pointer. fstream can perform both input and output operations and hence it has one ‘get’ pointer and one ‘put’ pointer. The ‘get’ pointer indicates the byte number in the file from where the next input has to occur. The ‘put’ pointer indicates the byte number in the file where the next output has to be made. There are two functions to enable you move these pointers in a file wherever you want to:

seekg ( ) - belongs to the ifstream class seekp ( ) - belongs to the ofstream class

We’ll write a program to copy the string "Hi this is a test file" into a file called mydoc.txt. Then we’ll attempt to read the file starting from the 8th character (using the seekg( ) function).

Strings are character arrays terminated in a null character (‘\0’). If you want to copy a string of text into a character array, you should make use of the function:

strcpy (character-array, text);

to copy the text into the character array (even blank spaces will be copied into the character array). To make use of this function you might need to include the string.h header file.

#include <iostream.h> #include <fstream.h> #include <string.h> int main( ) { ofstream out("c:/mydoc.txt",ios::binary); char text[80]; strcpy(text,"Hi this is a test file"); out<<text;

400

Files & Streams - C++

Page 402: C++ Basics to Advanced

out.close( ); ifstream in("c:/mydoc.txt",ios::binary); in.seekg(8); cout<<endl<<"Starting from position 8 the contents are:"<<endl; while ( !in.eof( ) ) { char ch; in.get(ch); if ( !in.eof( ) ) { cout<<ch; } } in.close( ); return 0; }

The output is:

Starting from position 8 the contents are: is a test file

As you can see, the output doesn’t display, "Hi this " because they are the first 7 characters present in the file. We’ve asked the program to display from the 8th character onwards using the seekg( ) function.

in.seekg(8);

will effectively move the bookmarker to the 8th position in the file. So when you read the file, you will start reading from the 8th position onwards.

The following fragment of code is interesting:

while ( !in.eof( ) ) { char ch; in.get(ch); if ( !in.eof( ) ) { cout<<ch; } }

You might be wondering as to why we need to check for the EOF again using an ‘if’ statement. To understand the reason, try the program by removing the ‘if’ statement. The result will be surprising and interesting. Think over it and you will be able to figure out the logic.

401

Files & Streams - C++

Page 403: C++ Basics to Advanced

The syntax for seekg( ) or seekp( ) is: seekg(position, ios::beg) seekg(position, ios::cur) seekg(position, ios::end)

By default (i.e. if you don’t specify ‘beg’ or ‘cur’ or ‘end’) the compiler will assume it as ios::beg.

o ios::beg – means that the compiler will count the position from the beginning of the file.

o ios::cur – means the compiler starts counting from the current position. o ios::end – it will move the bookmarker starting from the end of the file.

Just like we have 2 functions to move the bookmarker to different places in the file, we have another 2 functions that can be used to get the present position of the bookmarker in the file.

o For input streams we have: tellg( ) o For output streams we have : tellp( )

You would think that the value returned by tellg ( ) and tellp ( ) are integers. They are like integers but they aren’t. The actual syntax for these functions will be:

streampos tellg ( );

where streampos is an integer value that is defined in the compiler (it is actually a typedef).

Of course you can say:

int position = tellg ( );

Now, the variable ‘position’ will have the location of the bookmarker. But you can also say:

streampos position = tellg( );

This will also give the same result. ‘streampos’ is defined internally by the compiler specifically for file-streams.

Similarly, the syntax of seekg ( ) and seekp ( ) was mentioned as:

seekg(position, ios::beg)

Again in the above syntax, ‘position’ is actually of type ‘streampos’.

Sequential and Random Access Files

Basically variables are used for temporary storage and files are used for permanent storage of data. Based on how files are accessed, they can be divided into sequential and random access files. Actually this division of files depends on how we read and write to files (physically the file is stored as a sequence of bytes in memory).

402

Files & Streams - C++

Page 404: C++ Basics to Advanced

All data is represented in the form of bits. A set of 8 bits (or a byte) can be used to represent one character. A set of similar bytes will form a ‘field’. A set of related fields will form a record and a file consists of a set of related records. Let us suppose that a University maintains a database consisting of its student’s details.

Fields and records are the terms used when we deal with files. Records are equivalent to ‘structures’ or ‘objects’ in C++.

Usually when storing such data (like a student record as shown above), the programmer will use one unique ‘key field’. A ‘key field’ is the field which can be used to identify or locate a particular record in the file. For example in the above diagram, the student ID number will be the key field (thus if we want to access the details about the student Ajay then we can just refer to student ID number 1). The key field should be something unique (i.e. no two records should have the same key field).

Usually we write and read records from a file. Sequential access files are the simplest way of organizing a file. In sequential access files we write the variables continuously one after the other. The length of each record isn’t fixed and can vary (i.e. each record needn’t occupy the same amount of memory). The advantage of this is that we do not waste any memory. The disadvantage is that if you want to access the 3rd record stored in the file, you will have to read the first two records before accessing the third (i.e. you cannot directly jump to the third record). The reason for this is because in sequential access files the record length is not fixed and you cannot predict as to where the third record might be stored. This leads to a few other problems. It is not possible to directly insert a new data in the middle of the file. If a new record has to be inserted, the old record has to be copied into a new file (up to the point where you want to insert), then the new entry should be added to the new file and then the remaining records from the old file have to be copied to the new one. You cannot directly update/modify a record in sequential access files. Let us suppose that we have a disk file containing the data:

403

Files & Streams - C++

Page 405: C++ Basics to Advanced

This file has been stored sequentially and maybe the name Ajay needs to be modified to Williams. If you attempt to overwrite the existing record the resultant will be:

1 Williams2 Suresh 88

Because data is stored continuously in a sequential file, if the modified entry you make is longer in length than the existing entry then the neighbouring field will get overwritten.

Random access files overcome this problem since they have fixed length records. The problem here is that even if we want to store a small sized record we still have to occupy the entire fixed record length. This leads to wastage of some memory space. For example if we are using 10 bytes to store a complete sentence in the file then even if you want to store a single letter (like ‘a’) 10 bytes will also be used up for this. But even though some memory space is wasted this method will speed up access time (because now we know where each record is stored. If a record length is fixed as 10 bytes, then the fifth record will start at byte number 50 and it is easier to jump directly to that location instead of reading the first four records before accessing the fifth).

Word processing program usually store files in a sequential format while database management programs store files in a random access format. A simple real life analogy: Audio tapes are accessed sequentially while audio CDs (Compact Discs) are accessed randomly.

So, how do we create sequential and random access files in C++? Actually we have already covered both these topics without explicitly using the terms sequential and random access. Whenever you make use of the ‘read’ and ‘write’ functions to write structures/objects to a file, you are actually creating a random access file (because every record will have the size of the structure). Whenever you use the << and >> operator to read and write to disk files, you are accessing the file sequentially (this was the first example program). Whenever you write to a stream (or a file) using << operator, you are writing varying length records to the file. For example: You might first write a string of 10 characters followed by an integer. Then you may write another string of 20 characters followed by a ‘double’. Thus the records are all of varying lengths.

To effectively use random access files we make use of the seekp( ) and seekg ( ) functions. Though these can be used on sequential files it will not be very useful in sequential access files (because when you are searching for a data you are forced to read each and every character/byte, whereas in random access files you can jump to the particular record that you are interested in).

404

Files & Streams - C++

Page 406: C++ Basics to Advanced

Command Line Arguments

You know that functions can have arguments. You also know that main ( ) is a function. In this section we'll take a look at how to pass arguments to the main function. Usually filenames are passed to the program.

First of all, let us suppose that we have a file by the name marks.cpp. From this file we make an exe file called marks.exe. This is an executable file and you can run it from the command prompt. The command prompt specifies what drive and directory you are currently in. The command prompt can be seen as the ms-dos prompt.

C:\WINDOWS>

This denotes that you are currently in C drive and in the directory named Windows. (By the way, if you want to go to the MS DOS command prompt from Windows, just go to "Start" and click on "Run". Type "command" in the text box and click "Ok").

Your marks.exe program is in this directory (let us assume it is here. If it isn’t in this directory then you have to change to that particular directory). To run the program you will type:

C:\WINDOWS> marks name result

You must be thinking that we will type only the name of the program? In this case the C++ program that you wrote is assumed to have arguments for the main ( )function (i.e. in the marks.cpp file you have provided arguments for the main( ) function):

int main (int argc, char * argv[ ] ) argc (the first argument - argument counter) stands for the number of arguments passed from the command line. argv (argument vector) is an array of character type that points to the command line arguments. In our example, the value of ‘argc’ is 3 (marks, name, result). Hence for ‘argv’ we have an array of 3 elements. They are:

argv[0] which is marks. argv[1] which is name argv[2] which is result

Note: argv[0] will be the name that invokes the program (i.e. it is the name of the program that you have written).

If you feel a little vague in this section don't worry. In the next section we'll take a look at a simple program.

A program using Command Line Arguments

// This file is named test.cpp

#include <iostream.h> int main ( int argc, char * argv[ ] )

405

Files & Streams - C++

Page 407: C++ Basics to Advanced

{ cout<<"The value of argument counter (argc) is: "<<argc; int i; for ( i = 0 ; i<argc ; i ++ ) { cout<<endl<<argv[i]; } return 0; }

Save the file as test.cpp. Compile it and then make the executable file (test.exe). If you run test.exe from Windows (i.e. by just double clicking on the file),

the output will be as follows:

The value of argument counter (argc) is: 1 c:\windows\test.exe

This will be the output since you didn't specify the arguments. To specify the arguments you have to go to DOS prompt. From there type:

c:\windows>test one two three You have to go to the folder in which you have the test.exe file (I assume that your program is in the windows directory in C drive).

The output will be:

The value of argument counter (argc) is: 4 c:\windows\t.exe one two three

There are numerous things you can do with this. You can pass the names of files upon which you want the C++ program to operate, or you could pass the name of a file that you want your program to create, etc. Depending on your application, you can make use of the arguments. For example: if you write a program for zipping files, then the arguments can be used to specify the files that you want to zip.

Recap

• A stream is an intermediate medium used for accessing other devices (like disk files or printers).

• The general I/O classes are: istream, ostream and iostream. • The predefined streams are: cout, cin, cerr and clog. • The stream status flags (or bits) are used to determine the state of the stream. • The file I/O classes are derived from the general I/O classes. They are: ifstream,

ofstream and fstream. • In random access of files two pointers called the ‘get’ and ‘put’ pointers are used.

They are also called file position indicator or file pointer. • The read ( ) and write ( ) function can be used to read and write structures from a file. • Command line arguments are used to pass arguments to the main ( ) function.

406

Files & Streams - C++

Page 408: C++ Basics to Advanced

Chapters 9 to 11

(Inheritance, Operator overloading and streams)

Q.) Complete the following tabulation on operator overloading by specifying the return type which one would normally use:

Operator overloaded Return type + :: = <

+= !

= = -

? :

Q.) Which form (postfix/ prefix) of the unary operator ++ involve more overhead? Or are they just different in terms of representation alone?

A.) When we say:

x = ++y;

‘y’ is incremented and the value is assigned to ‘x’ (first increment and then obtain value).

Whereas in:

x = y++;

the value of ‘y’ is first assigned to ‘x’ and then ‘y’ is incremented (so ‘x’ actually has the old value of ‘x’). Here we obtain the value first and then increment. To remember how they work all you need to do is take a look at how we use them; when we say ++y the ++ comes before the ‘y’. Thus we first increment and then obtain the value whereas in y++, we get ‘y’ first and then increment it (it’s just a memory aid since many new programmers tend to get confused with the 2 forms).

Next let’s consider the following statement:

y++++; //Error

This is equivalent to: (y++)++;

Q&A - 9-11

Page 409: C++ Basics to Advanced

Obviously y++ returns an integer, so why is it not permitted? That’s because the return type of the postfix operator is a constant (in this case a constant integer). Thus you can modify the returned value again.

Let’s consider the other statement:

++++y; //no error

You wouldn’t get an error because the prefix operator returns a reference (rather than an object).

Why the difference in return types?

In ++y, all that needs to be done is to increment the current value and return a reference to the same object. But in y++, we need to create a new object to store the old value of ‘y’, then increment ‘y’ and then return the new object (which is why postfix form means ‘get value and then increment’).

Thus the postfix form would be less efficient than the prefix form since it involves an extra object.

And the following fragment will work:

i=5;

++++++i;

would end up incrementing ‘i’ 3 times (‘i’ would have a value of 8). The statement is equal to:

(++ ( ++ (++i) ) );

And the prefix form of ++ has associativity from right to left. This works because the prefix form of ++ returns a reference to the same object. But we wouldn’t want to use such a statement because it reduces readability (you will have to keep counting the number of +s each time you go through the code).

Q&A - 9-11

Page 410: C++ Basics to Advanced

Q.) Why doesn’t the following code compile?

class employee { public: employee(int x) {} };

class teacher:public employee { public: teacher( ) {} };

class librarian:public employee { public: librarian(int y):employee(y) {} };

int main( ) { teacher f1; librarian m1(5); return 0; }

A.) When an object of type teacher is created, the compiler will try to call the default constructor of the base class (i.e. of the class employee). But employee doesn’t have a default constructor and thus the following code produces the error:

public: teacher( ) {}

The class librarian is fine since here we are explicitly calling the parameterized constructor for employee (here the compiler won’t call the default constructor).

Q.) What does “is a kind of” relationship in C++?

A.) This term is just another name for the “is a” relation (public inheritance).

Q&A - 9-11

Page 411: C++ Basics to Advanced

Q.) Why would a programmer place a constructor in the protected region of a class? Or is this an error?

class base { protected: base( ) { cout<<"base constructor"; } };

class derived:public base {};

int main( ) { derived d1; return 0; }

A.) This is perfectly legal. By placing the base constructor in the protected region, a user cannot instantiate an object of type ‘base’. The following code would cause a compiler error:

base b1; //error-cannot access protected constructor

But any class derived from ‘base’ will be able to invoke the base class constructor (as done in the question). The output of the program will be:

base constructor

Q.) Is it legal to place the constructor in the private region of a class?

class base { private: base( ) { cout<<"base constructor"; } };

A.) This would serve no useful purpose because now even a derived class cannot access the constructor of ‘base’ (because private members are inaccessible by the derived class irrespective of how you inherit the base class).

And as discussed in the earlier question, neither can we directly instantiate an object of type ‘base’ (this would lead to a compiler error).

Q&A - 9-11

Page 412: C++ Basics to Advanced

Q.) What does the following code do? What is the purpose of the keyword ‘using’?

class base { protected: void b_func( ) { cout<<endl<<"Base function"; } };

class derived:private base { public: using base::b_func; };

A.) Any object of type ‘derived’ can now invoke the function b_func( ) just as if b_func ( ) were a member function of class ‘derived’. If the code:

public:

using base::b_func;

was not written then the following would be illegal:

int main( ) { derived d1; d1.b_func( ); //error- cannot access protected member return 0; }

The general syntax for ‘using’ in such a scenario is:

using base-class-name:: base-class-function-name;

We shouldn’t specify the parameters. i.e. the following is wrong:

using base::b_func( ); //error

Q.) Why don’t we have a virtual constructor?

The concept of ‘virtual’ functions is used since the type of object is not known at compile-time. The calling object is determined at run-time instead. But a constructor is used to create/ construct an object and this is almost certainly known while compiling. We can construct something only if we know what we are going to create. Thus C++ doesn’t provide for virtual constructors (but there are some ways of obtaining the effect of virtual constructors- generally not required).

Q&A - 9-11

Page 413: C++ Basics to Advanced

Q.) What is wrong with the following code (it crashes sometimes and sometimes it gives weird results):

class myarray { public: int *ptr; myarray(int val) { ptr= new int; *ptr=val; }

~myarray( ) { delete ptr; }

void display( ) { cout<<endl<<*ptr; }

};

int main( ) { myarray arr1(10); cout<<endl<<"arr1 declared outside block is:"; arr1.display( ); { myarray arr2(50); cout<<endl<<"arr2 is within inner block:"; arr2.display( ); arr2=arr1; cout<<endl<<"arr2 after assignment is:"; arr2.display( ); }

cout<<endl<<"arr1 outside the block is:"; arr1.display( ); return 0; }

A.) • Object arr1 is visible throughout the program but arr2 is visible only within the scope of the inner block.

• Within the inner block, arr1 is assigned to arr2 (since assignment operator hasn’t been overloaded for the class the compiler provides a default assignment operator which perform bitwise copy operation).

• Now the pointer of both arr2 and arr1 point to the same memory location. • When we exit the inner block, arr2 object will be destroyed (local objects are

destroyed at the end of their scope). • The destructor of arr2 will free the memory pointed to by ‘ptr’ of object arr2

(which is actually the same memory location as that pointed by ptr of arr1). • Now when arr1 tries to retrieve the value stored, it can’t do so because the

memory has already been freed.

Remember: It is advisable to overload the assignment operator and copy constructor in a class which uses dynamic memory allocation.

Q&A - 9-11

Page 414: C++ Basics to Advanced

Q.) What would be the output of the following? Explain the problem as well.

class myChar { private: char alpha; public: myChar(char c):alpha(c) {} int operator!= (const myChar &right) { if (right.alpha!=alpha) return 1; else return 0; } };

int main( ) { myChar ch1('a'); int i = 97; cout<<(ch1 != i); return 0; }

A.) The output will be 0. When the compiler encounters:

ch1! = i

it is equivalent to:

ch1.operator!=(i);

But the operator!= function requires a character and the compiler will perform an implicit conversion of 97 into a character (97 happens to be the ASCII value for ‘a’). Thus since a= = a, the output will be 0. To avoid this from occurring, we should use an explicit constructor.

Q&A - 9-11

Page 415: C++ Basics to Advanced

Bonus questions - Units IX to XI

Interview/ viva questions:

1. What is meant by operator overloading? Why is it necessary?

2. What is polymorphism?

3. Explain run-time polymorphism with an example. Differentiate between run-time and static polymorphism.

4. What is the meaning of ‘has a’ and ‘is a’ relationship in C++?

5. What is the need for an abstract class?

6. What are the operators that cannot be overloaded? Can new operators be defined in C++?

7. What are the dynamic memory operators in C++? What are the equivalent of malloc and free in C++?

8. What are streams? How are they useful?

9. Name some of the predefined streams.

10. What is the difference between:

• cin and cout

• cout and cerr

• Random access and sequential access

11. Write a simple program to write and read a character from a file.

12. How is the file pointer used for random file access?

13. Write down the syntax of the main function (including its arguments).

14. If we create a class with no member functions, what are the functions which will be automatically created?

Q&A - 9-11

Page 416: C++ Basics to Advanced

Extra Stuff:

Q.) In the complex numbers class that you had created, overload the + and – operators to add and subtract two complex numbers.

Q.) In the same complex numbers class, overload the * and / operator to multiply and divide complex numbers.

Q.) Create a class called ‘string’ which will consist of a character array (of maximum length 80). Overload the + operator such that two string objects will be concatenated.

Q.) In the same string class, overload the ‘= =’ operator such that we can test whether 2 string objects are equal or not (for comparing use ASCII values/you can also use the ‘strcmp’ function. The overloaded operator function should return a Boolean value just like the normal == operator depending on the comparison).

Q.) In the same string class, overload the ‘<=’ operator such that we can test whether the left side string object is less than or equal to the right side string object (the function should return a value just like the normal <= operator depending on the comparison).

Q.) Create a class called person and then derive two classes for teaching faculty and non teaching faculty (for a school). The person class should be an abstract class.

Q.) Create a file using C++ in which we can store an employee’s details (like name, experience and salary). The user should be able to store as many records as he wants.

Q.) Write a C++ program to read the employee record file and display the contents.

Q.) Write a C++ program to check whether a given word is present in a text document.

Q.) Write a program that obtains the file name from the user through command line argument and returns the number of bytes occupied by the file.

Q.) Write a program to encrypt a file (using your own algorithm for encryption) and store it in a different file. Write a decryption program that will retrieve the original file contents.

Q&A - 9-11

Page 417: C++ Basics to Advanced

Useful Classes and Functions - I

The following topics are covered in this section:

• Manipulators • Flushing • setw and other manipulators

There are two ways in which we can perform stream-formatting operations: using manipulators or using the stream member functions.

Manipulators

There are many stream-formatting functions available but it is a little tedious to use them. Manipulators duplicate these functions and make it easier to carry out stream-formatting operations. Usually it is the output (what we display on the screen) that needs to be formatted to suit our needs. In this case, ‘cout’ is the stream.

One of the most commonly used manipulators is ‘endl’. This is used to end the current line and go to the next line. You cannot use ‘endl’ separately. All the manipulators have to operate on streams. Manipulators that do not require any arguments are defined in the iostream.h header (manipulators with arguments are defined in the iomanip.h header file).

Let's check out an example program.

# include<iostream.h> int main( ) { cout<< "Hi, this is a test program"<<endl; cout<<endl; cout<<endl<<"You should see three empty lines above this"; return 0; }

The output will be:

Hi, this s a test program You should see three empty lines above this

Each ‘endl’ will cause the present line to be terminated and hence, there will be three empty lines in between the two sentences.

Remember: Don't use double quotes on endl.

416

Useful Classes & Funtions

Page 418: C++ Basics to Advanced

The above program can also be written as:

cout<< "Hi, this is a test program"<<endl<<endl<<endl<<"You should see three empty lines above this";

The output will be the same since manipulators can be cascaded in a single statement.

Flushing and Buffer:

Actually endl is equivalent to writing:

cout<<"\n";

This is called as the newline character. But the ‘endl’ manipulator does something more than just inserting a newline character. It will also flush the stream. When you write something to a stream, data is first written into a buffer (stream buffer called as streambuf) and then from there into your stream (this will happen automatically and it may seem as if the data is being directly written to the stream). Data will be written to the stream only when the buffer is full. When a stream is flushed, all the pending data present in the buffer (which hasn’t been used as yet) will be flushed from the buffer to the stream. A buffer is a temporary intermediate storage area used when information is transferred between a stream and a file. The ‘ostream’ has a member function called flush( ). To flush a stream you can either type:

cout.flush( );

or you can use the manipulator:

cout<<flush;

The OS will have its own buffer to improve efficiency but through C++ you can only control the buffers created by your code (i.e. streambuf). Why do we need buffers? Every stream is associated to some physical device (a simple example is that of streams being associated to files). The stream buffer will be tuned to the physical device associated to the stream. Most of the physical devices are much slower than memory access (i.e. memory operations are much faster than I/O device operation). Now if buffers are not used then the processor will waste a considerable amount of time in waiting for something to happen in the I/O device. For example when we enter something through a keyboard, there is a considerable delay between two keystrokes; maybe just a fraction of a second but this time can be used by the computer to perform its internal operations instead of waiting for the user to type something. This is where buffer comes into the picture. A buffer will keep collecting the data (it is like a storage area) till it gets full. Once the buffer is full it will flush the data to the corresponding stream. Once again the buffer will start collecting new data till it gets full and the process repeats.

Using the flush ( ) function, you can forcefully flush data into the stream continuously (instead of waiting till the buffer gets full). A simple program should make the concept of buffering and flushing clear. Let us write a program to write the number 12 to a disk file (using streams).

417

Useful Classes & Funtions

Page 419: C++ Basics to Advanced

# include <iostream.h> #include <fstream.h> #include <stdlib.h> int main( ) { ofstream out("c:\\test.txt",ios::binary); out<<12; system("PAUSE"); out.close( ); return 0; }

When the program executes:

out<<12;

It will not write the number ‘12’ to the file test.txt. To verify this run your program in windows. While the program is running, because of:

system("PAUSE");

The program will pause for the user to press a key. Now go to the desktop, open "My Computer" and in C: drive open the file test.txt (or open this file from ‘Notepad’). You will notice that the file is empty. Thus, though we think that 12 has been written to the file, in reality it hasn’t been written as yet. The number ‘12’ is still in the buffer. Close the Notepad and press a key to continue the execution of the program. After execution once again open the file test.txt in Notepad and you will see 12 written in the file. Thus since the buffer was not full (because we wanted to write only a small number to the file) the data was not written immediately. Instead the buffer waited hoping that more data would be entered. But when out.close( ) was encountered it knew that now it had to flush the data from the stream (because the stream is going to be disassociated from the file). To instantly update the file, we can make use of the flush( ) function as shown below:

# include <iostream.h> #include <fstream.h> #include <stdlib.h> int main( ) { ofstream out("c:\\test.txt",ios::binary); out<<12; out.flush( ); //12 is written immediately to the file system("PAUSE"); out.close( ); return 0; }

In the above program we forcefully flush the stream and data is immediately written to the file. Flushing forcefully is useful in situations where you need to update the file continuously (when there is likeliness of frequent power shortages etc.) otherwise you might lose the data present in the buffer.

418

Useful Classes & Funtions

Page 420: C++ Basics to Advanced

setw( )

This manipulator is used to set the width of the data being displayed. If the data is longer than the width you specify, this manipulator will not truncate your data to fit the width. Instead it will display the entire data but this will lead to misalignment. The setw( ) manipulator defined in the <iomanip.h> header since it takes arguments.

#include <iostream.h> #include <iomanip.h> int main( ) { cout<<endl<<"James"<<setw(20)<<"Harry"<<setw(4)<<"Joe"; return 0; }

The output is:

James Harry Joe

The diagram should make it clear as to how the setw( ) works.

The statement:

cout<<setw(20)<<"Harry";

means that the word Harry will be given an alignment of 20 characters and Harry will be starting from the right end (as shown in figure). The net result is that we will have 15 blank whitespaces between ending of James and the starting of Harry.

Setprecision( )This also comes under the iomanip header. It is used to set the number of decimal places that you would like to view in your output.

Example:

#include <iostream.h> #include <iomanip.h> int main( ) { cout<<setprecision(3)<<3.456<<endl<<setprecision(1)<<3.14213; return 0; }

419

Useful Classes & Funtions

Page 421: C++ Basics to Advanced

Output is:

3.46 3

You can see that the result gets rounded up to the number of places that you want. 3.456 is displayed as 3.46 because we set the precision to 3 digits only.

The other manipulators available in <iostream.h> are:

Name of the Manipulator Purpose

ends Inserts a null character (‘\0’)

dec Display integer in base 10

oct Display integer in base 8

hex Display integer in base 16

ws Skip whitespace in input stream

flush Flush the stream

The manipulators available in <iomanip.h> are (these manipulators require arguments):

Name of the Manipulator Purpose

setiosflags(names-of-flags) Set formatting flags

resetiosflags(names-of-flags) Reset formatting flags

setbase(int base) where base=8,10 or 16

setfill (char c) Uses ‘c’ as the fill character

setprecision (int n) Changes precision to ‘n’

setwidth (int n) Width set to ‘n’

In the table above ‘names-of-flags’ refers to the formatting flags that can be set/reset depending on which manipulator you use. If a formatting flag is set then that particular format will be active (or effective). The formatting flags are tabulated below:

420

Useful Classes & Funtions

Page 422: C++ Basics to Advanced

Name of the Formatting Flag Purpose

ios::showbase display an integer’s base

ios::showpos indicates positive numbers by using the + sign

ios::skipws skip whitespaces

* ios::dec display integers in base 10.

ios::hex display integer in base 16

ios::oct display integer in base 8

*ios::fixed use fixed notation for displaying floating point numbers

ios::scientific use scientific notation (i.e. using exponential)

*ios::left align left

ios::right align right

* indicates that these flags are already set by default.

By the way, these flags are usually known as IOS Formatting flags. If you want to play around with these flags, then you should make use of

setiosflags(names-of-flags)

or

resetiosflags(names-of-flags)

An example to illustrate the use of these formatting flags is given below:

#include <iostream.h> #include <iomanip.h> int main( ) {

cout<<setiosflags(ios::showpos)<<20; cout<<endl<<setiosflags(ios::scientific); cout<<300.4567; return 0;

}

421

Useful Classes & Funtions

Page 423: C++ Basics to Advanced

The output is:

+20 +3.004567e+002

Beware: All the formatting flags come under the namespace std::ios::

This is why we have used ios::scientific and ios::showpos. This will be dealt with in the section on ‘namespaces’.

422

Useful Classes & Funtions

Page 424: C++ Basics to Advanced

Useful Classes and Functions - II

The following topics are covered in this section:

• Custom Manipulators • Strings

Creating New Manipulators (Custom Manipulators)

In the previous chapter we dealt with how to overload << and >>. C++ also provides a way to create your own manipulators.

You can create your manipulators to work along with ‘cout’ (output stream object) or with ‘cin’ (input stream object). Let us consider an example of a manipulator dealing with ‘cout’. The syntax for creating a manipulator is:

ostream& name (ostream &str) { //code to be executed; }

In the case of manipulators to be used with input stream, only one modification is necessary.

istream& name (istream &str) { //code to be executed; }

Remember: You should return the stream at the end of your manipulator definition. Only then can you use your manipulator in cascaded operation.

#include <iostream.h>

ostream& divider (ostream &str) { str<<"--------------------------------"<<endl; return str; }

int main( ) { int x; cout<<divider<<"Enter an integer value : "; cin>>x; cout<<divider<<"The value you entered is : "; cout<<x; return 0; }

423

Useful Classes & Funtions

Page 425: C++ Basics to Advanced

The output is:

--------------------------------

Enter an integer value : 98

--------------------------------

The value you entered is : 98

In the above program we have created a new manipulator called ‘divider’. This will simply display a series of dashes on the screen as can be seen in the output. This manipulator can be used for objects belonging to ‘ostream’. Since ‘cout’ belongs to ‘ostream’, we can use this manipulator to operate only on output streams.

In a similar way you can also create manipulators to operate on input streams as well. The advantage of creating your own manipulators is that you can reduce on the amount of typing and make your code easier to understand.

Stream Formatting Member Functions:

The alternative to manipulators is to use the member functions available in streams. Manipulators can be used to perform all the stream-formatting that can be done using member functions.

Each stream has a set of format flags associated with it (just like what we have seen earlier). There are member functions available to either set/reset these format flags (the information is formatted depending on the setting of these flags).

The functions available are:

• setf (format-flags); • unsetf(format-flags); • width (int n); • precision (int n); • fill (char c);

These functions can be called from streams using the dot operators (since they are all member functions). The setf( ) and unsetf( )functions are used to set and reset the format flags respectively. The list of format flags used are the same as those described for manipulators. The bitwise OR operator ( | ) can be used to set or reset more than one flag using setf( ) or unsetf( ).

Strings

There are two methods for creating and using strings. A character array terminated in a null character (‘\0’) is called a string but in new compilers you can make use of a class called ‘string’. There are a few reasons why you might prefer to use the existing string class instead of creating a character array. The problem with a character array is that many (in fact none) of the operators will work with character arrays. You cannot use ‘+’ and expect the two

424

Useful Classes & Funtions

Page 426: C++ Basics to Advanced

arrays to get added (adding in this case would mean concatenation of the two character arrays). You cannot use the assignment operator and assign a value to your character array. Many beginners make this fundamental mistake.

int main( ) { char name[40]; name="John"; //wrong cout<<name; return 0; }

It might seem as if the above coding is correct but if you compile this program you will get an error. The compiler will not accept such assignments but the following is correct:

int main( ) { char name[40]; cin>>name; cout<<name; return 0; }

We can obtain the input for a character array from the user and store it in the array ‘name’. This will work but assignment using the = operator will not work.

Most of the new compilers have a class called ‘string’. Since this is a class, many operators have already been overloaded such that they work effectively with string objects. Thus it would be better to make use of the string class in many instances. The string class is present in the header file string. The concept of namespaces will be explained later in this section. But for the time being just know that if you want to add headers according to the latest standards, then you should not make use of .h in the #include directive.

#include <iostream> // New style headers #include <string> using namespace std; int main( ) { string name; name="John"; cout<<name; return 0; }

The output is:

John

There are many constructors available in the string class, which allows you to initialize the string in different ways. The main ones are (‘name’ is the string object that is created):

425

Useful Classes & Funtions

Page 427: C++ Basics to Advanced

string name; //an empty string string name("John"); //initialized to John string name(another-string); //initialized to value of another-string string name(5,’j’); //initialized to "jjjjj"

Again there are different ways to assign values to strings:

name = "John"; name = another–string;

For example:

int main( ) { string s1("Jenny"); string s2; s2=s1; cout<<s2; return 0; }

s2 will now have the string "Jenny".

Similarly, you can even use the + operator on 2 strings. Consider the same program given above. If you write:

s2 = s1+s2;

The value in s2 will be: JennyJenny

Remember: The + operator (when operating on string objects) will concatenate the two strings.

Though you have created a string called ‘s1’, you can still access the individual characters of the string as well. You just need to use the index number for accessing particular characters. For example:

cout<<s1[1];

will give the output as:

e

In Jenny, ‘e’ is the second character and this is displayed. You might be led into thinking that if you want to change the first character of the string ‘s1’, all you need to do is type:

s1[0] = ‘k’;

The result will be compiler errors. You cannot change characters directly using the = operator. For this purpose the string class has a member function called

at(character-position)

426

Useful Classes & Funtions

Page 428: C++ Basics to Advanced

The program would be as below:

int main( ) { string s1("Jenny"); string s2; s2=s1; s2=s1+s2; cout<<s2; s1.at(0)='K'; cout<<endl<<s1; return 0; }

The output will be:

JennyJenny Kenny

Remember: Strings are like character arrays and they also start from the index number 0 (not 1).

Comparison:

You can compare between two strings using the > or <. How do you think two strings are compared? See the following results:

e > bee - True Hello>hello - True dear>bee - True He>HE - False

From the above results it is clear that upper case letters are greater than corresponding lower case letters. An alphabet that comes later in A to Z is greater than one that comes before it. Comparison does not depend on the length of the strings.

To find the length of a string, you can make use of the length ( ) member function of the string class.

If s1 is a string object with the value of "Jenny", then

s1.length( )

Will return a value of 5.

Extracting strings:

You can extract a part of the string by using the substr ( ) function. The syntax is:

substr(position-from-where-you-want-to-start, number-of-characters-to-extract);

427

Useful Classes & Funtions

Page 429: C++ Basics to Advanced

This function can be called only through a string object (because it is a member function of the class ‘string’).

int main( ) { string s1("Jenny is a wonderful person"); cout<<s1.substr(6,4); return 0; }

The output is: is a

Searching in Strings:

There are two functions for the purpose of searching for particular characters or strings within strings. These are the find (find) and reverse find (rfind) member functions. Both functions will return the position (an integer) of the place where the particular character/string is located.

Remember: The results that you get are always starting from 0. So if your result is 2, it actually means the 3rd character.

#include <iostream> #include <string> using namespace std; int main( ) { string s1("penny is a wonderful person"); cout<<endl<<s1.find("is"); cout<<endl<<s1.find('w'); cout<<endl<<s1.find('p'); cout<<endl<<s1.rfind('p'); }

The output is: 6 11 0 21

The first three results should be quite clear. The function find ( ) will start searching from the left side of your string object. But rfind ( ) will start from the right-end of the string object. Once rfind ( ) locates a match it will display the result by counting how many characters from the left the match was located.

Remember: find ( ) searches from the left and gives result by counting from left. rfind( ) searches from the right, but gives the resultant position by counting from left.

What if find ( ) or rfind ( ) don’t find a match? They’ll return some erroneous number (something that will be outside the length of the string itself!).

428

Useful Classes & Funtions

Page 430: C++ Basics to Advanced

429

Useful Classes & Funtions

Page 431: C++ Basics to Advanced

Useful Classes and Functions - III

The following topics are covered in this section:

• Character Arrays • Mathematical Functions

Some compilers may not permit String Objects

The string objects that we discussed above can be created only in new C++ compilers. In older compilers you cannot create an object of type string (this is because the string class is not defined). In such cases you have to use character arrays terminated by a null character for strings. But there are many functions provided for operating on such strings as well. A few of these are discussed below.

These functions were part of the standard C library and it has been incorporated into the C++ library as well. To use these functions, if you are using an old C++ compiler, you should include the header file string.h. If you have a new C++ compiler that supports namespaces, then you should use the header <cstring>

Copying strings and concatenating strings:

For copying we have a strcpy( ) function and for concatenation we have the strcat( ) function.

int main( ) { char s1[20]; char s2[20]; strcpy(s1,"hi"); strcpy(s2,"bye"); strcat(s1,s2); cout<<s1; return 0; }

The output is:

hibye

The function strcpy ( ) will act only on character arrays. Similarly the concatenation function strcat ( ) will also act only on two character arrays. You cannot create a string object and then use strcpy ( ).

string s1; strcpy(s1,"hi");

will lead to an error (because s1 is not declared as a character array).

430

Useful Classes & Funtions

Page 432: C++ Basics to Advanced

String length: This function will give you the length of the character array.

strlen(character-array)

String Comparison:

The function will return an integer. The syntax for the function is:

int strcmp(char-array s1, char-array s2);

If s1>s2, then the result will be 1. If s1<s2, then the integer returned will be negative. If s1=s2, then the result will be 0.

There are many other functions that you might find useful but make sure as to whether you can create a string object or whether you have to work with character arrays. Depending on what you use, you will have different functions available.

Remember: New C++ compilers that permit the creation of string objects will also support all the old functions available for character arrays. You should be careful that you don’t use those functions on string objects.

Character conversion functions:

There are two functions for converting a character into uppercase or into lowercase. The two functions are:

int toupper (int ch); int tolower (int ch);

These functions are defined in the header file <ctype.h>

char letter = 'a'; letter = toupper(letter); cout<<letter; // ‘letter’ is now ‘A’

431

Useful Classes & Funtions

Page 433: C++ Basics to Advanced

Mathematical Functions

In many of your program you might want to perform some special mathematical operations (other than the basic ones). For this purpose it will be helpful if you know about the existing mathematical functions provided in C++. In older compilers you have to make use of the math.h header file. If you are using a new compiler and adding headers through namespaces, then you should include the header <cmath>

Descriptive Name

Purpose Syntax Similar functions

Remarks

Trignormetric functions (and hyperbolic)

calculate the cosine of a given angle

double cos(double angle)

sin, tan, cosh, sinh, tanh

Remember to give the angle in radians (and not in degrees). Radians are in terms of the constant ‘pi’ (one radian equal 180 degrees).

Inverse trigonometric functions

returns the value of the angle in radians

double acos(double value)

asin, atan It is the opposite of the cos, sin and tan functions. Value should be between –1 and 1 when using acos ( ) or asin ( ) functions.

Hypotenuse Pass two arguments, this function will find the hypotenuse.

double hypot (double val1, double val2)

For example: if we code:

cout<<hypot(3,4);

the output will be 5.

Square root Returns the square root of the given number.

double sqrt (double val)

Logarithm Functions to calculate natural logarithm and also base 10 logarithms.

double log (double val)

To calculate natural logarithm of ‘val’.

double log10 (double val)

To calculate logarithm of ‘val’ to the base 10.

log10(10) = 1

log(10) = 2.3

Absolute value Two functions to obtain the absolute value of a

int abs (int val)

The abs( ) function will operate on integers and return

double fabs (double val)

To retain the decimal

fabs(2.1) = 2.1

fabs(-2.1) = 2.1

432

Useful Classes & Funtions

Page 434: C++ Basics to Advanced

number. Absolute value means only positive numbers.

integers. places use the fabs( ) function.

abs (2.1) = 2.

abs (-2.1) = 2

abs( ) will ignore decimal places.

Raising to power

Raise a base to the power of an exponent. If we say, 23, then 2 is the base and 3 is the exponent.

double pow(double base, double exponent)

pow(2,3) is equal to 8.

Exponent This function will raise the natural logarithm base ‘e’ to the power of the argument provided. The value of e is 2.718.

double exp(double power)

cout<<exp(1);

The result will be: 2.718

Rounding Off values:

ceil:

The ceil ( ) function is used for rounding up a number to an integer. But it doesn’t exactly do rounding up. It will return the lowest integer that is greater than the number you gave as argument.

double ceil (double val)

If val=2.1, then the returned value will be 3. If val= -2.1 then the returned value will be –2.

Floor:

This is again similar to ceil ( ) function except that this will return the highest integer that is less than the value you provided.

double floor (double val)

Suppose that val=2.1, after the floor ( ) operation you will get a result of 2.

If val = -2.1 then the result will be –3.

433

Useful Classes & Funtions

Page 435: C++ Basics to Advanced

Random Numbers:

The rand ( ) function can be used to generate random numbers. The srand( ) function can be used to initialize the random number generator. srand ( ) will not return any value while the rand ( ) will return a random integer.

• int rand( ) • void srand (unsigned int val)

The example below should clarify your doubts about srand ( ).

int main( ) { srand(10); cout<<rand()<<endl; cout<<rand()<<endl; srand(10); cout<<rand( ); return 0; }

The output will be:

71 16899 71

Once initialized to the same number, the random number generator will generate the same random number. In the above case, srand (10) initializes the random number generator to 71.

434

Useful Classes & Funtions

Page 436: C++ Basics to Advanced

Useful Classes and Functions - IV

The following topics are covered in this section: • Good Programming Practices • Typedef • Bitwise Operators

Good Programming Practices

First of all, there isn’t any international standard for the purpose of writing C++ programs. You are free to write the way you want to. This section is something like a section of tips that you could use.

• Always before you start coding, it is better to have a rough idea written on paper. This is called a pseudocode or an algorithm. You will realize the advantages of this when you start coding larger programs.

• Give a good thought before creating classes. Have a clear idea about what data you want to hide and what are the methods you need.

• Whenever you make use of mathematical calculations in your program, do not use long statements. For instance do not type:

x = 3+4*10/5+1-4/2;

Writing something like this might make one feel that the person who wrote this was a genius! Avoid such statements (even the programmer himself would have to break his head before he is certain of the answer). Use simple brackets to break the long calculations into smaller parts as shown below:

( 3 + ( 4 * (10/5) ) + 1 - (4/2) );

Now, anyone will be able to say that the result of the above statement is 10. See how a few brackets can make a big difference!

• Leave adequate whitespaces in your C++ programs. Whitespaces are not taken into consideration by the C++ compiler. So by using whitespaces you can keep your program clean.

( 3 + ( 4 * (10/5) ) + 1 - (4/2) ); is better than (3+(4*(10/5))+1-(4/2));

• Indent your C++ programs properly.

for (i =0; i<10 ; i++) { for (int j = 0 ; j<10 ; j++) { cout<<i*j; } }

435

Useful Classes & Funtions

Page 437: C++ Basics to Advanced

• Use braces to clearly define your ‘for’ loops, ‘if’ and while blocks. By using braces to define the starting and ending of these blocks, there will be no confusion as to what statements come under the particular block.

{ for (int i = 0 ; i<5 ; i++) cout<<endl<<i; return 0; }

will print the output as:

0 1 2 3 4

Check out the modification of the above code. Here we want to print the number and also its square.

int main( ) { for (i = 0 ; i<5 ; i++) cout<<endl<<i; cout<<endl<<i*i; return 0; }

will give the output as:

0 1 2 3 4 25

The statement i*i is not considered as part of the ‘for’ loop itself. This problem can be avoided if you clearly specify the ‘for’ block.

int main( ) { for (int i=0;i<5;i++) { cout<<endl<<i; cout<<endl<<i*i; } return 0; }

436

Useful Classes & Funtions

Page 438: C++ Basics to Advanced

Now there is no confusion about what is included in the ‘for’ block.

• Make sure your variable names are not too long and also try to give some meaningful names for variables. The same rule applies for names of functions, classes and objects as well.

• Use lots of comments throughout your program. You should always write what you are trying to do within comments. This way, even if you continue your work after one week you will know exactly what you were attempting to do. Use comments as frequently as possible (for functions you can describe about the function in short; for classes you can give a brief idea about the class etc.). Don’t use comments for self-explanatory code.

• A new line is not a statement terminator. For instance when using ‘cout’, if the line to be displayed seems to be very long in your compiler you can split it up in two lines as follows: cout<<"This is a really really really" " long line that i am typing";

• In classes, make sure that you provide all the needed constructors. It is always good to provide a no argument constructor that will initialize all your member data.

• Avoid using macros for constants and functions. Make use of C++ inline function and the const keyword for these purposes.

• Be very careful while using the ‘new’ operator. Make sure that every ‘new’ has a corresponding ‘delete’ in the program.

• Use correct data types (particularly whenever using numerical data types like float, int or double). If an ‘int’ is sufficient don’t go in for ‘double’ (since this occupies more memory and also calculations involving double will take longer). Do not rely on implicit type conversions.

• Minimize the amount of work performed within loops. Perform calculations which do not involve the iteration variable outside the loop.

• Avoid using function calls within a ‘for’ loop conditional expression. A commonly used for loop is:

for (int j=0; j<=strlen(str);j++) { }

This forces the strlen ( ) function to be invoked for every iteration (every function call involves a lot of overhead). You can rewrite the above code as:

int len=strlen(str); for (int j=0; j<=len;j++) { }

Now the strlen( ) function is invoked only once.

437

Useful Classes & Funtions

Page 439: C++ Basics to Advanced

Typedef

Typedef basically stands for type definition. The keyword ‘typedef’ lets you create your own data types; well, not exactly your own data types but your own names for the standard data types. Consider the following:

int x;

means that x is declared to be an integer variable. Now, using typedef you can give a different name for ‘int’. The syntax for using different names instead of the standard ones is:

typedef standard-name new-name;

For example:

typedef int weight; weight x=50;

In this case, we are using the name of ‘weight’ instead of the standard name ‘int’. Thus you can now declare your variables by using ‘weight’ instead of ‘int’. Basically we are just giving ‘int’ a new name called ‘weight’.

Remember: By using typedef we are only giving a new name for an existing standard data type. We are not creating a new data type.

Bit-wise Operators

This section will explain another set of operators. Bit-wise operators will act on binary digits. Maybe you know that C programming supports assembly level programming. In assembly level programming there is a definite necessity for operating on bits (mind you, not decimal numbers but binary digits). There might be other applications where you would like to manipulate data at the bit level. For example you could use it for hiding data within images (this is called steganography). You can make some bit level manipulations in your image file (such that the image appears to be the same) and hide data within the file.

Basically if you want to operate on the individual bits instead of operating on the number as a decimal number, then you should use bitwise operators. Bit-wise operators can also be used to perform some calculations faster or with lesser number of steps. The bit operators are:

• AND (&) • OR ( | ) • EX-OR (^) • NOT (~)

If you’ve done a course in digital electronics you will know about the AND gate, OR gate, EXOR gate and NOT gate. These operators are exactly similar to these logic gates. Let us assume that ‘x’ and ‘y’ are two separate bits (they are binary digits not decimal numbers). The effect of each operation on these bits is shown below:

438

Useful Classes & Funtions

Page 440: C++ Basics to Advanced

x y x & y

0 0 0

0 1 0

1 0 0

1 1 1

AND Operation The AND operator will give a result of 1, only if both ‘x’ and ‘y’ are equal to 1. Similarly, the OR operator will give a resultant value of 1 if ‘x’ or ‘y’ value is 1.

EX-OR (stands for exclusively OR). This operator will yield a value of 1 only if ‘x’ alone is 1 or if ‘y’ alone is 1.

The NOT operator is also known as the complementary operator. If you give an input of 1, the output will be 0 and vice-versa.

x y x | y (x OR y)

0 0 0

0 1 1

1 0 1

1 1 1

OR Operation

x y x ^ y (x EXOR y)

0 0 0

0 1 1

1 0 1

1 1 0

EX-OR Operation

x ~x (NOT x)

0 1

1 0

NOT Operation

439

Useful Classes & Funtions

Page 441: C++ Basics to Advanced

First of all, you should have an idea about how these operations will work on a group of binary digits. Eight bits make up a byte and we have already seen the process of conversion from decimal to binary format in the first chapter itself.

Let us assume that we have two numbers: 128 and 255. Their binary representations will be: 0100 0000 and 1000 0000.

0 1 0 0 0 0 0 0 When you perfom bitwise AND operation, each pair of bits

& 1 0 0 0 0 0 0 0 are ANDed individually

------------------

0 0 0 0 0 0 0 0 Resultant value (0)

0 1 0 0 0 0 0 0 OR operation performed

| 1 0 0 0 0 0 0 0

------------------

1 1 0 0 0 0 0 0 Resultant value (192)

0 1 0 0 0 0 0 0 EXOR operation performed

^ 1 0 0 0 0 0 0 0

------------------

1 1 0 0 0 0 0 0 Resultant value (192)

0 1 0 0 0 0 0 0 NOT operation performed on decimal value of 64

~ ------------------

1 0 1 1 1 1 1 1 Resultant value (191)

440

Useful Classes & Funtions

Page 442: C++ Basics to Advanced

If you apply the tabulation that was given, you will get the results shown above. We can do obtain the same result using a C++ program.

int main( ) { unsigned char x,y; x=128; y=64; cout<<endl<<"x AND y = "<< (x&y); cout<<endl<<"x OR y = "<< (x|y); cout<<endl<<"x EXOR y = "<< (x^y); return 0; }

The output will be:

x AND y = 0 x OR y = 192 x EXOR y = 192

There are two more bit-oriented operators. They are called the shifting operators:

• The left shift operator (<<) • The right shift operator (>>)

Their syntax is:

operand << number-of-bits-to-shift

operand >> number-of-bits-to-shift

Let us consider the two operations with the help of a variable ‘x’.

Let the value of ‘x’ be 96 (and let us assume that the representation is unsigned representation). Therefore in binary form x = 0 1 1 0 0 0 0 0

0 1 1 0 0 0 0 0 >> 1 (means shifting right by one bit will give)

= 0 0 1 1 0 0 0 0 (equivalent decimal value of 48)

0 1 1 0 0 0 0 0 >> 2 (means shifting right by two bits will give)

= 0 0 0 1 1 0 0 0 (equivalent decimal value of 24)

This is the right shift operation. As you can see, once the bits are shifted their original place is filled by a zero. Remember that we are assuming unsigned representation (for signed representation there is a slight difference that we shall see later).

The same logic applies for left shift operation as well.

0 1 1 0 0 0 0 0 << 1 (means shifting left by one bit will give)

441

Useful Classes & Funtions

Page 443: C++ Basics to Advanced

= 1 1 0 0 0 0 0 0 (equivalent decimal value of 192)

0 1 1 0 0 0 0 0 << 2 (means shifting left by two bits will give)

= 1 0 0 0 0 0 0 0 (equivalent decimal value of 128)

In the second case of left shifting, we have ended up losing one of the bits. This is because the shifting operation is not a rotation operation. Once you shift a bit left beyond the 7th position (i.e. the MSB) or to the right beyond the 0th position (i.e. the LSB) you will lose the bit.

Beware: Shifting past the 7th bit to the left will lead to loss of the bit if the result is being stored in a ‘char’ (because a character variable can take up a maximum of 1 byte only). Suppose we have an integer variable named ‘z’ then it will occupy 4 bytes (or 2 bytes depending on the OS). If we store the value of 96 in z, it will be stored as shown below:

32 bits for z

00000000 00000000 00000000 01100000

This clearly indicates that you can shift the bits to the left by more than one place.

Another interesting point to note is that when we right shift by one bit, we are in effect dividing the number by 2 (48 = 96/2 and 24 = 48/2). Similarly when we left shift by one bit it is equivalent to multiplying the number by 2 (192 = 2 * 96).

For signed numbers there is a possibility for a slight modification. Signed numbers were handled in the first chapter. In signed numbers, the 7th bit is used to denote the sign (1 for negative and 0 for positive). When you perform shifting operations on signed numbers, usually the vacant spaces will be filled up by the sign bit. In some compilers it may be filled up by 0 always irrespective of whether the number is signed or unsigned. So be careful while performing bit operations on signed numbers. By the way, the sign bit is the MSB of the binary number (if it an 8-bit number then the 7th bit is the sign bit. If it is a 16-bit number then the 15th bit is the sign bit. For easier explanation we have considered the case of an 8-bit number).

During shifting it is not that only the 1s move. The 0s and 1s will be shifted together.

0 0 1 0 1 0 0 0 << 2

= 1 0 1 0 0 0 0 0 Result (bold indicates the original bits)

In C++, for shifting the coding will be:

unsigned char y; y=96; cout<<endl<<"Shifting left by 1 = "<<(y<<1); cout<<endl<<"Shifting right by 1 = "<<(y>>1);

442

Useful Classes & Funtions

Page 444: C++ Basics to Advanced

Preprocessor Directives

The preprocessor is part of your compiler and when you compile a program the preprocessor is the first person to scan through your coding. It is particularly bothered with some specific instructions that are meant exclusively for it. These instructions are known as preprocessor directives and the preprocessor searches your program for them. How does it know that some code is meant for it? Preprocessor directives start with the # (hash) symbol and also they need not be terminated by a semi-colon. You have different types of preprocessor directives:

• #include • #define • #undef • #if….#elif…..#else…..#endif • #ifdef…..#ifndef • #line • #error • #pragma

#include:

You’ve already been using this in all of your C++ coding. It simply includes the contents of whatever you include into the source file. One favourite C++ question is: What’s the difference between

#include "iostream.h" #include <iostream.h>

The only difference is in the way the compiler searches for the iostream.h file. If you use the double quotes, the compiler will start searching for the header file in the directory where you have your source file. If it doesn’t find it here, it will then search in the directory where the compiler is supposed to search for header files.

If you use the angle brackets, the compiler will search in the directory supposed to be having all header files. You usually have an option in your C++ compiler where you can change the default directory used by the compiler (of course don’t change it unless you have specifically made a new directory with the header files).

#define: We’ve seen about this in the section on constants. This used to be the method for C programmers to create constants. Constants created using #define are also called as "macros". It is not only used to create constants but can also be used for defining functions. It is equivalent to the inline function option available in C++. Of course in C++ it is not advisable to use #define because you can make use of the inline keyword and also the const keyword.

#undef: This is the opposite of #define. It will undefine anything that you defined earlier. For example:

#define speed 100 #undef speed #define speed 200

443

Preprocessor Directives

Page 445: C++ Basics to Advanced

#if….#elif…..#else…..#endif : This is equal to our if….else if….else statement construct. Endif is used at the end to indicate the end of the ‘if’ block.

#include <iostream> using namespace std;

#define speed 200

int main( ) { #if speed= =200 cout<<"Speed is 200"; #elif speed = =100 cout<<"Speed is 100"; #else cout<<"I don't know what's the speed!"; #endif return 0; }

The output is:

Speed is 200

#elif is actually a shortened expression for else if. Instead of mentioning the directives inside the main ( ) function you could use them for defining the value of some other value outside the main ( ) function.

#include <iostream> using namespace std;

#define speed 200 #if speed= =200 #define brake 100 #elif speed= =100 #define brake 50 #else #define brake 25 #endif

int main( ) { cout<<brake; return 0; }

444

Preprocessor Directives

Page 446: C++ Basics to Advanced

#ifdef…..#ifndef:

#ifdef means if the particular variable is defined do something. #ifndef means if the particular variable is not defined do something.

The syntax is:

# ifdef variable-name

//body

#endif

This can also be written as:

# if defined (variable-name)

//body

#endif

Check out a short program given below:

#include <iostream> using namespace std;

#define speed 200

int main( ) { #ifdef speed cout<<"Speed Defined"; #else cout<<"Speed not defined"; #endif #ifndef brake cout<<endl<<"Brake not defined"; #endif return 0; }

Output is:

Speed Define Brake not defined

As can be seen above, the variable ‘brake’ is not defined in the program. Usually #ifndef and #endif are used in multiple file programs. This is explained later in the chapter.

445

Preprocessor Directives

Page 447: C++ Basics to Advanced

#line:

All your programs have a certain number of lines (depending on the size of your coding). When you get errors, the compiler will inform you about the line number, which has an error. Using the

#line line-number

you can change the starting line number that is used by the compiler for the program. This might not seem very useful to you but anyway it’s better to know about the various directives that exist.

#pragma:

This is used to make changes in your compiler settings. The use of this directive is compiler-dependent since each compiler will have its own pragma directives. For example the #pragma pack ( ) directive (which we discussed in an earlier chapter with reference to padding of structures) is available in VC++ but not in Turbo C++.

The preprocessor also recognizes two operators: # and ##.

1. The String operator (#): When this operator is used in a macro definition, it will convert whatever follows it into a string.

#include <iostream> using namespace std; #define disp(val) cout<<"You passed : " #val int main( ) { disp(20); return 0; }

The output is:

You passed : 20

Similarly if you write:

disp(Hi);

the output would be:

You passed : Hi

Actually, the operator # is equal to inserting two double quotation marks (this makes anything into a string).

446

Preprocessor Directives

Page 448: C++ Basics to Advanced

2. Concatenation Operator (##): This is used to concatenate the operands. The operands could be anything.

#define conc(a,b,c) a ## b ## c

int main( ) { cout<<conc("hi","and","bye"); return 0; }

The output is: hiandbye

It might seem crazy to use such operators, but it is better to know about the various features that exist. Actually, programmers rarely use these two operators.

Standard defined Macros

The compiler already has some defined macros. They are: __FILE__ gives the filename __LINE__ gives the line number __DATE__ gives the date the code was compiled __TIME__ gives the time the code was compiled __cplusplus this is defined when the source code in compiled as a C++ code

Check out an example:

#include <iostream> using namespace std;

int main( ) { cout<<endl<<"Filename : "<<__FILE__; cout<<endl<<"Line no. : "<<__LINE__; cout<<endl<<"Compiled date : "<<__DATE__; cout<<endl<<"Time : "<<__TIME__; return 0; }

The output will be:

Filename : C:\tutorial\prepro\prepro.cpp Line no. : 7 Compiled date : Aug 10 2003 Time : 11:36:12

Beware: The standard macros start with a double underscore and end in a double underscore (it is __FILE__ not _FILE_ ).

447

Preprocessor Directives

Page 449: C++ Basics to Advanced

Namespaces and new headers

The programs that we've seen so far are not wrong but they can't be called as 100% C++ programs either. The reason is because we've been making use of header files (such as iostream.h etc...). Making use of these .h files is more similar to C programming. Of course it isn't wrong to use them in C++ but there is a better method provided in C++. This method makes use of namespaces.

Instead of # include <iostream.h> we shall use the following two lines: #include <iostream> using namespace std;

Firstly take note of the fact that there is no ‘.h’ in the include statement. This is the format of the new style headers used in C++. They do not use .h files. Old C++ compilers may not support namespaces. In case you are using such a compiler then you have to stick to the old method of #include with the ‘.h’ extension. But all the latest C++ compilers will support namespaces and they will also support the old style headers.

When C++ was created it used header files like those used in C programming. But then changes were made. Though it still supports the old header files, they are not recommended. You know that iostream.h is a file. C++ created a new style of its own. In this method we do not specify filenames. Instead it is a method that makes sure that appropriate definitions required by the C++ library have been declared. The new style headers are not filenames and hence the .h extension is left out.

The tabulation below lists out the various headers we have used and their equivalent new style headers.

Old style header name Equivalent new style header

Purpose

iostream.h <iostream> General purpose I/O

fstream.h <fstream> File I/O operations

iomanip.h <iomanip> Manipulators

string.h <cstring> character array string operations

math.h <cmath> Mathematical Functions

stdlib.h <cstdlib> Memory allocation, numeric and system functions

- <string> (supports the string class).

String objects

448

Namespaces & Newheaders

Page 450: C++ Basics to Advanced

If you notice in the tabulation above, some of the new style headers make use of the prefix ‘c’ in the header file. For example:

stdlib.h

is now:

<cstdlib>

These headers were actually part of Standard C and since C++ supports C these headers were retained. Though these headers are supported they could be eliminated in the future.

When using a new style header you can make use of the namespace statement:

using namespace std;

A namespace is a region of declaration. The purpose of namespace is to avoid collision of identifiers (i.e. to avoid collision of variables or class names or function names etc.). Elements declared in one namespace are different from those declared in another. Always make use of C++ new style headers whenever you write a C++ program.

The first.cpp program using namespaces:

You can make use of new style headers in all the other programs that we have seen so far.

Thus the first program you wrote will become:

# include <iostream> using namespace std; int main( ) { char letter; cout << "Enter any letter" ; cin >>letter; cout << "The letter you entered is : " <<letter; return 0; }

A closer look into Namespaces Why we need Namespaces?

Namespaces were introduced to avoid name collisions. When many people started to create libraries for different purposes, each person made use of global variables. These variables are visible to the entire program and can be accessed by any function. Now, when a programmer made use of two or more libraries, there is a good chance that some variable names might coincide or some identifier used by the programmer could clash with that used by the library creator. This can lead to serious problems.

449

Namespaces & Newheaders

Page 451: C++ Basics to Advanced

The problem can even be extended to function names( ). You could end up having a program that has two functions performing different tasks, having the same name and arguments. For example, both libraries may have the function draw( ). But both may have a different meaning to the function. So when you use the function draw ( ), the compiler would have a problem in deciding which library function it should use. Namespaces were created to solve these problems.

What namespace does?

Namespace localizes the visibility of names declared within it (the names are only visible within that particular namespace). So when you refer to some name, you can specify the namespace to which it belongs to avoid conflicts.

Syntax:

General form for creating a namespace is:

namespace name { // declarations }

Anything defined within a namespace is only within the scope of the namespace and not available outside.

Example:

#include <iostream> using namespace std;

namespace bact //A namespace called bact {

int life;

class bacteria //class belonging to namespace bact { private: int step;

public: bacteria( ) { cout<<"\nCreated Bacteria"; } };

} //End of bact namespace

450

Namespaces & Newheaders

Page 452: C++ Basics to Advanced

namespace vir //Another namespace ‘vir’ {

int life; class virus { private: int step; public: virus( ) { cout<<"\nCreated virus"; } };

} int main( ) { bact::life=1; vir::life=0; bact::bacteria fever; vir::virus cholera; cout<<"\nThe bacteria has a life : "<<bact::life; cout<<"\nThe virus has a life : "<<vir::life; return 0; }

The output would be:

Created Bacteria Created virus The bacteria has a life : 1 The virus has a life : 0

Two namespaces ‘bact’ and ‘vir’ have been created in the program. Both contain a variable using the same identifier (‘life’) and both have a class within them. Within the main ( ) function, when we want to refer to the variable ‘life’ remember the following:

• ‘life’ is visible within 2 namespaces. So when referring to it, you have to use the scope resolution operator to access it (to indicate which ‘life’ you are referring to).

Similarly when creating an object belonging to the class ‘bacteria’, remember:

• The class ‘bacteria’ is present within the namespace ‘bact’. So you have to specify the namespace and then the scope resolution operator.

But once the object has been created, you needn't keep specifying the namespace (i.e. for calling member functions you needn’t specify the namespace again).

451

Namespaces & Newheaders

Page 453: C++ Basics to Advanced

The ‘using’ Keyword

In the previous section, you must have noticed that each time we refer to a namespace variable you have to make use of scope resolution operator. You also need to specify the namespace. Doing this repeatedly is a tedious job, especially if you are going to make use of a particular namespace frequently. Imagine how a simple program cluttered with the name of the namespace and the scope resolution operator everywhere would look. The best solution is to use the keyword using.

What about namespace std? The std namespace is the area in which the entire Standard C++ library is declared. Remember how we start our programs:

#include <iostream> using std namespace; int main ( ) { }

The ‘using’ statement informs the compiler that you want to use the ‘std’ namespace. The keywords cout, cin etc. are all present in the std namespace. You can also make use of the following method for writing programs:

#include <iostream> int main ( ) { int x ;

std:: cout<< "Enter a number : ";

std:: cin>>x; std :: cout<<x; return 0; }

In this case we haven’t specified the statement:

using namespace std;

Generally we don't use this method because we will use ‘cout’ and ‘cin’ very frequently in our coding. By the above method you will have to use std :: each time you use ‘cout’ or ‘cin’. It is very convenient to make use of

using std namespace;

before the main ( ) function to tell the compiler that by default use the ‘std’ namespace.

452

Namespaces & Newheaders

Page 454: C++ Basics to Advanced

There are two methods to make use of the using keyword:

using namespace name; //This is called as the Using Declaration

Example: using namespace bact;

or

using name :: member; //This is called as the Using Directive

Example: using bact :: life;

In the first case, you don't need to make use of scope resolution when you are referring to any variable or function belonging to namespace ‘bact’. In the second case, only the variable ‘life’ belonging to the ‘bact’ namespace is made visible. Check out the example below:

#include <iostream> //we’ve not specified using namespace std;

namespace bact { int life; class bacteria { private: int step; }; }

namespace vir { int life; class virus { private: int step; }; }

int main( ) { using namespace bact; //the compiler uses bact namespace life=1; //this refers to ‘life’ of ‘bact’ vir::life=0; bacteria fever; vir::virus cholera; std::cout<<"\nThe bacteria has a life : "<<life; std::cout<<"\nThe virus has a life : "<<vir::life; return 0; }

453

Namespaces & Newheaders

Page 455: C++ Basics to Advanced

In the main ( ) function, we have said

using namespace bact;

‘life’ is a variable belonging to ‘bact’ namespace and the class ‘bacteria’ is a class belonging to the ‘bact’ namespace. Since you’ve asked the compiler to use the ‘bact’ namespace, you don't need the scope resolution operator whenever you are referring to something from ‘bact’ namespace. Suppose you want to refer to the ‘life’ variable of the namespace ‘vir’, then you have to explicitly say so (using scope resolution).

Note that in the above program we are making use of

std::cout

because we haven’t mentioned:

using namespace std;

So, we need to explicitly state the namespace to which ‘cout’ belongs to (otherwise your compiler will say that it doesn’t know what ‘cout’ is).

What if we declare a variable outside the namespaces? In C++, if you declare a variable outside the main ( ) function and outside all other functions, then the variable becomes a global variable. If you declare a variable outside all namespaces, then the variable is now present in the global namespace. Check out the example below:

#include <iostream>

int life=0; //life in the global namespace

namespace bact { int life; //life in bact namespace class bacteria { private: int step; };

}

int main( ) { bact::life=1; ::life=0; std::cout<<"\nThe bacteria has a life : "<<bact::life; std::cout<<"\nThe organism has a life : "<<::life; return 0; }

454

Namespaces & Newheaders

Page 456: C++ Basics to Advanced

The output would be:

The bacteria has a life : 1 The organism has a life : 0

When we say:

::life

it refers to the ‘life’ present in the global namespace.

Beware: You cannot create namespaces within functions. Namespaces can be nested (one within the other).

455

Namespaces & Newheaders

Page 457: C++ Basics to Advanced

Advanced C++ - Part IIIa (Creating Libraries)

Creating Libraries

We still haven’t achieved implementation hiding in the real sense. Any user of your class can open the *.cpp files and modify your function definitions. Generally, the creator of a class wouldn’t want to let a user do this. Also, the user will now have to copy the car.cpp file into his project directory and compile it before he can run his program. Wouldn’t it be simpler if the user could just put a #include statement in his project without having to bother about someone else’s *.cpp files?

This is where a library comes into the picture. The class creator can create a library file as well. When others want to use his class, they have to just include his header file and also ask the linker to search for the class implementation in the new library. A library is a collection of object files (i.e. the output of the compiler stage). Let’s create a library to make the concept clear. (We’ll look at the process to be followed in VC++. The appendix section will deal with C++ on Unix/Linux).

First we’ll need to create a library. Go to File and choose New. In the options tab choose Projects.

Name the project as carlib and click the OK button. The next dialog box will ask if you want to include MFCs or other files. Don’t choose any of these (for this library we don’t require anything extra).

Add a new header file (carlib.h) to this project. Include the following code:

456

Creating Libraries

Page 458: C++ Basics to Advanced

//carlib.h #include <iostream> using namespace std;

class car { private: char color[20]; int speed;

public: car( ); void input( ); void display( ); ~car( ); };

Include a source file (named car.cpp) in this project. The code for this will contain the implementation details of the class ‘car’.

#include "carlib.h"

car::car( ) { cout<<endl<<"New car created."; }

void car::input( ) { cout<<endl<<"Enter the color : "; cin>>color; cout<<endl<<"Enter the top speed : "; cin>>speed; }

void car::display( ) { cout<<endl<<"The color is : "<<color; cout<<endl<<"The top speed is : "<<speed; }

car::~car( ) { cout<<endl<<"Your car is destroyed."; }

Now compile car.cpp. If you have no errors then go to ‘Build’, and choose the option "build carlib.lib". If there are no errors then you are successful in creating your library. How does one use this?

457

Creating Libraries

Page 459: C++ Basics to Advanced

If someone wants to use your library then all you need to do is supply them with the carlib.lib file and carlib.h header file. The *.lib file contains the object code while the carlib.h file just contains the interface to your class. Anyone who uses your class can take a look at the *.h file to know what functions are available for them to use (of course for a more complex class you would have to provide some documentation in the header file).

Let’s use this library in a program to check if it works. Create a new application project and include a C++ source file into the project. Name it as mycar.cpp and type the following code:

//mycar.cpp #include "carlib.h" int main( ) { car mine; mine.input( ); mine.display( ); return 0; }

Now compile mycar.cpp and voila! You should have an error saying:

c:\tutorial\mycar\mycar.cpp(2) : fatal error C1083: Cannot open include file: 'carlib.h': No such file or directory

The error message is self-explanatory. We haven’t told the compiler as to where the file carlib.h is present. You have 2 options at this point:

1. Copy that file into your current project directory and include it in this project. 2. Your compiler will search for include files in particular directories. You can

add the name of the directory where your *.h file is present to this list (this is usually present in the Project Settings option).

Since we are planning to distribute our library to a 3rd person, we’ll take the first route. Copy the file carlib.h into your current project directory. Then go to VC++ and make sure that you include this file into your current project (otherwise VC++ will not consider this file as part of the project even though it is physically present in the same directory).

For doing this go to PROJECT -> Add to Project -> Files and choose the header file.

Now compile mycar.cpp. You shouldn’t have any errors this time. The next step is to build the exe file mycar.exe. Go to ‘BUILD -> Build mycar.exe’ option.

Well, you should be getting a bunch of errors this time (which might appear weird). The following messages appeared in my computer:

Linking...

458

Creating Libraries

Page 460: C++ Basics to Advanced

mycar.obj : error LNK2001: unresolved external symbol "public: __thiscall car::~car(void)" (??1car@@QAE@XZ)

mycar.obj : error LNK2001: unresolved external symbol "public: void __thiscall car::display(void)" (?display@car@@QAEXXZ)

mycar.obj : error LNK2001: unresolved external symbol "public: void __thiscall car::input(void)" (?input@car@@QAEXXZ)

mycar.obj : error LNK2001: unresolved external symbol "public: __thiscall car::car(void)" (??0car@@QAE@XZ)

Debug/mycar.exe : fatal error LNK1120: 4 unresolved externals

Error executing link.exe.

Don’t panic on seeing the strange symbols. From the error messages we can deduce that this is a linker error (compiling went fine but linking failed). Why? If you look at the individual messages you’ll realize the problem. The linker hasn’t been able to resolve references to certain function definitions (car::~car( ), car::display( ) etc.). They are not present in carlib.h, not in mycar.cpp and certainly not in #include<iostream>.

So, should we add car.cpp to this file (but then we are not using the power of libraries). Car.cpp contains the definitions of these functions but carlib.lib contains the object code for all these functions. The linker only requires the object code for the functions and hence we should include the library carlib.lib in our current project. Then the linker will search carlib.lib and find the required object code for all the definitions. To do this, there are 2 settings you have to change in your project:

1. Tell VC++ the path where to search for the libraries. 2. Tell VC++ the name of the library.

For step 1 go to TOOLS-> OPTIONS. You’ll get a pop-up box with lots of tabs at the top (as shown below):

459

Creating Libraries

Page 461: C++ Basics to Advanced

We need to tell the path for the Library files. So change the SHOW DIRECTORIES FOR option as shown below:

Now add the pathname where you have the file carlib.lib (click the new icon present in the options dialog box to add a new path).

In my case it is in the current project directory itself. Click OK.

Next we need to tell VC++ the name of the library we want to use.

Go to PROJECT-> SETTINGS and you’ll see a dialog box as shown below:

460

Creating Libraries

Page 462: C++ Basics to Advanced

In the object/Library modules text field, enter our library name carlib.lib at the end of the list. This will automatically get added in the Project Options textbox at the bottom. Click OK and now build the exe file. You shouldn’t encounter any errors this time since the linker will be able to obtain the object code for all the function definitions. What have we achieved by using a library? The class user is now totally unaware about the implementation details of your function. Since the library is in object code, no user would try to manipulate the contents of the library file (you could open the library file in Notepad to view the contents which would appear cryptic). The library file would be something like this (the snapshot is only part of the file):

This isn’t something that a user would want to play around with!

What we created is called a static library. There are 2 types of libraries: • Static • Dynamic (or shared).

Static is simple to create but has a major disadvantage. If many programs make use of static libraries and if all of them are being run at the same time, then each program will have a copy of the library in memory. Shared libraries (or dynamic link libraries) overcome this problem.

461

Creating Libraries

Page 463: C++ Basics to Advanced

Advanced C++ - Part IIIb (Creating libraries)

The programs that we've seen so far are not wrong but they can't be called as 100% C++ programs either. The reason is because we've been making use of header files (such as iostream.h etc...). Making use of these .h files is more similar to C programming. Of course it isn't wrong to use them in C++ but there is a better method provided in C++. This method makes use of namespaces.

Instead of # include <iostream.h> we shall use the following two lines:

#include <iostream> using namespace std;

Firstly take note of the fact that there is no ‘.h’ in the include statement. This is the format of the new style headers used in C++. They do not use .h files. Old C++ compilers may not support namespaces. In case you are using such a compiler then you have to stick to the old method of #include with the ‘.h’ extension. But all the latest C++ compilers will support namespaces and they will also support the old style headers.

When C++ was created it used header files like those used in C programming. But then changes were made. Though it still supports the old header files, they are not recommended. You know that iostream.h is a file. C++ created a new style of its own. In this method we do not specify filenames. Instead it is a method that makes sure that appropriate definitions required by the C++ library have been declared. The new style headers are not filenames and hence the .h extension is left out.

Multiple File C++ Programming

So far we’ve been writing programs within one source file. The entire program (including class declarations, functions etc.) was written in one single *.cpp file. This file was compiled and run to check the output. In this section I would like to explain the process by which an executable file is created from our C++ source file.

Let’s assume that we’ve written a source file called test.cpp. What are the steps involved after that? Broadly you can say that four people are involved:

• Preprocessor • Compiler • Assembler • Linker

As mentioned earlier, the preprocessor will search for the preprocessor directives. It will convert these directives into C++ coding. In other words, the include header files will be

462

Creating Libraries

Page 464: C++ Basics to Advanced

inserted into your source file. If you have used macros, they will be substituted in the corresponding places.

Next, the compiler takes over. The compiler will read through the whole program, and in case it finds any problems while compiling, it will display compile time errors. Otherwise it will convert your source code into assembly language code.

The assembler will finally convert your assembly language code into machine language (i.e. into language that your computer can understand).

What is produced by the assembler is known as an object module (or a single translation unit because the source code has been translated into object code). Is the process over?

Even in a most basic C++ program we make use of other library functions and objects. Thus there are a few other object modules that are related to your C++ program. Only when these modules are linked with your object module, can the program work. The linker takes care of this aspect. All the different units are linked together to form the final executable program.

So, nowadays when we say a compiler, for example the Turbo C++ compiler, it usually contains all the required parts (a compiler, preprocessor, linker and assembler). Thus you needn’t worry about these individual parts of the compiler. But when you do create a multiple file C++ program, you should beware that just because all your individual files compile successfully doesn’t mean that they will be linked properly. You can get linker errors (possibly you forgot to include some header file or something like that). The question arises, why should we go in for multiple file programming?

When a program is very complex it is always advisable to break up the program into smaller units. In C programming, we would define the functions in a separate header file and then include this header file in our source file. In C++ we are bothered with classes. So, instead of functions, we will define classes separately. What we will do is to define the class in a header file. We will not define the member functions in this file (we will only declare all the member functions). The definitions will be done in a *.cpp file using the scope resolution operator. Now when you want to write a program using that class, you will simply include the header file in your source code. What about compiling? Header files cannot be compiled. But now you have two *.cpp files (one with the member function definitions and the other is your source file). We will compile both files separately and then when you build your project the linker will link up both the object modules together. Might seem a bit confusing but an example should make things easier to understand.

One more point to remember: Member functions are defined in a separate *.cpp file. This is usually referred to as the implementation. The idea is to keep the implementation of the class separate from the declarations.

463

Creating Libraries

Page 465: C++ Basics to Advanced

Creating a Multiple File Program

The example demonstrated below has been tried in Turbo C++ as well as in VC++. Other compilers will also have similar options available.

First of all we shall take a look at how to create a multiple file program in Visual C++ (later we’ll deal with Turbo C++). Click on File and then choose New. You’ll have a lot of options like (File, Project, Workspace etc…). Choose the Projects tab and in that you’ll have numerous choices available. Choose Win 32 Console Application from the list and give a suitable name. Let us assume that the name is given as carcheck. Choose the option which says "Create New Workspace" and then click on Finish/Ok. If you are using Visual C++ 6.0, another menu will pop up asking what type of application you want to create (whether you want an empty project or whether you want a "Hello World" application etc.). Choose "Empty Project". The new workspace will be created with the name as "carcheck". You will have two tabs with the labels "Class View" and "File View". This will give you an idea as to what are the classes and files present under your current project. Click on the FileView tab and you’ll see that there are no files there.

Now again go to "File" and choose "New". This time create a new C++ header file. Give a suitable name (here we have named it as ‘car’). Now you’ll have a new header file called car.h within the "FileView". This file will be empty and we shall now declare our class within this file.

//car.h #include <iostream> using namespace std;

class car { private: char color[20]; int speed;

public: car( ); void input( ); void display( ); ~car( ); };

As you can see, all the functions have only been declared in the file car.h (we have not defined them here).

Next, go to "File", "New" and create a new C++ source file. Name this file as car.cpp. Open the file in your editor window and type in the following:

464

Creating Libraries

Page 466: C++ Basics to Advanced

//car.cpp – we will define the various functions in this file.

#include "car.h"

car::car( ) { cout<<endl<<"New car created."; }

void car::input( ) { cout<<endl<<"Enter the color : "; cin>>color; cout<<endl<<"Enter the top speed : "; cin>>speed; }

void car::display( ) { cout<<endl<<"The color is : "<<color; cout<<endl<<"The top speed is : "<<speed; }

car::~car( ) { cout<<endl<<"Your car is destroyed."; }

Now since this is a *.cpp file, you can compile this file. Go to the "Build" option and choose "Compile car.cpp". After successful compilation, we shall now proceed to create our main source file (the file containing the program). Create another new C++ source file and name it as main.cpp. You might be wondering why the following was used:

#include "car.h"

in the car.cpp file while the following statement:

#include <iostream> using namespace std;

was used in the car.h file.

car.h is a header file created by us. So, it is present in the directory where we are doing our project. But <iostream> is present in the compiler’s include directory. Hence for car.h we include it using the double quotation and for iostream we use the angle brackets.

465

Creating Libraries

Page 467: C++ Basics to Advanced

//main.cpp #include "car.h" int main( ) { car mine; mine.input( ); mine.display( ); return 0; }

Now, compile this file (main.cpp). After compilation, you can go to "Build" and choose "Build carcheck.exe". It is now that the linker will work and link up everything. After this, go back to "Build" and choose "Execute carcheck.exe".

The output will be:

New car created. Enter the color : red Enter the top speed : 300 The color is : red The top speed is : 300 Your car is destroyed.

Remember: The header car.h already include <iostream>, so there is no need for you to include <iostream> again in the file main.cpp.

Beware: When you are linking many object modules together, only one of them should have the main ( ) function. In the above case we created two object files from car.cpp and main.cpp but only main.cpp had the main ( ) function.

By the way, in Visual C++ workspace files are stored with the extension *.dsw.

Preventing multiple inclusions of Header files in a program:

Usually when creating header files, the programmer will provide a provision so that the header file is not included in the source code more than once. To do this, we can make use of the preprocessor directives. The modified ‘car.h’ file will be:

#ifndef CAR_H #define CAR_H #include <iostream> using namespace std; class car { private:

466

Creating Libraries

Page 468: C++ Basics to Advanced

char color[20]; int speed; public: car( ); void input( ); void display( ); ~car( ); }; #endif

The header file be included within the source code only if CAR_H has not been defined. This prevents multiple inclusions of the ‘car.h’ in a program. CAR_H actually stands for ‘car.h’ (you cannot use the dot in preprocessor directives and thus have to use an underscore). You can use car_h or CAR_H (programmers usually prefer to use the latter).

If you are using Turbo C++

In Turbo C++, the above process is much easier. First of all create 3 files as done previously (car.h, car.cpp and main.cpp). Next you have an option that says "Project". Click on that and choose "Open Project". Since you still haven’t created a project, just type in some name for the project (ex: carcheck.prj) and click open. (In Turbo C++, project files have a *.prj extension). You’ll see a new project window at the bottom of the screen. Again click on "Projects" and choose "Add new item". A list of all the files will pop up on your screen. Locate the two files (car.cpp and main.cpp) and click on "Add". After adding both the files, choose "Done". Now both your *.cpp files have been added to the project. Next, you have to compile both these files. Do the compiling as you normally would. Open the file car.cpp and then click on "Compile". Then open main.cpp and compile this file also. Now if you look at the project window at the bottom of the screen you’ll notice that there are two items listed:

car.cpp main.cpp

Once you compile the two files, the project window will also display the number of lines in each file, whether they have any data etc.

Next choose "Build All" from the "Compile" option menu. Once successfully done, you can choose "Run" and your program will execute.

To summarize the concept of multiple file programming:

467

Creating Libraries

Page 469: C++ Basics to Advanced

All C++ classes can be divided into two main parts:

Class Declaration: This is also called as the interface of the class. It just contains information about the data members and the various functions that are available in the class. This is usually in the header file (in our case it was in car.h).

Class Implementation: This defines all the member functions that were declared in your header file (in our case it is car.cpp).

This will help you to hide the implementation from the user. Even though you may change your implementation, the class interface will remain the same.

The keyword ‘extern’

The ‘extern’ keyword was briefly mentioned in the chapter on data types. This is basically another storage class specifier (the others are static, auto and register) which you can use with your data types when you declare them.

When you develop a project consisting of many separate files there is a good possibility that you will be using global variables. The question arises, "Should we declare and define global variables in each of the files?" (because each *.cpp file will be compiled separately). This is not allowed in C++ and it is bad programming practice to declare and define the same global variable in more than one place. Hence in C++ we have ‘extern’. This will only declare a variable, it will not define it. Only when we define a variable will the compiler allot memory for the variable. Usually declaration and definition of a variable occur at the same time. But when we use the extern specifier (which stands for external), we tell the compiler that this is only a declaration (the definition of the variable is done in some other file). Thus usually all global variables are defined in one separate file and in all the other places we only declare the variable.

The syntax is: extern data-type variable-name;

The linker will take up the responsibility of resolving all the references made to the external variable.

Another use of Extern:

By default, C++ compilers will link all your functions as C++ functions. All C++ compilers make use of name mangling. When the compiler reads through the source code and encounters a function name, it stores the details about the function in a table (called a symbol table). For example if we have a function:

void sub (int, int);

468

Creating Libraries

Page 470: C++ Basics to Advanced

the compiler will store this function in its symbol table. But functions in C++ are name mangled, i.e. the function name stored by the compiler is not the same one as given by us. The compiler may store it as:

sub_int_int

Thus whenever the compiler encounters a function call to sub ( ) it will retrieve the appropriate definition for the function at compile-time. Name mangling permits C++ to implement function overloading. Let us say we have another function with the same name ‘sub’:

void sub (double,double);

The compiler will store this as

sub_double_double

Thus whenever there is a statement calling the function sub ( ), the compiler will check the data types of the arguments and call the correct function.

C compilers do not implement name mangling and thus they do not support function-overloading. Different C++ compilers (supplied by different vendors) may implement name mangling in a different way but the bottom-line is that all C++ compilers implement name mangling. Other languages mostly do not use name mangling (C compilers do not mangle names).

Let us suppose that you have written a few functions in C and have compiled them using a C compiler. Now in your C++ program you want to use those C functions. Is it possible? C++ will mangle all the function references and the mangled names will not be the same as the original C function name. Thus the linker won’t be able to match the function call (compiled in C++) to the function definition (compiled in C). To overcome this problem we need to tell the C++ compiler, "Do not mangle this function since it is not a C++ function".

The extern keyword can be used for this purpose.

The syntax is:

extern "name-of-the-language" function

Beware: You cannot use this within a function (it wouldn’t make any sense to do so). Always make such declarations global.

Example:

extern "C" int display ( ); //global declaration

469

Creating Libraries

Page 471: C++ Basics to Advanced

The C++ compiler now knows that the function display( ) is a C compiled function and so it won’t mangle the function name display( ). Sometimes if you have many functions that you want to link as a different programming function then you can do it as follows:

extern "name-of-language" { //function declarations/headers can also be included here }

Creating Libraries 470

Page 472: C++ Basics to Advanced

Advanced C++ - Part IV

The following topics are covered in this section: • Exception Handling • Templates • Template Classes

Exception handling Older C++ compilers will not support exception handling. This feature is used for dealing with certain specific situations (these situations are not supposed to occur and if you don’t provide some way of dealing with them then your program could simply freeze when the conditions really occur). To implement exception handling, C++ provides us with three keywords:

o Try o Catch o Throw

How do they work? First you try (or execute) the coding and in case some special situation is encountered, then the program will throw an exception. The exception that is thrown will be caught by the catch part of the program.

#include <iostream> using namespace std; int main( ) {

float a,b; try { cout<<"\nEnter a number : "; cin>>a; cout<<"\nEnter the denominator : "; cin>>b; if (b= =0) { throw 0; } cout<<"\nResult of division : "<<a/b; } catch(int i) { cout<<"\nError - You entered : "<<i; } return 0;

}

471

Advanced C++ - IV

Page 473: C++ Basics to Advanced

The output if you enter two non-zero numbers will be:

Enter a number : 5 Enter the denominator : 4 Result of division : 1.25

Suppose the second number you enter is zero, the following will result:

Enter a number : 5 Enter the denominator : 0 Error - You entered : 0

You should note that once the program throws something, it will not execute what follows it within the try block. It will go directly to the catch block and execute the underlying statements. The catch block that you write should immediately follow after the try block. Even if you add one display statement between the try and catch blocks it will lead to errors. Can I have more than one catch? Yes but each catch should catch a different exception (meaning that the argument types for each one should be different). You can write one catch block to catch integers, one to catch strings etc. Suppose you want to catch everything that is thrown, there is a simple way to do it. Just use:

catch(…) { //body of the catch }

The 3 consecutive dots imply that this ‘catch’ block will catch anything that is thrown.

Uncaught Exceptions

What will happen if we throw a string but we have programmed only for catching integers? This is what is called as an uncaught exception and if you try it out, your compiler will display the message:

Abnormal Program Termination

Actually, the compiler executes the terminate ( ) function. The terminate ( ) function will in turn call the abort ( ) function. Usually, if there is some problem with the exception handling, then the compiler would either go to the terminate ( ) function or it would invoke the unexpected ( ) function. The unexpected ( ) function will call the terminate ( ) function. Hence, ultimately the program will call the abort ( ) function. You can create your own version of the terminate function (in other words, you can set the terminate function) using:

set_terminate(your-function);

472

Advanced C++ - IV

Page 474: C++ Basics to Advanced

For using this, you would need to make use of the <exception> header file.

#include <exception> #include <iostream> using namespace std; void myterminate( ) { cout<<"\nMy Terminate Function"; abort( ); } int main( ) {

float a,b; try { set_terminate(myterminate); cout<<"\nEnter a number : "; cin>>a; cout<<"\nEnter the denominator : "; cin>>b; if (b= =0) { throw "zero"; } cout<<"\nResult of division : "<<a/b; } catch(int i) { cout<<"\nError - You entered : "<<i; } return 0;

}

The output would be(when you enter the second number as 0):

Enter a number : 5 Enter the denominator : 0 My Terminate Function Abnormal program termination

Thus you can see that the terminate ( ) function has been modified and even in our terminate ( ) function we make use of the abort( ) function. The idea is that you do not

473

Advanced C++ - IV

Page 475: C++ Basics to Advanced

want the program execution to continue once the compiler encounters some problem in exception handling.

The <exception> also defines some standard exceptions that are thrown. For example:

bad_alloc - thrown if some problem is encountered using ‘new’ bad_exception - thrown when exception doesn't match any catch

Just check out the following program:

#include <exception> #include <iostream> using namespace std; int main( ) { int *p; try { p = new int; } catch(bad_alloc) { cout<<"\nError in Allocation "; return 1; } return 0; }

Templates

This section is intended to provide a brief idea about templates rather than going too deep into the topic. Again this is a new feature incorporated in C++. First of all, you should know the reason behind the need for templates. Suppose you are writing a program in which you want to compare two numbers (finding out which one is greater or whether they are equal). Maybe in your program, once you want to compare integers and once you want to compare float quantities. As you know, you could write two separate functions to perform the task (of course you might in fact even feel that functions are not needed but let’s assume that you want to use functions). It seems odd that we’d have to write separate coding for integers and floating points (the process will be the same and only the data type is going to be different). Hence, instead of creating a specific function (for each data type), we can use a template function (or a generic function) to write a general function that would work with any data type.

474

Advanced C++ - IV

Page 476: C++ Basics to Advanced

The syntax for creating a template function would be as follows:

Template<class mytype> return-data function-name (parameters) { //body of function written using mytype as the data type }

or Template<class mytype> return-data function-name (parameters) { //body of function written using mytype as the data type }

To make it generic we have to write the entire function using mytype as the data type. What is mytype? Have a look at the example that follows. In this program we want to compare two numbers (a and b) and return 1 if a>b, return 0 if a=b and return –1 if a<b.

#include <iostream> using namespace std; template<class mytype> int compare(mytype x,mytype y) { if (x>y) { return 1; } else if (x<y) { return -1; } else if (x= =y) { return 0; } } int main( ) {

int a,b; double i,j; a=5; b=10; i=1.45; j=1.35;

475

Advanced C++ - IV

Page 477: C++ Basics to Advanced

cout<<"\nComparing integers a and b the result is : "<<compare(a,b); cout<<"\nComparing double i and j the result is : "<<compare(i,j); return 0;

}

The output is:

Comparing integers a and b the result is : -1 Comparing double i and j the result is : 1

As you can see, we can call the compare function using integers as arguments or even double as arguments. It doesn’t matter because the function is a generic function. When the compiler comes to:

compare(a,b);

it knows that a and b are integers. So it will create a specific instance of the compare function to handle integers. In other words, it will create an instance in which

mytype=int

Of course you will not see all this happening. The compiler does it automatically. Similarly for:

compare(i,j); mytype=double

By the way, ‘mytype’ is a generic type parameter.

Remember: You needn’t feel restricted to the use of only one ‘mytype’ within the template function. You can have mytype1, mytype2 etc… Again you can even use the standard parameters (like integer, double etc.) along with generic type parameters.

An Alternative (but crude) Method

#include <iostream> using namespace std;

#define max(a,b) (a>b?a:b)

int main( ) { int a,b; double i,j;

476

Advanced C++ - IV

Page 478: C++ Basics to Advanced

a=5; b=10; i=1.45; j=1.35; cout<<endl<<max(i,j); cout<<endl<<max(a,b); return 0; }

The output would be:

1.45 10

As you can see, the use of macros for functions seems to act as good as a template would act. But there are some problems with this:

The biggest problem would be when you compare using the unary operators:

cout<<endl<<max(a++,--b);

you would expect that the compiler should now compare between a++(6) and –b(which would be 9) and give a result of 9. But the output will be 8. Why? It’s because of the way the macros work. When the compiler sees max(a++,--b), it will substitute this in the function definition as:

(a++)>(--b) ? (a++): (--b)

The end result is that it decrements ‘b’ two times.

You won’t have this sort of problem when you make use of C++ inline function. And you also won’t have the problem when you make use of template functions.

Template Classes

In this case, we make the member data types of generic type. This is done whenever the functions performed by the class are the same (irrespective of the data type of the members). For instance, if you take the working of a stack the working is the same whether the data are integers or characters. Hence in such cases you can create a generic stack class that can operate on any type of data.

477

Advanced C++ - IV

Page 479: C++ Basics to Advanced

The syntax to create a class similar to creating a template function.

template <class mytype> class name-of-class { //body of class };

To create an object belonging to the class:

name-of-class <data-type> object-name;

An example should make it clear; here we are just going to pass two different types of data to a class and use a member function to display the two member data.

#include <iostream> using namespace std; template<class mytype1, class mytype2> class display {

private: mytype1 a; mytype2 b; public: display(mytype1 x, mytype2 y) { a=x; b=y; } void show( ) { cout<<endl<<"The values are : "<<a<<" , "<<b; }

}; int main( ) {

display<int,char> ob1(12,'d'); display<int,double> ob2(20,12.123); ob1.show( ); ob2.show( ); return 0;

}

478

Advanced C++ - IV

Page 480: C++ Basics to Advanced

The output is:

The values are : 12 , d The values are : 20 , 12.123

The above program shouldn’t pose much difficulty for understanding. You might be wondering as to where class templates might be really useful? Consider the problem we have with arrays in C++. When you create an array of a fixed size and in your program if you exceed the limit of the array, the compiler will not issue any error message. Instead it will continue and your results will be ambiguous. The problem is that there is no array boundary checking in C++ as there is in some other languages (like MATLAB – in MATLAB the compiler will never let you exceed the boundary limits and will produce an error message saying so). Thus, in C++ you can make your own generic array class and overload the [ ] operator such that it will check to see whether the index number specified lies within the size of the array. Else it should display an error message and terminate the program. Try it out.

479

Advanced C++ - IV

Page 481: C++ Basics to Advanced

Advanced C++ - Part V

The following topics are covered in this section:

• Casting • An intro to STL

Casting

Casting basically means forcing something into something different. For instance if you have an integer division, you can change (or rather force) it into a floating type division through casting. The general form of cast is:

(new type) expression;

Casting might also be referred to as typecasting (because we are casting between data types). The following example should make the concept clear:

int main( ) { int i=5; cout<<i/2; return 0; }

The output would be 2 because ‘i' is an integer and the compiler will perform integer division.

Using casting, the above program will be modified as below:

int main( ) { int i=5; cout<<(float)i/2; return 0; }

The output now will be 2.5

This method of casting was actually part of C programming. In C++ we have another four methods of casting. In this section we’ll discuss them briefly. These operators were introduced in C++ to restrict casting and enforce some rules in the process of casting.

480

Advanced C++ - V

Page 482: C++ Basics to Advanced

Otherwise using the C type of casting a programmer can indiscriminately cast and it could lead to unexpected results or errors.

The general syntax for using these casting operators is as below:

operator_cast <new-type> (expression/variable)

where operator_cast is replaced by one of the 4 casting operators.

dynamic_cast:

This is used to cast a base class pointer/reference to a derived class pointer/reference and vice-versa. It is dynamic because it will determine at runtime whether a base class pointer refers to an object of the base class or whether it points to an object of a derived class (this is known as downcasting-going from base to derived).

The opposite is known as upcasting. In upcasting, we can cast a pointer of a derived class to a pointer of base class (we go from derived to base).

This will work only on polymorphic classes (polymorphism means that at least one of your base class functions should be a virtual function). This operator will check at runtime to make sure that the casting was properly carried out. Suppose you are casting a pointer and if the casting is unsuccessful (i.e. it wasn’t cast to the requested type), then the value returned will be NULL. If you are using references and if the casting fails, then a bad_cast exception will be thrown.

This must be confusing you so let’s take up an example.

class car {

public: virtual void disp( ) { }

}; class ferrari : public car { }; int main( ) {

car *cp1, *cp2; ferrari *fp; cp1 = new ferrari; cp2 = new car; fp = dynamic_cast<ferrari*>(cp1); // successful if (fp) { cout<<"successful cast-base points to derived object";

481

Advanced C++ - V

Page 483: C++ Basics to Advanced

} else { cout<<"casting failed"; } fp = dynamic_cast<ferrari*>(cp2); // will fail fp value is NULL if (fp) { cout<<"successful casting"; } else { cout<<"casting failed-base points to base object"; } return 0;

}

The output is:

successful cast-base points to derived object casting failed-base points to base object

Take note of the following points (regarding cp1, cp2 and dynamic_cast):

1.) The casting will be successful if the base class pointer points to a derived object type. ‘cp1’ points to an object of type ‘ferrari’. Hence,

fp = dynamic_cast<ferrari*>(cp1);

will be successful.

2.) The casting will fail if the base class pointer points to a base class object. ‘cp2’ points to an object of type ‘car’ (which is the base class). Hence,

fp = dynamic_cast<ferrari*>(cp2);

will fail.

3.) You base class should be polymorphic (it should contain a virtual function). Otherwise you’ll get an error while using dynamic_cast.

482

Advanced C++ - V

Page 484: C++ Basics to Advanced

static_cast:

This is similar to the C type of casting discussed earlier. The syntax is:

static_cast<new-type> (expression/variable)

A simple program is written below:

int main( ) { int i=5; cout<<static_cast<float>(i)/2; return 0; }

Basically we are casting ‘i' into a floating type. The expression i/2 will be equal to dividing a floating value by 2.

Beware: What would happen if we write it as:

cout<<static_cast<float>(i/2);

Do you think there will be any change in output? Well, in fact the output will now be 2 and not 2.5. The reason is because the compiler would already have divided i/2, the result would be 2 (because of integer division) and the value of 2 will be forced into floating type (which makes no difference because you have already lost the decimal place). So be very careful while casting.

Static_cast performs no run-time check. This cast operator is not used only for standard data types; it can be used for casting pointers to classes.

483

Advanced C++ - V

Page 485: C++ Basics to Advanced

reinterpret_cast:

The reinterpret_cast can be used to convert between two totally different types. It is used in situations where none of the other operators can be used. It can cast pointers of one type into another type or it can also convert from pointer to integer and vice-versa. Since you can cast pointer from one type into another type, using reinterpret_cast we can cast from between two pointers pointing to totally unrelated classes.

class bacteria {

public: void disp( ) { cout<<"\nThis is bacteria"; }

}; class car { }; int main( ) {

bacteria *bp; car *cp; car mine; cp=&mine; reinterpret_cast<bacteria*>(cp)->disp( ); return 0;

}

The output is:

This is bacteria

Just go back to this line in the program:

reinterpret_cast<bacteria*>(cp)->disp( );

‘cp’ is actually a pointer to the class ‘car’. Here we have forced this pointer to point to the class bacteria and using this pointer we call the bacteria member function disp ( ). The output illustrates that the casting was successful.

484

Advanced C++ - V

Page 486: C++ Basics to Advanced

const_cast:

None of the other 3 casting operators that we’ve seen can get rid of the constant-ness of an object. For example: we discussed about constant objects and these objects can access only constant functions. Constant functions cannot modify member data (unless the data is mutable). Using the const_cast operator we can get rid of the constant-ness of the object; thus permitting it to modify member values. Constant objects are usually not supposed to change member data values but sometimes there are member data which are hidden from the user (but necessessary for all objects-irrespective of whether the object is a constant object or not). In such cases, there has to be a mechanism to modify the data (even by constant objects). One way is to use the ‘mutable’ method and the other method is to use the const_cast operator.

#include <iostream> using namespace std; class bacteria {

private: int life; public: bacteria( ) { life=0; } void set( ) const { cout<<"The original bacteria life is : "<<life; const_cast<bacteria*>(this)->life=1; cout<<endl<<"The new life value is : "<<life; }

}; int main( ) {

bacteria const ecoli; ecoli.set( ); return 0;

}

The output is:

The original bacteria life is : 0 The new life value is : 1

In the above case we have cast away the constant-ness of the ‘this’ pointer and hence it can modify the member data ‘life’.

485

Advanced C++ - V

Page 487: C++ Basics to Advanced

An introduction to STL (Standard Template Library)

This section will give you a brief introduction into STL. We learnt about templates earlier and the STL provides us with general-purpose template classes and functions to implement some common data structures and algorithms. The major types are stacks, queues, lists and vectors. As you know, templates can be used for any type of data and the STL is a generic library. It not only defines the general structure but also provides with some useful functions that can be used to operate on them.

The three main aspects of STL are:

• Container classes • Algorithms • Iterators

Container Classes:

These are classes that will contain/hold other objects. The various containers available are stacks, deque (double ended queue), lists, vectors, maps etc. I’ll just give you an idea as to what these containers generally mean.

Stacks: This is a way to store data such that whatever data you put in first can be taken out last. In other words, just imagine that you are piling up books one on top of the other. This forms a stack of books. The book that you kept at the bottom was the first book that you kept. The book that was kept at last will be right on top of the stack When you want to retrieve the books, the topmost book will be the first to be retrieved (hence what went in last will come out first). This is known as the last-in-first-out (LIFO) principle.

Queues: This is similar to our real life queues. The first person who enters will be the first person to leave the queue. The principle here is first-in-first-out (FIFO).

Now the container classes will contain functions to operate on them. In the case of the stack you will usually come across the terms ‘push’ and ‘pop’. If you add a new item to the stack, you are said to ‘push’ into the stack. When you retrieve a data from the stack you are said to ‘pop’ from the stack. Thus these operations are modeled into functions.

Algorithms:

Algorithm basically means how you are going to operate on a container class. For instance, there are algorithms for sorting values in a container (sorting in ascending order), there are algorithms to reverse the order of elements present in a container and so on. Algorithms will operate on the containers.

486

Standard Template Library

Page 488: C++ Basics to Advanced

Iterators:

Iterators are similar to pointers. They can be used to access particular elements of a container. The relation between iterator and container is similar to the relation between pointer and array. Either you can access a particular element of the container or you can go through all the elements. There are different types of basic iterators:

Forward iterator – this will move in a forward direction from one element to the next. It can be used for storing as well as retrieving values.

Random Access – This is used to access elements randomly.

Bidirectional – This is similar to the forward iterator except that this can move in both directions.

Let’s take a look at a simple program to illustrate vectors (and STL in general).

Vectors: Vector container is similar to the C++ array type. It can hold objects of the same data type. Vector container permits random accessing and it is dynamic (i.e. it can keep increasing in size as and when needed). The vector will allocate the required memory space whenever the vector grows in size.

#include <iostream> #include <vector> using namespace std;

int main( ) { vector<int> vec; //creating an object of vector vec.push_back(1); //push_back will put a value into the vector vec.push_back(2); vec.push_back(3); vec.push_back(4);

cout<<"Size of the vector : "<<vec.size( ); cout<<endl<<"The elements are: "<<vec[0]<<" "<<vec[1]<<" "<<vec[2]<<" "<<vec[3]; vec.erase(vec.begin( )+1,vec.end()-1); cout<<endl<<"Size of the vector after erasing: "<<vec.size( ); cout<<endl<<"The elements are : "<<vec[0]<<" "<<vec[1]; vec.clear( ); cout<<endl<<"Size of the vector after clearing: "<<vec.size( ); return 0; }

487

Standard Template Library

Page 489: C++ Basics to Advanced

The output is:

Size of the vector : 4 The elements are: 1 2 3 4 Size of the vector after erasing: 2 The elements are : 1 4 Size of the vector after clearing: 0

The various functions that have been used in the program will be explained (the various function syntaxes will not be dealt with since this program is to give you a feel of STL).

vec.size( );

This will return the size of the vector (how many elements are contained in the vector).

vec.clear( );

The clear ( ) function will delete all the value present in the vector object. Thus the size after clearing is 0. You would have noticed that we can access the vector elements by using index numbers (just like we do for arrays):

vec[0];

Another function that has been used is the erase function:

vec.erase(vec.begin( )+1,vec.end( )-1);

This function is used to selectively delete particular values from your vector object. Deleting doesn’t simply delete the values, but it also reorders the vector. In the above example the vector initially had 4 values:

1 2 3 4

Using the erase function we asked the compiler to delete the values from the second element to the second last element (which means that 2 and 3 have to be deleted). The compiler will delete 2 and 3 and then it will put the value 4 in the second position of the vector. Hence the vector now contains:

1 4

and the size of the vector is now 2.

Some of the functions like size ( ) and push_back ( ) are available to the other container classes as well. This section was intended to just give you an idea about STL.

488

Standard Template Library

Page 490: C++ Basics to Advanced

Question and Answers Time

This chapter is meant to answer some of the questions that were not discussed in the earlier.

Q.) What is a process?

Every instance of a program is called a process. Suppose in Windows we open “Notepad” ten times, then we’ll have ten Notepad windows on our screen. There is only one Notepad executable file in the system but we’ve invoked this executable ten times (we have ten instances). Each instance is called a process.

• In some operating systems, many processes can run at a time (like Windows). Actually it

appears to us as if many processes are running simultaneously. The operating system will

allocate a small time slot to each process and keep switching from one to the other rapidly

(it is so fast that we don’t notice this switching).

• In other operating systems like DOS, only one process is executed at a time (i.e. a process can be started only after the current one has completed execution).

Processes are also called ‘tasks’.

Q.) What is an OS?

OS (Operating System) is the software which acts as an interface between application programs

(programs we write) and the hardware. It the OS which will execute our programs; in other words,

our programs aren’t fed directly to the microprocessor. The OS takes care of this. There are many

reasons why an OS is needed as an intermediary.

• If 10 processes are running then all these processes will compete for memory. Someone needs to ensure that memory is allocated properly to each of these processes. • All the running processes will want to use the CPU for themselves. • Each program running would require input-output services and there needs to be someone to synchronize this efficiently. • When many processes are running, every process should get some timeslot where it can use the resources (otherwise one process could hijack the processor and prevent all other processes from running). • Some processes might need to be assigned higher priority. • One process shouldn’t be able to manipulate the data of another process.

These are some of the functions that an OS does (scheduling, memory management, setting

priorities, I/O management etc).

489

FAQ

Page 491: C++ Basics to Advanced

Q.) More info on linkers and loaders?

To execute a program, the processor needs the executable machine code to lie in main memory

(RAM). Whereas when we create an executable file we create this on our hard-disk (secondary

storage) which cannot be directly accessed by the CPU. Someone needs to copy or load the

program into main memory for execution and this job is performed by the loader.

When a code is compiled, or when a code is assembled, the equivalent object code produced

assumes that the program will be placed at memory location 0 (zero) of main memory (because the

linker can’t predict beforehand where the OS will place the program in memory). But the OS won’t

load programs at memory location 0. And even if it did load the program at location 0 we would still

have problems. If we have 10 programs, and we want to run all 10 programs at the same time,

obviously all 10 programs cannot occupy memory location 0! Which brings out another important

point: not only don’t we know at which location a program will begin but we also don’t have the

entire main memory available for use. If our PC were designed such that at a time only 1 program

can run, then it is fair to assume that the program which is running will have the entire main

memory to itself. But this isn’t the case; in fact the OS itself needs to occupy some part of main

memory. So no program would be able to occupy the main memory completely.

Absolute loader:

One method to solve this problem is to use some memory address other than 0 at the time of

linking. For example the linker could decide to use 1500 as the starting memory address (or some

provision could be provided such that the programmer can specify a starting address in the

program itself). The loader would now simply attempt to place the program at main memory

location 1500. If that memory location is currently occupied then it’ll have to try again later. This is

an absolute loader. It just takes the code and loads it into main memory.

Relocating Loader:

Let’s go back to the initial problem: all programs can end up anywhere in main memory. But since

they are created with the assumption of starting at location 0, there needs to be someone who will

change the starting value; or in other words someone needs to offset the entire program from

memory location 0 by some fixed amount. This is known as relocation.

Let’s take an example:

You may wonder why we need to worry so much about this but it helps to understand how things

work in the background.

In a high-level language, we would say:

490

FAQ

Page 492: C++ Basics to Advanced

int x = 1;

x = x + 5;

The compiler, when converting to object code would write something like this:

1. Allocate a memory address for the variable x (say location 1500).

2. Move 1 to location 1500.

3. Add 5 to the value stored at location 1500 and store the result in location 1500.

If you note, everything is written in terms of a memory address (in fact we’ve ended up with just

memory locations and constants). It is quite possible that the memory location for data (variables)

is allocated just after the code.

Memory used for storing the code

Memory allocated for variables

We could customize the diagram as:

int x = 1; x = x + 5;

Memory space for ‘x’

Let’s assume that each of the instructions occupies 5 bytes. Thus:

Position in memory (in terms of bytes)

Instruction

0 Move 1 to location allotted for x (location 10)

6 Add 5 to the value stored at location allotted for x and store the

result in the same location (location 10).

10 Memory allotted for variable ‘x’.

Since the compiler will create code assuming that the starting address is location 0, the first

instruction is placed at byte 0. Memory for ‘x’ is byte number 10 onwards.

Let’s assume that this is the executable file (which has been created by the linker). The OS

wouldn’t permit the program to start at memory location 0 (that location might be reserved for

491

FAQ

Page 493: C++ Basics to Advanced

something else). Thus now the program needs to start elsewhere. To start somewhere else, we’ll

need to offset all memory locations in the program. Let’s say that the memory location from 8000

onwards is vacant. The loader will now offset the entire program by 8000 and then load the

program into main memory. Thus our table will effectively become:

Position in memory (in terms of bytes)

Instruction

8000 Move 1 to location allotted for x (location 8010)

8006 Add 5 to the value stored at location allotted for x and store the

result in the same location (location 8010).

8010 Memory allotted for variable ‘x’.

This kind of a loader is called a relocating loader; it relocates the code in main memory (the

problem is that the loader has to check each instruction of the code and perform the required

offsetting).

Relocating using a hardware register:

To reduce the burden of a relocating loader, we can use the CPUs internal registers for relocating.

In our example the first program instruction should be:

“Move 1 to location allotted for x (register value + 10)”.

Similarly every instruction will depend on the value held in the register. So if the code has to be

relocated to 8000, then the value 8000 needs to be placed in this base register.

We’ve so far discussed how loaders work in simple operating systems (like the old version of MS-

DOS etc). Modern operating systems (like the latest version of Windows, Unix/Linux) make use of

virtual memory (which involves activities like paging and segmentation). Discussing this topic is

beyond the scope of this book (for reference you could use a book dealing with computer system

architecture or a book on operating systems). In these systems, the loader will need to perform

some more additional work in loading a program into main memory.

To sum up: the loader is part of the operating system and it is responsible for loading an

executable file into main memory. Our program is stored in the hard-disk (secondary storage)

which is not directly accessible by the processor. A program can be executed only when it is

loaded into main memory and the loader is responsible for this.

492

FAQ

Page 494: C++ Basics to Advanced

Q.) What is system software and application software?

A.) Software which acts as an interface to computer hardware is system software. Software used to

develop applications is also system software. Example: Operating system, loaders, compilers etc.

Software used for other purposes like text editors, music players etc. are called application

software. Example: web browsers, calculator, word processors etc.

Q.) Is the hard-disk directly accessible by the CPU?

A.) No. Secondary memory (which includes the hard disk; floppy disk; CD-ROM) cannot be

accessed directly by the processor. The processor can only access main memory directly (i.e. main

memory can be directly accessed because it is directly connected to the main memory via an

address bus) whereas the processor is not connected to the hard disk directly. This is the reason

why a program that has to be executed needs to be first loaded into main memory at the time of

execution.

The number of address lines in the processor determines the maximum amount of main memory

which can be accessed by the processor. A CPU with 8 address lines can access up to 28 memory

locations (or 256 bytes of main memory).

Q.) If secondary storage is not accessible then how does the processor access it?

The illustration below would help understand the relation between the CPU, main memory and secondary storage.

493

FAQ

Page 495: C++ Basics to Advanced

A bus is nothing but a group of wires; each wire can be used to transmit one bit and the system bus

consists of address and data bus. As the name suggests, addresses are sent on the address bus

and data is sent/received on the data bus.

Processor and main (primary) memory:

The processor and main memory are connected to the same bus. Let’s say the processor wants to

access memory location 1500.

• It would say (through the control bus): “Send me the data at this memory location”

• And it would pass the memory location 1500 on the address bus.

• On receiving the control signal the main memory will respond by placing the data at

1500 onto the data bus. Then the processor can read the data from the data bus.

All this is possible because the CPU and RAM are connected to the same system bus.

Processor and hard disk:

The problem is obvious; hard disk and CPU are not directly connected. An intermediary device

called an I/O controller is in charge of the hard disk and it is this mediator who can be accessed by

the CPU. There are many reasons why we use I/O controllers; if we didn’t have them then the CPU

would need to worry about directly interacting with these devices (and each I/O device behaves

and operates differently). Just like the operating system hides the low-level processor details from

us, an I/O controller specific internal details of the I/O device from the CPU (isn’t it easier to

program in C++ than in machine language!).

The diagram also explains why the CPU can access the primary memory faster than secondary

storage (primary memory is directly connected).

Q.) Is a program divided into different regions?

A.) • · Text - the program code is present here

• · Data - contains initialized global variables

• · BSS - contains uninitialized global variables

• · Heap - dynamically allocated memory is stored in the heap

• · Stack - local variables are allocated on the stack

494

FAQ

Page 496: C++ Basics to Advanced

Q.) If I were to make a declaration and definition as follows:

int i=4;

Now in which part of memory will the value of ‘i' be stored in? Will it be in the RAM?? And they say

that when we use the ‘new’ operator, memory is taken from the ‘heap’…Is the heap made use of

only when the ‘new’ operator is used or can a program make use of the heap in any other way?

How does heap differ from stack? Or are they the same? And what is free-store?

When you say memory, we always refer to the RAM. If you have done a course in microprocessor

programming you will learn about it. The stack is basically part of the RAM and whenever the

program has to branch elsewhere, it will store the return address in the stack. There are many

other uses for the stack as well. Now when a parent process asks the operating system to create a

child process, the OS allocates space for the child process's code, stack, and data (variables,

whose values are given in the program itself). These regions are just memory blocks. The orders in

which these blocks exist and the amount of memory purely depends on the OS implementation.

Hence a programmer should not be bothered about it. The 'stack' is used to allocate temporary

variables. All local variables are temporary variables. All global variables are allocated in the 'data'

region. So where did this term 'heap' come from? Heap is a region of memory which is used by

'malloc' (this is an operator similar to ‘new’ and was used in C) and 'new' to allocate memory. When

the OS allocates memory, it usually doesn’t do so in a count of bytes. Usually the OS allocates

memory to a process in terms of 4KB. Hence when a process needs 100 bytes, the OS gives it

4KB. The 'malloc' and 'new', consider this as the 'heap'. They allocate memory from the 'heap'.

When the heap gets over, again the OS is requested for more memory. All this happens

transparently; so it is hard to see it before your eyes. Actually this term 'heap' is kind of getting

wiped away. It got introduced in MSDOS where the 'heap' referred to all the memory from the last

block allocated to a process to the end of memory. This was so because, MSDOS was not a

multitasking OS and it didn’t have any protection from user processes. So that was some

background information. Coming back to the question,

int i = 4;

If the above declaration was done locally (i.e. within a function), then the allocation is done on the

stack. If it is done globally it is in the 'data' section. There is another section called 'bss' which is the

un-initialized data, which is similar to 'data'.

There is one more point to note. Sometimes we have a region of memory called as ‘free store’.

Sometimes this would be the same as the ‘heap’ but sometimes it could be different. If they are

different then there are certain things you have to note. When you use the C++ dynamic memory

operators (new and delete), these will operate on the free store. When you use the C operators

(malloc and free) they will operate on the ‘heap’. What you have to take care of is not to mix up

‘new’ with ‘free’ and ‘malloc’ with ‘delete’. Suppose the ‘heap’ and ‘free store’ are different, then you

495

FAQ

Page 497: C++ Basics to Advanced

will be allocating space in one region and freeing up memory elsewhere. So always use ‘new’

along with ‘delete’ and ‘malloc’ along with ‘free’.

One more important thing; C Standards doesn’t document what I've explained to above. Though

this is the convention that is generally followed by most of the C/C++ compilers, a programmer

should never wonder where in memory his variable is living. Rather he should think, “what is the

scope of a variable?”. That is better programming practice. Usage of global variables should be

only after thorough review of the problem.

Q.) Executable formats, what are they and are there many formats?

Just as we have different operating systems, we have different executable formats. What does a

format mean? An executable file is not directed at the microprocessor but rather at the operating

system. Remember that the operating system is the one which acts as an interface between our

program and the hardware. So the OS expects its input to be in a specific format.

Each OS expects an executable file to be in a specific format. In DOS it’s the exe format, in

Windows the PE (portable executable) format, in Linux/Unix the ELF (executable and loadable file

format) or the a.out format (which was the initial format in Unix). Why bother about this? Actually

we don’t need to bother about them but it is one reason why a DOS executable file won’t run on

Linux. Linux expects the executable format to be in a structure different from that of DOS.

Q.) Who decides on the size of the stack?

This is again operating system dependent. Every operating system has a specific executable file

format. Like in MSDOS you have EXE file format. The old executable format of Linux was 'a.out'.

Now the native format is ELF for most of the Unixes. So at the time of compilation, the compiler

computes the amount of stack you'd need and it puts in the respective fields of these executable

files. Mind you, these executable files are not fully executable. They have a header part, which

says where the 'code' section is, where the 'data' section is, how much stack is needed, how much

memory to allocate, what are the regions that need not be loaded into memory and so on. As a

rule, a C/C++ programmer when writing for a Standard implementation should not be bothered

about setting the stack and such. These were an issue only in MSDOS. But still some compilers

provide you non-standard features to control the size of the stack. Like the variable 'extern int

_staklen' in Turbo C/C++.

Supposing you set _staklen = 0x1000;

then the compiler after compilation, when generating the EXE file, will write in the EXE header that

you need a stack of '_staklen' bytes. When you execute the executable, the OS will read this

496

FAQ

Page 498: C++ Basics to Advanced

header first to load your program. It'll see how much space you need and will allocate it. Then it'll

load the respective sections of your program into the respective regions of memory. So, usually it is

better not to bother about changing stack settings.

Q.) Can I call a function from within a function?

The main ( ) function does call other functions. Similarly you can call other functions also from

within a function. See the example below:

#include <iostream>

using namespace std;

void sum(int a, int b)

{

cout<<endl<<"The sum is : "<<a+b;

}

void input( )

{

int x,y;

cout<<endl<<"Enter the two numbers : ";

cin>>x>>y;

sum(x,y);

}

int main( )

{

input( );

return 0;

}

The code will compile and run properly. You will get errors if you try to define a function within

another function. The compiler will say that local function definitions are illegal. For instance in the

above program you cannot define the function input ( ) within the main ( ) function.

Q.) Assume that I am declaring a constant using #define (say something like PI). Does the #define act like a function? Will the #define take up memory space itself?

No, the #define's will not occupy any space in memory. They are preprocessor directives. i.e they

are processed before compilation. When you compile a program, there are different stages; the first

497

FAQ

Page 499: C++ Basics to Advanced

one is pre-processing where directives such as these can be expanded. So these are decided at

compile time and not at run time.

Check out the following code:

#define X 100

int main( )

{

const int mark = 20;

int y,z;

y = X;

z = X + mark;

return 0;

}

In the above code, at the time of compilation all occurrences of 'X' will simply be replaced by '100'.

But on the other hand when I define 'const int mark', it means that 'mark' is a variable whose value

won’t change. So the compiler allocates some memory for the 'const int'. After preprocessing the

code would look as below:

int main( )

{

const int mark = 20;

int y,z;

y = 100;

z = 100 + mark;

return 0;

}

Q.) What is a library? Is it a collection of header files?

A library file contains functions, which can be called from your code. The header files usually

contain only the function declarations (function header) while their implementation is present in the

library files. Just have a look at stdio.h or conio.h.; they contain tons of functions but their internal

coding (function definition) will not be present. The actual functions exist in the library, which is

precompiled but not linked. All functions like printf, scanf, gets, puts, etc. are present in the library.

We call them in our programs but we can’t include the library just like that. We have to link our file

with the library. If you are using Turbo C++, the compiler automatically does this for you (it might be

a file called CS.LIB). But you can also do this manually. In some compilers we will have an option

in Project Settings to mention the library files that want to link.

498

FAQ

Page 500: C++ Basics to Advanced

Q.) Where should the ‘const’ keyword be placed?

const int i=5;

int const j=4;

Which one is correct?

Both are acceptable. You can place the ‘const’ keyword before the data type or after the data type.

But be careful about placing the ‘const’ keyword when you are using pointers.

int x,y;

int *const p1=&x; //constant pointer

int const *p2; //not a constant pointer

x=y=5;

p2=&y;

*p2=6; //Not allowed - we cannot modify the value at p2.

p2=&x; //Allowed because p2 is not made a constant pointer

The placement of the ‘const’ keyword will determine whether the pointer is a constant pointer or

not.

Q.) I get the following errors when attempting to compile my program (‘Time’ is a class that has private members called ‘hours’ and ‘minutes’):

'hours' cannot access private member declared in Class 'Time'

'minutes' cannot access private member declared in Class 'Time' and, 'operator << is ambiguous'

These errors point to the line "ostream & operator<<(ostream & os, const Time & t)"

in my source code. The << operator has been overloaded using friend functions. Why can’t a friend

function access the private members of the class? I am currently using Microsoft Visual C++.

A.) If you use some other compiler (other than VC++) you won’t have this problem. Actually this is

a bug in Microsoft VC++ version 6.0. They have released a service pack to rectify this problem.

499

FAQ

Page 501: C++ Basics to Advanced

The problem (the bug) is because of ‘using namespace std;’ If you avoid this statement and use

iostream.h you won’t have the problem. Another option is to define the overloaded operator <<

within your class itself (instead of defining it outside the class using the scope resolution operator).

Check out the following website for details about the problem as well as an explanation about the

bug and the service pack. http://support.microsoft.com/default.aspx?scid=kb;EN-US;q192539

Q.) What is name-mangling?

All C++ compilers make use of name mangling. When the compiler reads through the source code

and encounters a function name, it stores the details about the function in a table (called a symbol

table). For example if we have a function:

void sub (int, int);

the compiler will store this function in its symbol table. But functions in C++ are name mangled, i.e.

the function name stored by the compiler is not the same one as given by us. The compiler may

store it as:

sub_int_int

Thus whenever the compiler encounters a function call to sub ( ) it will retrieve the appropriate

definition for the function at compile-time. Name mangling permits C++ to implement function

overloading. Let us say we have another function with the same name ‘sub’:

void sub (double,double);

The compiler will store this as

sub_double_double

Thus whenever there is a statement calling the function sub ( ), the compiler will check the data

types of the arguments and call the correct function.

C compilers do not implement name mangling and thus they do not support function-overloading.

Different C++ compilers (supplied by different vendors) implement name mangling in a different

way but the bottom-line is that all C++ compilers implement name mangling.

500

FAQ

Page 502: C++ Basics to Advanced

Linked List 1

Introduction:

Data structure refers to the way we arrange data or the way in which data is stored in memory. There are different types of data structures. Array is a simple data structure that we have already seen.

We come across data structures in everyday life as well. When we go shopping, it is common practice to prepare a shopping list. Might be something like below:

Noodles - 2 packets

Tomatoes - 1 kg

Cornflakes - 1

Chocolate

Bread

A data structure is simply a way of organizing data and we have organized data in our shopping list as well. From what we’ve learnt so far we would categorize this as an array perhaps. The information isn’t sorted and we simply keep increasing the list when we think of more things to buy. But the problem with arrays is that they are fixed. You can’t keep on increasing their size as and when you feel like it. So we have a better data structure called a vector to serve this purpose.

Similarly, we witness queues everyday (either while shopping, booking tickets, waiting in the canteen etc.). A queue is based on the principle of first come first serve. A queue is another data structure.

When writing applications depending on the requirement we would need to use an appropriate data structure to organize information. In this chapter we’ll take a look at some common data structures.

Introduction:

What is a linked list?

If you have read books on memory then you would have come across a concept similar to linked lists. All these books usually explain one method for remembering the names of random objects in order. For example, your friend might read out the following list to you just once:

Sheep, carpet, bottle, cigar, tent

501

Data Structures

Page 503: C++ Basics to Advanced

and throw a challenge. The challenge is that after a few minutes you should be able to recollect the five names in the same order. Not a big deal, right? Try it with 20 and you’ll realize the difficulty. Memory experts suggest that we associate each object with the next one in the list; or in other words create a link between every neighbouring object. In our case we might say, “A sheep was born on a carpet, the carpet was rolled into a bottle, the bottle smoked a cigar, the cigar was resting in a tent!” The idea is to form comical associations between two objects since these associations are retained in our memory longer. The beauty of this technique is that if your friend names any object from the list, you will be able to say what is the next object in the list (irrespective of the object’s position in the list).

The linked list data structure is very similar to this (except for the comical association!); and instead of object names, we call the elements of a linked list as nodes. A linked list consists of a series of nodes with each node pointing to the next node in sequence. A node has 2 parts:

• Data • Address of the next node

Some points to note about linked lists:

• In terms of coding, linked lists are more complex than arrays. • It is easy to increase or decrease the size of a linked list (i.e. you can easily

manipulate the size of a linked list). In the case of an array this is a problem. • New nodes in a linked list are created as and when required. • Insertion of new elements (or nodes) is easier. In an array it is quite difficult to insert

an element in the middle of the array. • Deletion of a node is easy since only the address part of the previous node needs to be

changed. • Every node contains data and also has an address that points to the next node (this part

is basically a pointer). Diagrammatic representation of a linked list:

To distinguish the last node of the linked list from the other nodes, the last node will point to NULL (address 0). The very first node is called the 'head'. It doesn't have a data part and only points to the next node (basically the 'head' indicates the starting position of the linked list).

502

Data Structures

Page 504: C++ Basics to Advanced

Creating a linked list:

To aid in the understanding of the linked list concept, let's write a little program which will simulate a linked list. The program should do the following:

• Store a set of integers. • It should be possible to delete a particular element. • Permit insertion of a new node (element). • Deletion of a particular node. • A way to display the entire contents of the list.

Let's do this program the OOPs way. First of all we need to have an idea about how we are going to represent a single node of the linked list. The simplest way is to use a structure for a node.

struct node { int data; node *next; };

'data' is used to hold the data. In this case we are creating a linked list to store only integers (so 'data' is of type int). Every node contains a pointer to the next node. For this we create a pointer pointing to an object of type 'node'. Thus the pointer 'next' will contain the address of the next node.

So, we now have a way to represent a node in our linked list. Let's now model a class to simulate the Linked list itself.

//A class to simulate a Linked List

class list { private: struct node { int data; node *next; }; node *head; //to represent the 'head' of the linked list public: list ( ) { head=NULL; //initially the linked list is empty; initialize head to NULL } void addnode (int); void display( ); };

'head' is used to store the starting location of the linked list (we need the starting point of the linked list). When you create an object of type 'list', your new linked list will be empty (which means that 'head' should point to a NULL location since there are no nodes in the linked list).

503

Data Structures

Page 505: C++ Basics to Advanced

This is why we make use of the constructor to initialize the 'head' value to NULL. The last node is also called the 'tail'.

Adding a new node to the linked list:

First let's see how to add a new node to our linked list. The following steps are involved in adding a new node:

• If the 'head' has a value of NULL it means that the node we are going to add is the first node.

• If 'head' is not NULL, then identify the last node of the linked list and add the node at the end.

void list::addnode ( int num ) { node *newnode, *pointer;

newnode = new node; //creating a new node newnode->data = num; //assigning the data to the new node newnode->next = NULL; //the new node will be the last node, so point to NULL if (head = = NULL) { head = newnode; //'head' now contains the pointer to point to the newly added node } else { pointer = head; //initialize pointer to the 'head' while (pointer->next != NULL) //move from one node to the next till we reach the tail { pointer = pointer->next; } pointer->next=newnode; //make the last node point to the new node } }

The logic might initially seem confusing. The addnode( ) function is called with an integer argument (this integer is the data that we want to store in the new node). We create a new node saying:

node *newnode; newnode = new node;

'newnode' is a pointer which points to the structure 'node'. Thus you can access 'data' and 'next' using this pointer (by using the arrow operator). Next we set the elements of the newly created node.

newnode->data = num; //assigning the data to the new node newnode->next = NULL; //the new node will be the last node, so point to NULL

504

Data Structures

Page 506: C++ Basics to Advanced

Supposing the linked list is empty, then the 'head' value will be NULL. Thus there is no need to scan the list and locate the last node. So,

if (head = = NULL) { head = newnode; //'head' now contains the pointer to point to the newly added node }

'head' will now point to the first node of the linked list. In case we want to add more nodes then we need to find out the last node of the list (the last node will be the one which points to NULL). Once we identify this node, all we have to do is assign the address of the newly created node to the 'next' value of the last node.

else { pointer = head; //initialize pointer to the 'head' while (pointer->next != NULL) //move from one node to the next till we reach the tail { pointer = pointer->next; } pointer->next=newnode; //make the last node point to the new node }

Displaying the contents of the linked list:

This is relatively simple. All we need to do is traverse through the entire list (from the header to the tail) using a loop. Within the loop we can display the data stored in that node.

void list::display( ) { node *pointer; pointer = head; //start from the 'head' while (pointer != NULL) { cout<<endl<<pointer->data; pointer = pointer->next; } }

The above code should be quite self-explanatory.

505

Data Structures

Page 507: C++ Basics to Advanced

Whenever you use 'new' use a 'delete':

After using your linked list you will want to destroy all the nodes (i.e. free all the memory allocated using the 'new' operator). For this purpose you could write the code for destroying the list in the destructor as shown below:

~list( ) { node *current,*temp; current = head; temp = current; while(current != NULL) { cout<<endl<<"destroying node"; temp = current->next; delete current; current = temp; } }

The following section provides the complete program for a linked list.

Linked List (Part 2)

The complete program will be as below (along with the output):

//A class to simulate a Linked List

#include <iostream> using namespace std;

class list { private: struct node { int data; node *next; }; node *head; //to represent the 'head' of the linked list public: list ( ) { head=NULL; //initially the linked list is empty; initialize head to NULL }

~list( ) //Destructor { node *current,*temp; current = head; temp = current;

506

Data Structures

Page 508: C++ Basics to Advanced

while(current != NULL) { cout<<endl<<"destroying node"; temp = current->next; delete current; current = temp; } } void addnode (int); void display( ); };

//Function to add a new node to the end of the linked list

void list::addnode ( int num ) { node *newnode, *pointer;

newnode = new node; //creating a new node newnode->data = num; //assigning the data to the new node newnode->next = NULL; //the new node will be the last node, so point to NULL if (head = = NULL) { head = newnode; //'head' now contains the pointer to point to the newly added node } else { pointer = head; //initialize pointer to the 'head' while (pointer->next != NULL) //move from one node to the next till we reach the tail { pointer = pointer->next; } pointer->next=newnode; //make the last node point to the new node } }

//Function to display the contents of the linked list

void list::display( ) { node *pointer; pointer = head; //start from the 'head' while (pointer != NULL) { cout<<endl<<pointer->data; pointer = pointer->next; } }

507

Data Structures

Page 509: C++ Basics to Advanced

int main( ) { list x; x.addnode(2); x.addnode(1); x.addnode(12); x.addnode(10); x.display( ); return 0; }

The output will be:

2 1 12 10 destroying node destroying node destroying node destroying node

Assignments:

There are some more things you can do to your linked list class. You can write an insert statement to insert a new node in a particular location. You can write a function to delete a node (while deleting you have to physically remove the node using the 'delete' operator and you have to change the address stored in the previous node). Create a template for a linked list (so that the linked list can be used to hold any type of data).

Singly linked lists: What we have discussed so far are called singly linked lists (because they contain the address of only the next node – i.e. they can be used to traverse the list in one direction alone).

Doubly linked lists: These linked lists contain nodes having 2 pointers (one points to the next node and one points to the previous node). Thus you can traverse the linked list either forward or backwards.

Circular linked list: In this type of linked list the last node (i.e. the ‘tail’) will point to the first node (instead of pointing to the NULL address).

Continue learning linked lists in the next topic: Linked Lists using STL

508

Data Structures

Page 510: C++ Basics to Advanced

Linked List using STL

The standard template library provides a container for lists (it is a doubly linked lists). There are many functions available and the following program shows a few of them:

//A program to illustrate the STL container for linked lists

#include <iostream> #include <list> using namespace std;

int main( ) { list<int> intlist; list<int>::iterator ptr; int i;

//checking whether the list is empty if (intlist.empty( )= =true) { cout<<endl<<"The list is empty"; }

//adding 5 values to the list (0 1 2 3 4) for( i = 0; i<5; i++) { intlist.push_back( i ); }

//adding another element (4) to the list intlist.push_back(4);

//initializing the iterator to the first node //displaying the value held in the first node

ptr=intlist.begin( ); cout<<endl<<"First element has value: "<<*ptr;

//traversing through the list by incrementing the iterator cout<<endl<<"Initial list contents are : "; for (i = 0; ptr != intlist.end( ); i++) { cout<<" "<<*ptr; //displaying the value at that node ptr++; }

//removing duplicate elements from the list intlist.unique( );

509

Data Structures

Page 511: C++ Basics to Advanced

cout<<endl<<"Modified list contents are: "; ptr = intlist.begin( ); for (i=0; ptr!=intlist.end( ); i++) { cout<<" "<<*ptr; ptr++; } return 0; }

The output will be:

The list is empty

First element has value: 0

Initial list contents are : 0 1 2 3 4 4

Modified list contents are: 0 1 2 3 4

Let’s just take a look at some of the functions used in the above program:

list<int> intlist;

list<int>::iterator ptr;

Here we are creating a linked list named ‘intlist’ and we are also creating an iterator named ‘ptr’. An iterator is similar to a pointer and it is used to store addresses.

intlist.empty( )

This functions returns a bool value (true or false) depending on whether the list is empty or not. intlist.push_back(i);

push_back(data) is used to insert an element to the end of the list. There is also a pop_back ( ) function to remove the last element of the list. Or you can use the push_front (data) to insert data at the start of the list.

ptr = intlist.begin( )

This function returns a reference to the first node of the linked list. In our program we have stored this value in the iterator ‘ptr’. The function intlist.end( ) is the converse of begin( ).

intlist.unique( );

The unique( ) function removes any duplicate values occurring later on in the list.

There are lots of in-built functions that you can try out on linked lists. This program was meant to give you an idea of how to use these functions.

Learn in the next section about Stacks.

510

Data Structures

Page 512: C++ Basics to Advanced

Stacks and STL

Introduction:

If you have understood the working concept of a linked list, understanding a stack shouldn’t be difficult. Stack is another kind of data structure in which we store data one on top of the other.

An analogy in real life would be a stack of plates (i.e. a set of plates kept one on top of the other). The main feature in such an arrangement is that the last plate to be kept will be at the top of the stack. So, when you want to remove a plate from the arrangement, the last one will be removed first.

This is termed as ‘last-in-first-out’. What goes last comes out first. Similarly you cannot add a plate in between the stack; you can only keep placing plates at the top. The advantage of this kind of arrangement is that you can ensure that everything flows in a proper order (you cannot randomly remove/insert elements in the middle of a stack). The two operations (of adding an element to a stack and removing an element) are called ‘pop’ and ‘push’.

• Popping is the process of removing the topmost element from the stack. • Pushing is the process of adding an element to the top of the stack.

Thus it follows that popping will reduce the size occupied by the stack while pushing will increase the size of the stack.

There is a particular area of memory also which is called the ‘stack’. Whenever you create local variables in your functions, these variables get allocated memory in the stack. And this stack memory also follows the same principle of the stack. When variables are created they are pushed into the stack and when the function terminates, all the corresponding variables are popped out of the stack. This concept is used in recursion.

Implementation:

A stack can be implemented using arrays (dynamically allocating the size of the array) or you can make use of linked lists. In fact if the linked list is slightly modified you will get a stack. You will have two functions to pop and push elements on to the stack. Just like we created our own linked list, you can create a stack implementation as well. This is left as an assignment for you to try out.

The STL has a stack template class available in it. The basic two functions needed are pop ( ) and push ( ). There is another function empty ( ) to check whether the stack is empty. Below is a simple program to illustrate the STL stack.

511

Data Structures

Page 513: C++ Basics to Advanced

//Program to illustrate the stack container of STL

#include <iostream> #include <stack> using namespace std;

int main( ) { stack<int> intstack; int i;

//Adding 5 values to the stack (100 101 102 103 104) for( i=100; i<105; i++ ) { intstack.push(i); }

//Displaying the size of the stack cout<<endl<<"Number of elements in the stack: "<<intstack.size( );

//Popping the elements of the stack till it becomes empty. //Before popping we display the value of the element which is // going to be popped

cout<<endl<<endl<<"Popping the elements one by one"<<endl;

for (i=0; !intstack.empty( ); i++) { cout<<endl<<intstack.top( ); intstack.pop( ); cout<<endl<<"Size of stack after popping: "<<intstack.size( ); }

return 0; }

The output will be:

Number of elements in the stack: 5

Popping the elements one by one

104 Size of stack after popping: 4 103 Size of stack after popping: 3 102 Size of stack after popping: 2 101 Size of stack after popping: 1 100 Size of stack after popping: 0

512

Data Structures

Page 514: C++ Basics to Advanced

Notice in the output that 104 is the first element to be popped (it was the last element to be pushed into the stack). The member function

size( )

is used to determine the size of the stack (i.e. the number of elements present in the stack).

The function top( )

can be used to retrieve the value of the last element pushed into the stack (i.e. you can find out the value of the element lying at the top of the stack).

Beware: If you attempt to pop the stack after the stack has become empty, you will get runtime errors. Use the empty ( ) function to check whether the stack is empty or not.

Queues

A queue is similar to a stack but it is based on the saying ‘First come first serve’. In a stack the principle is ‘last come first serve’. The concept is termed as FIFO (first-in first-out). Queues are just like queues in everyday life. The person who comes late has to stand at the end of the queue.

Similar to a stack you cannot enter in between an existing queue (i.e. you cannot insert elements into a queue); you can only keep adding to the end (back) of the queue. The two operations (of adding an element to a queue and removing an element) are called ‘pop’ and ‘push’. The only difference between the pop and push of stacks is that the elements popped and pushed are different from that of the stack.

• Popping is the process of removing the first element of the queue (i.e. the element which entered the queue first).

• Pushing is the process of adding an element to the back of the queue.

Diagrammatically a stack can be represented as shown below:

Stack

BOTTOM

TOP

Order in which elements are serviced/removed

Order in which elements enter

0

1

2

3

4

513

Data Structures

Page 515: C++ Basics to Advanced

The STL has a queue template class available. The functions available are similar to those available for a stack. The basic functions needed are pop( ) and push( ). There is another function empty( ) to check whether the stack is empty. Below is a simple program to illustrate the STL queue:

//A program to illustrate the STL container for queues

#include <iostream> #include <queue> using namespace std;

int main( ) { queue<int> intqueue; int i;

//adding 5 values to the queue (0 1 2 3 4)

for(i = 0; i < 5 ; i++) { intqueue.push(i); }

cout<<endl<<"The first element is : "<<intqueue.front( ); cout<<endl<<"The last element is : "<<intqueue.back( ); cout<<endl<<"The size of the queue is: "<<intqueue.size( );

//Removing the elements of the queue one by one cout<<endl<<"Queue contents are : ";

for (i = 0; !intqueue.empty( ); i++) { cout<<endl<<"Element removed: "<<intqueue.front( ); intqueue.pop( );

BACK

FRONT

Order in which elements enter

Order in which elements are serviced/removed

Queue

4

3

2

1

0

514

Data Structures

Page 516: C++ Basics to Advanced

cout<<endl<<"Size of queue after popping: "<<intqueue.size( ); } return 0; }

The output of the program will be:

The first element is : 0 The last element is : 4 The size of the queue is: 5 Queue contents are : Element removed: 0 Size of queue after popping: 4 Element removed: 1 Size of queue after popping: 3 Element removed: 2 Size of queue after popping: 2 Element removed: 3 Size of queue after popping: 1 Element removed: 4 Size of queue after popping: 0

Beware: Iterators cannot be used in STL stacks and queues.

We’ll discuss more about STL and data structures later on.

A closer look into STL

We’ve already had an introduction about STL. Having learnt about data structures you will be in a better position to appreciate the use of STL. Data structures are used frequently by programmers for various applications. Every programmer could keep creating his own model of the data structure and use them in his application. But the idea of OOP is reuse. Since data structures are used frequently it was felt that creating a common standard would greatly benefit programmers. This led to the development of STL. The programmer can reuse the existing code and need not waste time in developing the data structures or algorithms (like sorting and searching).

How STL was developed?

An example might help you appreciate the concept behind development of STL. Let us suppose that we want to write 2 algorithms: one for sorting and one for searching. Let us say that we want to operate on two data types: integers and float. Sorting and searching is not applied to a single element. We will be needing a sequence of elements and let us restrict ourselves to arrays and linked lists.

515

Data Structures

Page 517: C++ Basics to Advanced

Thus we are attempting to write programs to:

• Sort • Search

They have to work on 2 containers:

• Arrays • Linked List

The data types we will use are:

• Integer • Float

So, how many codes do you think we have to write? We’d have to write 2*2*2 (2 algorithms * 2 data structures * 2 data types = 8) sets of codes. The combinations would be:

1. Searching an array of integers 2. Searching an array of float 3. Searching a linked list of integers 4. Searching a linked list of float

Similarly you’ll need to have 4 more codings for sorting. The problem with the above methodology is that our basic algorithm is going to be the same things. It’s only the data types and data structures that will vary. To reduce on this template functions were created. With template functions we could use the same function across any data type. Thus the data type factor was eliminated from the equation and the number of codings needed was reduced to 4 (2 algorithms * 2 data structures). The next logical step was to write code that could be used across different containers. This would mean that ultimately you only need to write an algorithm for each operation and needn’t worry about what data structure was used. This is the basis for STL.

STL was developed by Stepanovs and Ming Lee and it has been drafted into the standard ANSI C++ standards. Hence all C++ compilers (the latest ones) will support STL.

We have already dealt with the terms used in STL: containers, iterators and algorithms. We use pointers to access objects in C++. When using STL we use iterators instead of pointers. Containers are basically the data structures, a few of which we have already discussed. They are designed as template classes and can be used to hold any data type. Containers can be divided into different categories:

1. Sequence containers 2. Associative containers 3. Container Adapters 4. Specialized/ Near containers

There are 3 types of sequence containers namely vectors, lists and deque. Algorithms are used to perform some operation on the elements held within these containers. These algorithms are implemented as functions and whenever you want to use them you will usually have to pass iterators to the function. For example by passing 2 iterators we can specify the elements on which we want the algorithm to act upon.

516

Data Structures

Page 518: C++ Basics to Advanced

There are 5 types of iterators:

1. Input - used to read elements from a container. They can move in only the forward direction (i.e. from the first element to the last) and they can pass only once through the container.

2. Output - used to write elements to a container. They are similar to input iterators. 3. Forward - they are a combination of the input and output iterators. They can be used

to pass multiple times through the container. 4. Bidirection - they have the properties of the forward iterator and they can be used to

move in the backward direction also. 5. Random access - They have the properties of the bidirectional iterator and also permit

random access of container elements.

There is a hierarchy for the 5 types of iterators and the ones at the top of the hierarchy are considered ‘weaker’ than those at the bottom of the hierarchy. The lower levels in the hierarchy support all the functions that are performed by the ones above it (thus they are stronger than the ones above them).

Containers and iterators are related. Every type of container will support particular types of iterators. Also each container has a set of member functions and some of these functions will return iterators.

The following member functions are a few of the common functions available in almost all STL containers (including vectors).

Function/operators Use

bool empty( ) Returns true if container is empty.

size_type size( ) Returns the number of elements present in the container.

void clear( ) Clears the contents of the container.

const_pointer/pointer begin( ) Returns reference to the first element of the container.

forward

bidirectional

random access

outputinput

517

Data Structures

Page 519: C++ Basics to Advanced

const_pointer/pointer end( ) Returns a reference to the end of the container.

reverse_iterator rbegin( ) Returns a reverse iterator.

reverse_iterator rend( ) Returns a reverse iterator.

erase( ) Used to erase a particular element or a range of elements (needs to be passed iterator arguments).

Overloaded operators = =,<,<=,>,>= All return a Boolean value depending on the result of comparison.

Overloaded assignment operator = Used to assign one container to another.

Note: Italics denote the return data type of the functions.

We’ll use some of the overloaded operators in programs to illustrate their working.

Some of the return data type might appear to be new. These are actually typedef’s which have already been defined. Some typical typedefs you might encounter in STL are:

Typedef Description

size_type Data type used to count the number of elements in a container.

const_iterator An iterator that can be used to only read elements of a container.

reverse_iterator This is used to pass through a container in the reverse order (from the last element to the first).

const_reverse_iterator A constant reverse iterator used for reading the elements of the container in reverse order.

Next we’ll take a look at each of the container types in a little more detail.

Learn about Sequence Containers (Vectors)

518

Data Structures

Page 520: C++ Basics to Advanced

Sequence Containers

As the name implies, these containers will have elements arranged in a linear manner. There are 3 types of sequence containers:

• Vectors • Deque • List

There are many functions which are common to all sequence containers while a few are specific to some of them.

Vectors

A vector is basically an array, which can grow dynamically in size as and when required. The programmer does not need to worry much about memory management since this is taken care of automatically. The following are some interesting points to note regarding vectors:

• Though vectors can grow dynamically in size, they still need to occupy contiguous memory locations (just like arrays). So, suppose you increase the size of your vector from 4 elements to 5 and if there isn’t contiguous memory space available at the current location then the vector will be moved to some other memory location where contiguous space for all the elements are available (effectively it will copy the elements to the new location and then destroy the old one). This process brings to light one disadvantage of using vectors.

• Another problem is when you are inserting an element in the middle of a vector. In this case, all the elements that should follow the inserted element have to be shifted before the element can be inserted.

• Vectors are useful when random access to elements is needed. This can be done using subscripts (just like arrays).

• Vectors provide boundary checking also (something that isn’t available in arrays and which generally leads to a lot of errors in programs). Boundary checking means that you cannot attempt to reference a vector element outside the defined range (for example if your vector can hold 100 elements then you cannot attempt to access the 105th element. This was a problem in arrays since such checking is not available).

• Vectors are recommended if your program is going to add elements to the end of the vector and if random access is required.

Function Use

*front( ) Returns the value of the first element stored in the vector.

*back ( ) Returns the last element value stored in the vector.

*push_back( ) Pushing a new element (specified by the

519

Data Structures

Page 521: C++ Basics to Advanced

argument) into the container

*pop_back( ) Pops out the element at the end of the container (no arguments for this function).

*assign( ) Assign values to elements of the container

capacity( ) Returns the maximum elements that can be stored in the vector before automatic resizing

Note: * denotes that these functions are available in all sequence containers.

Capacity and Size of a vector:

Vectors keep resizing automatically as they increase in size. Thus memory is allocated automatically when needed as demonstrated in the program below:

#include <iostream> #include <vector> using namespace std;

int main( ) { vector<int> intvector; intvector.push_back(1); cout<<"Size is:"<<intvector.size( )<<" Capacity : "<<intvector.capacity( );

intvector.push_back(2); cout<<"\nSize is:"<<intvector.size( )<<" Capacity : "<<intvector.capacity( );

intvector.push_back(3); cout<<"\nSize is:"<<intvector.size( )<<" Capacity : "<<intvector.capacity( );

intvector.push_back(4); cout<<"\nSize is:"<<intvector.size( )<<" Capacity : "<<intvector.capacity( );

intvector.push_back(5); cout<<"\nSize is:"<<intvector.size( )<<" Capacity : "<<intvector.capacity( );

intvector.push_back(6); cout<<"\nSize is:"<<intvector.size( )<<" Capacity : "<<intvector.capacity( );

return 0; }

520

Data Structures

Page 522: C++ Basics to Advanced

The output is:

Size is:1 Capacity : 1

Size is:2 Capacity : 2

Size is:3 Capacity : 4

Size is:4 Capacity : 4

Size is:5 Capacity : 8

Size is:6 Capacity : 8

Size denotes the amount of memory space currently being used by the vector and capacity denotes the amount of memory allocated for the vector (as more space is needed this will keep increasing). It can be observed

Thus memory is allocated as follows:

• If the size of the vector is less than the capacity (i.e. the vector can still accommodate more elements without more memory being wanted) and if you insert an element, extra memory will not be allocated.

• If the size is equal to the capacity and if you insert an element then the capacity (i.e. the space occupied by the vector) will be doubled.

Using overloaded operators on STL containers:

Let’s take a look at the overloaded operators which are available in all containers using a simple program.

//illustrating the use of overloaded operators in STL vectors

#include <iostream> #include <vector> using namespace std;

int main( ) { vector<int> intvec1; vector<int> intvec2; vector<int>::const_iterator ptr; //declaring a constant iterator

intvec1.push_back(10); intvec1.push_back(20); intvec2.push_back(20);

if (intvec1<intvec2) { cout<<endl<<"Vector 1 is less than vector 2."<<endl;

521

Data Structures

Page 523: C++ Basics to Advanced

intvec1=intvec2; }

cout<<endl<<"Vector 1 now contains: ";

for ( ptr=intvec1.begin( ); ptr!=intvec1.end( ); ptr++ ) { cout<<" "<<*ptr; //dereferencing the iterator }

return 0; }

The output will be:

Vector 1 is less than vector 2.

Vector 1 now contains: 20

The code

vector<int>::const_iterator ptr;

is used to declare a constant iterator which can be used to only read the elements of a container.

When using the overloaded operators on containers, take note of the following points:

• The 2 containers being compared should be of the same type (i.e. if we have a vector containing integers then it can be compared or assigned to another vector of type integer).

• The relational operators work on an element by element comparison. In the above program:

Even though the size of vector 1 is more than vector 2, the first element of vector 2 is greater than that of vector 1 (20 > 10).

Go to the next section on Subscripts and Vectors

VECTOR 2

VECTOR 1

20

20 10

522

Data Structures

Page 524: C++ Basics to Advanced

Subscripts and Vectors

Subscripts in Vectors:

You can use the subscripts just like in arrays. The following is the method for using subscripts:

vector<int> intvec1;

intvec1.push_back(10);

intvec1.push_back(20);

intvec1[0] = intvec1[1] + 5;

After executing this piece of code, the vector intvec1 will contain 2 elements with the values 25 and 20.

Using subscripts in vectors helps in random access of the elements.

Boundary Checking in Vectors:

When you use subscripts to access vector elements boundary-checking is not performed. For example the following code fragment will cause a run-time error:

vector<int> intvec1; vector<int>::const_iterator ptr; //declaring a constant iterator intvec1.push_back(10); intvec1.push_back(20); intvec1[2]=30; //element does not exist cout<<endl<<"Vector 1 now contains: "; for (ptr=intvec1.begin( ); ptr!=intvec1.end( ); ptr++) { cout<<" "<<*ptr; }

Since intvec1[2] does not exist the program will crash while running. While running the program does not check to see whether the value 2 is within the boundary limit of the vector. To prevent this problem we can make use of the member function ‘at’ for vectors.

Instead of

intvec1[2]=30;

we can use

intvec1.at(2)=30;

523

Data Structures

Page 525: C++ Basics to Advanced

Now when the program is run, an exception will be thrown and this can be caught to prevent crashing. A simple program is illustrated below:

//Demonstrating the use of 'at' in vectors

#include <iostream> #include <exception> #include <vector> using namespace std;

int main( ) { vector<int> intvec1; vector<int>::const_iterator ptr; //declaring a constant iterator

intvec1.push_back(10); intvec1.push_back(20);

try { intvec1.at(2)=30; cout<<endl<<"Vector 1 now contains: ";

for (ptr=intvec1.begin( ); ptr!=intvec1.end( ); ptr++) { cout<<" "<<*ptr; }

}

catch(out_of_range exc) { cout<<endl<<"EXCEEDED THE BOUNDARY OF VECTOR"; cout<<endl<<"PROGRAM TERMINATED"; cout<<endl<<exc.what( ); }

return 0; }

The output will be:

EXCEEDED THE BOUNDARY OF VECTOR

PROGRAM TERMINATED

invalid vector<T> subscript

When this code:

intvec1.at(2)=30;

524

Data Structures

Page 526: C++ Basics to Advanced

is executed it will check the boundary limit of the vector. Since the vector currently has only 2 elements, it will throw an ‘out_of_range’ exception. By catching this you can prevent the program from crashing. In the above program ‘exc’ is an object of type out_of_range and it has a member function what( ).

exc.what( );

This will give a short description about what caused the problem.

More functions for Vectors

Using various functions available in Vectors:

//A program using various vector functions

#include <iostream> #include <vector> using namespace std;

//Function to display the contents of a vector void display(vector<int> &vec) { vector<int>::const_iterator ptr; for (ptr=vec.begin( ); ptr!=vec.end( ); ptr++) { cout<<" "<<*ptr; } }

int main( ) { vector<int> intvec1; int i; intvec1.assign(4,7); cout<<endl<<"After using assign: "; display(intvec1);

for (i=1;i<5;i++) { intvec1.push_back(i); }

cout<<endl<<"After pushing: "; display(intvec1);

intvec1.pop_back( ); cout<<endl<<"After popping: "; display(intvec1);

525

Data Structures

Page 527: C++ Basics to Advanced

intvec1.erase(intvec1.begin()); cout<<endl<<"After erasing the first element: "; display(intvec1);

cout<<endl<<"Capacity is: "<<intvec1.capacity(); intvec1.resize(17); cout<<endl<<"Capacity after resizing: "<<intvec1.capacity();

cout<<endl<<"After resizing: "; display(intvec1);

intvec1.assign(4,7); cout<<endl<<"After using assign: "; display(intvec1);

intvec1.clear( ); cout<<endl<<"After clearing: "; display(intvec1);

return 0; }

The output will be:

After using assign: 7 7 7 7 After pushing: 7 7 7 7 1 2 3 4 After popping: 7 7 7 7 1 2 3 After erasing the first element: 7 7 7 1 2 3 Capacity is: 8 Capacity after resizing: 17 After resizing: 7 7 7 1 2 3 0 0 0 0 0 0 0 0 0 0 0 After using assign: 7 7 7 7 After clearing:

There are 2 forms of the erase ( ) function. Either we can specify one iterator (this will erase that particular element) or you can specify a pair of iterators (this will erase all the elements in between these 2 iterators).

The resize( ) function can take 2 arguments. The first one specifies the capacity you want to allocate for the container and the second argument can be used to specify the values you want to initialize the elements to (by default this is 0 as illustrated in the program above).

526

Data Structures

Page 528: C++ Basics to Advanced

STL Lists 1

We’ve already written a program that makes use of the STL list container. Let’s take a deeper look into the list container in this section. The STL list container is a doubly linked-list. Thus each node has the element as well as two pointers pointing to the next and previous node of the list. Lists are advantageous if there are going to be many insertions and removals from the middle of the list (since all you need to do is modify the pointers). Lists do not support random access. Thus the list container supports the bidirectional iterator (not the random access iterator which is supported by the vector container).

Before going into an example program let’s take a look at the insert member function. The insert function is available in all the sequence containers and it has three forms:

intlist.insert(intlist.begin( ), val);

will insert the value ‘va’ at the beginning of the container.

intlist.insert(intlist.begin( ), n, val);

will insert the value ‘val’ n times at the beginning of the container.

intlist.insert(intlist.begin( ), iterator/pointer, iterator/pointer);

will insert the elements from the first pointer to the second pointer at the beginning of the container.

Note: You needn’t always insert at the beginning of a container. The first argument for the function insert ( ) specifies the location where you want to insert the values. By changing this iterator you can insert at different positions.

Member functions for the List container:

These two functions are available in the list and deque containers but not in the vector container:

push_front( )

pop_front( )

The following functions are available only in the list container:

splice( )

merge( )

unique( )

527

Data Structures

Page 529: C++ Basics to Advanced

sort( )

reverse( )

remove( )

Note: The sort( ) function is available as an algorithm and also as a member function in a few containers. The algorithm sort form can be used across any container (but it requires a random access iterator). If the container you are using has a specific member function sort( ), then it is more efficient to use this function than the generic sort because member functions have been written knowing the internals of that particular container. The generic sort will not take advantage of the internal structure of the container since it has to work on all containers.

STL Lists 2 (program)

//A program to illustrate the list functions

#include <iostream> #include <list> using namespace std;

//Function to display the contents of the list void display(list<int> &list1) {

list<int>::const_iterator ptr;

for (ptr=list1.begin();ptr!=list1.end();ptr++) { cout<<" "<<*ptr; } }

int main( )

{ list<int> intlist1,intlist2; const int SIZE=4; int arr[SIZE]={5,8,4,2};

//adding 4 values to the list (1 2 3 4) for(int i=1;i<5;i++) { intlist1.push_back(i); }

528

Data Structures

Page 530: C++ Basics to Advanced

intlist1.push_front(8); intlist1.push_back(1); intlist1.push_back(1); cout<<endl<<"List 1 :"; display(intlist1);

intlist1.unique( ); cout<<endl<<"List 1 after unique:"; display(intlist1);

intlist2.insert(intlist2.begin( ), arr, arr+SIZE); cout<<endl<<"List 2 :"; display(intlist2);

intlist1.splice(intlist1.end( ),intlist2); cout<<endl<<"List 1 after splicing:"; display(intlist1); cout<<endl<<"List 2 after splicing:"; display(intlist2);

intlist2.insert(intlist2.begin( ), arr, arr+SIZE); intlist2.sort( ); intlist1.sort( ); cout<<endl<<"List 1 after sorting :"; display(intlist1); cout<<endl<<"List 2 after sorting :"; display(intlist2);

intlist1.merge(intlist2); cout<<endl<<"List 1 after merging :"; display(intlist1); cout<<endl<<"List 2 after merging :"; display(intlist2);

intlist1.unique( ); cout<<endl<<"List 1 after unique :"; display(intlist1);

intlist1.remove(5); cout<<endl<<"List 1 (5 removed) :"; display(intlist1);

intlist1.swap(intlist2); cout<<endl<<"List 1 after swapping:"; display(intlist1); cout<<endl<<"List 2 after swapping:"; display(intlist2);

return 0; }

529

Data Structures

Page 531: C++ Basics to Advanced

The output will be:

List 1 : 8 1 2 3 4 1 1 List 1 after unique : 8 1 2 3 4 1 List 2 : 5 8 4 2 List 1 after splicing : 8 1 2 3 4 1 5 8 4 2 List 2 after splicing : List 1 after sorting : 1 1 2 2 3 4 4 5 8 8 List 2 after sorting : 2 4 5 8 List 1 after merging : 1 1 2 2 2 3 4 4 4 5 5 8 8 8 List 2 after merging : List 1 after unique : 1 2 3 4 5 8 List 1 (5 removed) : 1 2 3 4 8 List 1 after swapping : List 2 after swapping : 1 2 3 4 8

We’ll take a look at the function individually.

intlist1.unique( );

As you can see from the output of the above program, this function is effective only when the values in the list are sorted (otherwise there is a good chance that the same value might still have duplicates in the list).

intlist2.insert(intlist2.begin( ), arr, arr+SIZE);

This is the 3rd form of the insert ( ) function which we discussed earlier. Here ‘arr’ is a pointer (pointer to the first element of the array ‘arr’). Adding SIZE gives the second pointer (which will point to the last element of ‘arr’). Thus the array ‘arr’ will be inserted before the beginning of the list intlist2.

intlist1.splice(intlist1.end( ),intlist2);

Splice will remove the elements of the second container and add them to the first container after the position referred to in the first argument. There are 2 more forms of the splice ( ) function available.

void splice (iterator, 2nd list, iterator-to-element);

This 3 argument form of splice will move a single element to the list container that called the splice function.

void splice (iterator, 2nd list, iterator-to-1st element, iterator-to-2nd element);

The 4-argument form of splice can be used to move a range of elements from the 2nd list.

intlist1.sort( );

This is a member function sort ( ) which is specific for the list container. It takes advantage of the fact that when dealing with lists we don’t need to copy or move the objects (it is sufficient to change the linking pointers). This function arranges the elements in ascending order.

530

Data Structures

Page 532: C++ Basics to Advanced

intlist1.merge(intlist2);

The merger( ) function will remove elements from the 2nd list (i.e. the list specified as an argument) and insert them into the first list. For the merge( ) function to work properly both the lists should be sorted (the results otherwise will be ambiguous).

intlist1.remove(5);

The remove( ) function removes all occurrences of the specified element from the list.

Beware: remove( ) will eliminate all occurrences of the element (not just the first occurrence but all).

intlist1.swap(intlist2);

The swap( ) function swaps the specified lists.

STL Deque

The deque (short form for double-ended queue) is similar to a vector. It has random access capability using subscripts but does not have the limitation of needing contiguous memory. Memory for a deque is allocated in terms of many storage blocks. Deque is efficient in insertions and deletions at its front and back. Unlike a vector it has the member functions push_front( ) and pop_front( ).

It does not have the capacity( ) and reserve( ) functions available in vectors. The reserve( ) function in vectors can be used to specify how many elements you will store in the vector (this will reduce the amount of reallocations in a vector). Generally a vector is advisable if you have an idea about the number of elements you will need to store. A deque is efficient in situations where you need to do frequent insertions and deletions at the front.

There are no exclusive deque member functions. All of the member functions are available in other containers (unlike the list container which has exclusive functions).

531

Data Structures

Page 533: C++ Basics to Advanced

Let’s take a look at a simple program to illustrate a deque. //Program to use the deque STL container

#include <iostream> #include <deque> using namespace std;

void display(deque<int> &deq) { deque<int>::iterator ptr; for (ptr=deq.begin( );ptr!=deq.end( );ptr++) { cout<<" "<<*ptr; } }

int main( ) { deque<int> deqint; deque<int>::reverse_iterator rptr;

for (int i=1;i<5;i++) { deqint.push_front(i); } cout<<endl<<"Deque contains: "; display(deqint);

cout<<endl<<"The first element is : "<<deqint[0];

deqint.at(2)=99; cout<<endl<<"Deque contains: "; display(deqint);

//using a reverse iterator to display contents cout<<endl<<"Deque contents in reverse: "; for (rptr=deqint.rbegin( ); rptr!=deqint.rend( ); rptr++) { cout<<" "<<*rptr; }

return 0; }

The output will be: Deque contains : 4 3 2 1

The first element is : 4

Deque contains : 4 3 99 1

Deque contents in reverse : 1 99 3 4

532

Data Structures

Page 534: C++ Basics to Advanced

The functions are pretty much the same as we have seen earlier. We have used the reverse iterator to illustrate its working. Basically the reverse iterator can be used to pass through a container from the back to the front.

rbegin( ) will position the iterator at the end of the container. Whenever the reverse iterator is incremented it will move towards the beginning.

Associative Containers

Associative containers are effective in searching for elements based on keys. For example: You may want to obtain the name of an employee using his ID number. In this case, ID number is the key based on which you will search and retrieve the name.

These containers will store the elements based on the value of the key (i.e. they may store the elements in ascending order of their keys). This is what permits efficient searching of associative containers. Since they depend on the key value, generally you cannot modify the value of the key itself. Associative containers can be divided into two types:

• Simple associative • Pair associative

Pair associative is the example we discussed about employee ID and employee name. In this case the part of the element which is the key (i.e. the ID number) cannot be modified but the other part (i.e. the employee name) can be changed.

In simple associative containers we store the entire element as the key. Thus you cannot modify the elements of a simple associative container.

Again within the simple and pair associative containers, we have two more sub divisions:

• Unique key • Multiple key

In the unique key type, the key cannot be repeated (i.e. no two elements can have the same key). In multiple key type, many elements can have the same key value. For example: in a college, each student will take up lots of courses. Thus the student ID number is the same but there will be many subjects under that ID.

To permit quick retrieval of data, the elements are arranged in a tree structure (the binary tree structure aids in quick retrieval of data).

533

Data Structures

Page 535: C++ Basics to Advanced

Some common functions available in all associative containers are listed below.

Member function Use

void erase ( ) 3 forms available - Erases a particular element based on key value or based on a reference or a range of elements.

void clear( ) Erases all the elements of the container.

iterator find (x) The value of the key to be searched is passed as argument.

size_type count(x) Returns the number of elements with the key value ‘x’.

pair equal_range(x) Returns a pair of iterators specifying the range of elements having the key value as x.

Note: italics denotes the return data type.

Sorted associative containers contain some more member functions in common. The 4 containers we will discuss about are:

• Set • Multi-set • Map • Multi-map

All four are sorted containers.

534

Data Structures

Page 536: C++ Basics to Advanced

Set Container

Set is a simple, sorted, unique associative container. Attempting to insert a duplicate key will not lead to an error; it will simply be ignored by the container. If you are creating a word game in which you want to check whether a particular word exists in the list then you only need to store the words (i.e. you only need to keep track of the keys since you won’t be needing anything else). Whenever the user forms a word, all you need to do is use the find( ) member function to see if the word exists in the container.

There are many algorithms which you can use on containers but we’ll be dealing with algorithms later on.

Let’s check out an example program that uses the set container.

//Program using the 'set' container

#include <iostream> #include <set> #include <string> using namespace std;

int main( ) { typedef set<string,less<string> > stringset; stringset strset1; stringset::const_iterator ptr;

string a("test"); strset1.insert(a); strset1.insert(a); strset1.insert("testing"); strset1.insert("apple"); strset1.insert("sun");

cout<<endl<<"The container has the following words:";

for (ptr=strset1.begin( ); ptr!=strset1.end( ); ptr++) { cout<<endl<<*ptr; }

ptr = strset1.find("test"); if (ptr!=strset1.end( )) { cout<<endl<<"Found the word test"; }

cout<<endl<<"Size of the container: "<<strset1.size( ); return 0; }

535

Data Structures

Page 537: C++ Basics to Advanced

The output will be:

The container has the following words:

apple sun test testing

Found the word test Size of the container: 4

To make use of the set and multiset containers you need to include the <set> header file. A typedef has been used to avoid retyping the entire set declaration again.

typedef set<string,less<string> > stringset;

stringset strset1;

The part

set<string,less<string> >

says that we are declaring a set which will contain strings and is arranged in ascending order based on the string value. The data type of elements you use in the set container should support the < comparison operator. Hence if you are using a set container to hold your own user-defined data types, then you should have provided with an overloaded < operator function for that data type. The code:

strset1.insert(a);

strset1.insert(a);

should insert the same string twice but as you can see in the output this string is stored only once in the container. Such an operation does not cause an error. The code:

ptr = strset1.find("test");

if (ptr!=strset1.end( ))

checks to see if the string "test" is present in the container. If it is found in the container then the find( ) function will return the iterator corresponding to the position where the element was found. If the element is not present in the container then find( ) will return a reference to end of the container (i.e. a reference to a position beyond the last element of the container).

Remember: the end( ) function does not return an iterator to the last element; it returns an iterator past the last element.

The rest of the program uses the normal member functions that we have already discussed. Do take note of the output of the program. As you can see, the words are stored in ascending order (irrespective of their order of insertion).

536

Data Structures

Page 538: C++ Basics to Advanced

MultiSet Container

The main difference between set and multiset is that in multiset we can use duplicate key values. In multiset containers, the member function equal_range( ) will have some use. Let’s see a program using multiset container.

#include <iostream> #include <set> using namespace std;

int main( ) { typedef multiset<int,less<int> > intset; intset intset1; intset::const_iterator ptr; pair<intset::const_iterator,intset::const_iterator> p;

intset1.insert(9); intset1.insert(3); intset1.insert(9); intset1.insert(1);

for (int i=0;i<3;i++) { intset1.insert(8); }

cout<<endl<<"The container has the following numbers:"; for (ptr=intset1.begin( );ptr!=intset1.end();ptr++) { cout<<" "<<*ptr; }

cout<<endl<<"Size of the container: "<<intset1.size( ); p=intset1.equal_range(8); cout<<endl<<"At the lower end:"<<*(p.first); cout<<endl<<"At the upper end:"<<*(p.second);

return 0; }

The output will be:

The container has the following numbers: 1 3 8 8 8 9 9

Size of the container: 7

At the lower end:8

At the upper end:9

537

Data Structures

Page 539: C++ Basics to Advanced

The code:

pair<intset::const_iterator,intset::const_iterator> p;

will create an object ‘p’ of belonging to the class ‘pair’. Pair objects will contain a pair of values. The reason we have to use a pair in this program is because the equal_range( ) member function will return a pair of iterators. The function is:

pair-iterators equal_range(value)

The following diagram illustrates the iterators that are returned by equal_range(8).

To access the two iterators separately you can use the dot operator. The two parts are referred to as .first and .second. Hence in our program since the pair object is named ‘p’, we can refer to these two iterators as p.first and p.second.

Suppose you don’t want to get the pair of iterators and instead you just want one of them, you can use the member functions:

const_iterator lower_bound(value)

const_iterator upper_bound(value)

to yield the same results. The function lower_bound( ) returns an iterator pointing to the first occurrence of the ‘value’ element and upper_bound( ) returns an iterator past the last occurrence of the element with ‘value’.

Go to the next section on: Container Map

538

Data Structures

Page 540: C++ Basics to Advanced

Container Map

In a map you can store the key along with a separate value (of course to make logical sense the key and value should be related). It is a pair associative container. For example: Every employee has an employee ID number as well as a name. The ID will form the key while the value will be the employee name. In a map we call the relationship between the key and the value as one-to-one relationship (i.e. every key has a corresponding single value).

//Program using the 'map' container

#include <iostream> #include <map> using namespace std;

int main( ) { typedef map<int,char,less<int> > mapint; mapint mapalpha; mapint::const_iterator ptr;

mapalpha.insert(pair<int,char>(1,'a')); mapalpha.insert(pair<int,char>(2,'b')); mapalpha.insert(pair<int,char>(3,'c')); mapalpha[4]='d'; mapalpha.insert(make_pair(5,'e'));

for (ptr=mapalpha.begin( );ptr!=mapalpha.end( );ptr++) { cout<<endl<<ptr->first<<" "<<ptr->second; }

return 0; }

The output will be:

1 a 2 b 3 c 4 d 5 e

539

Data Structures

Page 541: C++ Basics to Advanced

The code:

typedef map<int,char,less<int> > mapint;

mapint mapalpha;

declares mapalpha to be of type ‘map’ having elements consisting of an integer and a character and arranged in ascending order of the integer (which is the key). Again there are 3 forms of the insert function. We have used the simplest:

mapalpha.insert(pair<int,char>(1,'a'));

This will insert the new pair of values into the container. We are using the class pair to construct the key-value pairs since we need to insert 2 values for each element (the key and the value).

Another way to construct the key-value pair combination is to make use of the make_pair( ) function. This is used in:

mapalpha.insert(make_pair(5,'e'));

Now a pair consisting of an integer value and a character are made and inserted into the map container.

The ‘map’ container also supports the subscript operator which is dual purpose: if the element exists then the value can be updated or else the element can be added.

mapalpha[4]='d';

Since an element with the key of 4 does not exist in the container, the key (4) and value (‘d’) will be added to the container. Suppose the key 4 was existing then the above code would change the value at key 4 into ‘d’. The subscript operator is basically implemented as a form of the insert function.

Go to the next section on: Container MultiMap

540

Data Structures

Page 542: C++ Basics to Advanced

Container MultiMap

Similar to a map container but it can contain duplicate keys. Let’s write a program to store objects of user-defined classes in the multimap container. We shall create two classes named student (with the name as its member) and another class subject (with name and maximum marks as the members). We shall store the names of students along with the subjects that they’ve enrolled for in the multimap container.

//Program using the 'multimap' container to store user-defined objects

#include <iostream> #include <map> #include <cstring> using namespace std;

class student { private: char name[40];

public: student( ) { strcpy(name," "); }

student(char *a) { strcpy(name,a); }

char* str( ) { return name; }

};

bool operator <(student a,student b) { return strcmp(a.str( ),b.str( ))<0; }

class subject { private:

541

Data Structures

Page 543: C++ Basics to Advanced

char name[20]; int maxmarks;

public: subject( ) { strcpy(name," "); maxmarks=0; }

subject(char *a,int x) { strcpy(name,a); maxmarks=x; }

char* str( ) { return name; }

};

int main( ) { typedef multimap<student,subject,less<student> > mapsub; mapsub studsubj; mapsub::const_iterator ptr;

studsubj.insert(pair<student,subject>(student("john"), subject("biology",100))); studsubj.insert(pair<student,subject>(student("john"), subject("chemistry",200))); studsubj.insert(pair<student,subject>(student("ian"), subject("french",100)));

for (ptr=studsubj.begin( );ptr!=studsubj.end( );ptr++) { student st=ptr->first; subject sub=ptr->second; cout<<endl<<st.str( )<<" "<<sub.str( ); }

return 0; }

The output will be:

ian french

john biology

john chemistry

542

Data Structures

Page 544: C++ Basics to Advanced

The code:

typedef multimap<student,subject,less<student> > mapsub;

mapsub studsubj;

will create a multimap container to hold elements of type student and subject with student being the key and elements ordered in ascending order of student.

Beware: Since the elements will be ordered in ascending order based on student, the class student should have an overloaded < operator (because the container will compare objects using < and decide on the order).

Since student is the key, we need to overload the < operator for the student class.

bool operator <(student a,student b)

{

return strcmp(a.str( ),b.str( ))<0;

}

In this program we just compare the ‘name’ element of the two objects using the strcmp( ) function. The reason for using a.str( ) is because you cannot directly access the private member name( ) using the dot operator.

studsubj.insert(pair<student,subject>(student("john"), subject("biology",100)));

Inserting elements into the container is similar to what we did in the map container. You construct a pair of values of the corresponding data type (in this case it is our own user-defined data type which makes use of the class’ constructor).

for (ptr=studsubj.begin( );ptr!=studsubj.end( );ptr++)

{

student st=ptr->first;

subject sub=ptr->second;

cout<<endl<<st.str( )<<" "<<sub.str( );

}

In this program we just want to display the list of what is stored in the container. ptr->first will contain an object of type ‘student’. We cannot directly code:

cout<<ptr->first;

because we haven’t overloaded the >> operator for the class ‘student’. Thus we need to call the member function str( ) which will return the character array value stored in ‘name’.

543

Data Structures

Page 545: C++ Basics to Advanced

Container Adaptors and Algorithms

The following topics are covered in this section:

• Container Adaptors • Algorithms

Container adaptors adapt and use one of the previously discussed data structures. They do not have their own data structure implementation but instead we can select the data structure. They do not support iterators and there are 3 types of container adaptors:

1. Stack 2. Queue 3. Priority Queue

By default the stack and queue are based on deque while priority queue is based on vector. Of course you can specify your own data structure to override the default but generally programmers will not change the default container.

Stack

The stack is built upon a deque. You might be wondering why we need a stack when it is ultimately based on one of the other existing containers? The reason is that by using a stack you clearly define the purpose for the container. When you use a stack it indicates that you are only going to perform valid stack operations and that you won’t violate the stack by using other operations. You can perform the functions of the stack container using a deque or vector but when you use these containers other operations (which are not stack operations) are also valid. Basically the stack adaptor converts the underlying data structure into a stack (i.e. such that only stack operations are permitted).

The header file needed to use a stack is: <stack>. Iterating through the elements of a stack is not permitted (iterators are not supported). You can push and pop elements from a stack. The pop( ) function will not return the value of the element present at the top. To read the element at the top you have to use the top( ) function.

We have already seen a program using the STL stack container.

544

Data Structures

Page 546: C++ Basics to Advanced

//Program to illustrate the stack container of STL

#include <iostream> #include <stack> using namespace std;

int main( ) { stack<int> intstack; int i;

//Adding 5 values to the stack (100 101 102 103 104) for( i=100; i<105; i++ ) { intstack.push(i); }

//Displaying the size of the stack cout<<endl<<"Number of elements in the stack: "<<intstack.size( );

//Popping the elements of the stack till it becomes empty. //Before popping we display the value of the element which is // going to be popped

cout<<endl<<endl<<"Popping the elements one by one"<<endl;

for (i=0; !intstack.empty( ); i++) { cout<<endl<<intstack.top( ); intstack.pop( ); cout<<endl<<"Size of stack after popping: "<<intstack.size( ); }

return 0; }

The output will be:

Number of elements in the stack: 5

Popping the elements one by one

104 Size of stack after popping: 4 103 Size of stack after popping: 3 102 Size of stack after popping: 2 101 Size of stack after popping: 1 100 Size of stack after popping: 0

545

Data Structures

Page 547: C++ Basics to Advanced

Suppose you want to use a different data stucture as the base for the stack then you have to declare the stack as:

stack<int,vector<int> > intvec;

This declares ‘intvec’ as a stack using a vector as the underlying data structure.

Queue

The queue adaptor is by default modeled on the deque. The reason for this adaptor is the same as the reason why stack exists. Only queue operations can be performed when using the queue container. The reason for using queue instead of deque is to emphasize on the fact that you will be using only queue operations. Just like a stack we have the push and pop functions but instead of top( ) we have the front( ) function to read the first element in the queue.

An example program using the queue adaptor container has been discussed earlier.

//A program to illustrate the STL container for queues

#include <iostream> #include <queue> using namespace std;

int main( ) { queue<int> intqueue; int i;

//adding 5 values to the queue (0 1 2 3 4)

for(i = 0; i < 5 ; i++) { intqueue.push(i); }

cout<<endl<<"The first element is : "<<intqueue.front( ); cout<<endl<<"The last element is : "<<intqueue.back( ); cout<<endl<<"The size of the queue is: "<<intqueue.size( );

//Removing the elements of the queue one by one cout<<endl<<"Queue contents are : ";

for (i = 0; !intqueue.empty( ); i++) { cout<<endl<<"Element removed: "<<intqueue.front( ); intqueue.pop( ); cout<<endl<<"Size of queue after popping: "<<intqueue.size( ); } return 0; }

546

Data Structures

Page 548: C++ Basics to Advanced

The output of the program will be:

The first element is : 0 The last element is : 4 The size of the queue is: 5 Queue contents are : Element removed: 0 Size of queue after popping: 4 Element removed: 1 Size of queue after popping: 3 Element removed: 2 Size of queue after popping: 2 Element removed: 3 Size of queue after popping: 1 Element removed: 4 Size of queue after popping: 0

Priority Queue

This is similar to a queue except that the elements get inserted into the queue based on their priority. Thus you can insert elements in sorted order into the underlying data structure (which is a vector by default). Take a look at the program below:

#include <iostream> #include <queue> using namespace std;

int main( ) { priority_queue<int,vector<int>,less<int> > intque;

for (int i=0;i<5;i++) { intque.push(i); }

while (intque.empty( )!=true) { cout<<" "<<intque.top( ); intque.pop( ); }

return 0; }

The output will be:

4 3 2 1 0

547

Data Structures

Page 549: C++ Basics to Advanced

As you can see even though the first element to enter the queue was 0 it ended up being the last element of the queue because all the other elements had a greater priority. To decide on the priority take a look at the declaration of the priority queue:

priority_queue<int,vector<int>,less<int> > intque;

Since we have specified less<int>, this indicates that the elements with higher value will be given greater priority (and hence they will be placed at the front of the queue). Instead of less<int> we can also use greater<int>. This has the opposite effect and elements with lower values will be placed at the front of the queue.

• greater< > and less< > are known as comparator function objects. These function objects decide the priority order. We used these function objects in associative containers as well.

• Iterators are not allowed in priority queues.

Algorithms

You will want to perform a lot of operations on your data structure. Some of the operations are quite common ones like sorting, searching, shuffling, finding the minimum or maximum element etc.

STL provides with a set of algorithms that can be applied across all data structures. These algorithms are generalized (i.e. they do not depend on the internal structure of the elements). For ex: the random_shuffle algorithm can work on vectors as well as on linked lists even though both are different data structures. How are algorithms implemented? Algorithms are basically template functions and most of the algorithms take iterators as arguments (thus they avoid relying on the data structure type). Some of these functions might also be available as member functions in some data types. In this case which one should we choose (the general function or the specific function)? It is better to use the specific member function since member functions are developed for specific data structures (they will be more efficient than the general algorithms).

There are many algorithms available in STL and we won’t be looking at all of them. To provide a basic idea of how they are used try out the following program:

#include <iostream> #include <vector> #include <algorithm> using namespace std;

int main( ) { vector<int> intvec1; for (int i=10;i<20;i++) { intvec1.push_back(i); }

548

Data Structures

Page 550: C++ Basics to Advanced

cout<<endl<<"Original Vector is: "; for (i=0;i<intvec1.size( );i++) { cout<<intvec1[i]<<" "; }

random_shuffle(intvec1.begin(),intvec1.end()); cout<<endl<<"Shuffled vector is: "; for (i=0;i<intvec1.size();i++) { cout<<intvec1[i]<<" "; }

sort(intvec1.begin(),intvec1.end()); cout<<endl<<"Sorted vector is : "; for (i=0;i<intvec1.size();i++) { cout<<intvec1[i]<<" "; }

return 0; }

The output will be:

Original Vector is: 10 11 12 13 14 15 16 17 18 19

Shuffled vector is: 14 13 10 12 16 17 18 19 15 11

Sorted vector is : 10 11 12 13 14 15 16 17 18 19

The two algorithms used above are: sort( ) and random_shuffle( ). The names are self-explanatory. We need to pass two iterators to define the range of elements on which we want the algorithm to act upon. In our program we’ve specified the entire range of the vector.

Some other algorithms available are:

min_element(iter1, iter2)

max_element(iter1, iter2)

find(iter1, iter2, value)

Remember: These algorithms are not member functions. Do not use the dot operator to invoke them.

549

Data Structures

Page 551: C++ Basics to Advanced

Trees

The following topics are covered in this section:

• Terminology • Binary Trees • An illustration of using binary trees

Terminology

Trees are non-linear data structures. They implement a hierarchical structure consisting of a set of nodes. Each node (a node is a member of the tree) except the root node will have only one parent but can have multiple children (called child nodes). The root node is the starting point of the tree – it does not have a parent node. The structural view of a tree should give you a good understanding about a tree.

In the above diagram ‘A’ is the root node. It has 3 child nodes- B, C and D. B has 2 child nodes E and F while C has only one child node G. As indicated above every node except the root node has a single parent node. No node in a tree can have two parent nodes.

Every node has a path connecting it to every other node. There are 2 links that have to be traversed if you want to reach ‘A’ from node ‘E’. Thus we say that the length of the path from E to A (or A to E) is 2. The length from B to E is 1.

The depth of a node is the length of the path of a node from the root node. This value is an indicator as to how far off the node is from the root. The depth of the root is 0. The height of the tree is the depth of the farthest node. In our case it would be 2 (A to E or A to F).

The degree of a node is equal to the number of its child nodes. B has a degree of 2.

Levels: Nodes B,C and D are at Level 1 while E and F are at Level 2 in the tree.

550

Data Structures

Page 552: C++ Basics to Advanced

Subtree and supertree: A tree actually consists of many subtrees rooted together. In the above case node B, E and F form a subtree. This subtree is rooted at node B. Supertree is the parent of the subtree.

An example of a tree: Most Operating Systems organize their file structure in the form of a tree. We’ll have a drive, under which we have a set of directories and under each directory you can have sub-directories or files. Check out the Windows Explorer and you’ll discover the concept of trees and sub-trees.

Binary Trees

We’ll mainly focus on binary trees which are effective when searching. Searching through a large amount of data is generally time consuming when using other data structures. Binary trees facilitate searching. So, what is a binary tree? A tree in which every node has two children is a binary tree.

How do we implement a tree programmatically?

You can create a tree using nodes (just like we created a linked list). Every node of a tree will contain:

1. Value stored at the node 2. A pointer to the next node on the right 3. A pointer to the next node at the left

In our linked list we had a node called the ‘head’. This basically was a node that contained the starting location of the linked list (because we need to know from which memory location our data structure starts).

In a tree we will use the same concept to identify the starting point of the tree. This node will only contain a single pointer pointing to the first node (i.e. the root node) of the tree. This is also called a ‘tree pointer’.

551

Data Structures

Page 553: C++ Basics to Advanced

In the above diagram we’ve illustrated a small and simple binary tree with a root node, 2 nodes branching off from the root and a tree pointer. All the ‘P’s in the figure denote a pointer (i.e. we’ll have to store memory locations). These memory locations will help us in identifying the two nodes to the left and right of the current node.

The figure below uses a few values to make the concept clear.

The reason binary trees are useful for searching is because we will insert nodes in an organized manner (i.e. we’ll insert them in a sorted fashion so locating an element becomes easy- it’s always easier to locate something in an arranged group than in an unsorted group). To achieve this, before inserting an element we’ll check as to whether the element has a value greater than the current node. If so then we need to place the element to the right of the current node, else to the left. Check out the diagram below (assume that we have created a binary tree to hold integers):

552

Data Structures

Page 554: C++ Basics to Advanced

Check the left and right values at each node in the diagram. The right node will always have a value greater than the parent node while the left node will always have a value lower than its parent.

You can check out an application of binary trees here.

Using Binary trees in Table Database indexes:

If you already know about Databases and Indexes then you probably have heard of binary

trees. If not I'll just try to provide a brief idea about indexes and binary trees in this section.

Let's consider our telephone directory. We always look up for the telephone number for some

particular name. And how do you do it? Suppose we are searching for the name Mathew

Thomas we would first open the directory to some page in the middle. If we see the names

starting in 'N' then we know that we have to go back a little. We skip back a few pages and

may land on an 'L'. Then we know that we have to go to somewhere in between these two.

Our search process continues in this manner. The most important fact is that the telephone

directory is sorted on the basis of names. If it weren't so, we wouldn't be able to locate names

in the directory. The type of searching we did is similar to a binary search in computer

terminology. The requirement for doing a binary search is that the data should be sorted.

Binary search is facilitated by storing the data in binary trees.

Let us suppose that we have a huge table containing all the records present in the telephone

directory (something like an online telephone directory). This table is going to have

thousands of rows and maybe 3/4 columns (one for the name, one for the telephone number

and maybe 2 for the address fields). We would create an index on one of these columns. An

index is created to make it easier for locating records in a table. If we didn't have an index

and if we wanted to locate Mathew Thomas in the database, we would have to search through

553

Data Structures

Page 555: C++ Basics to Advanced

each and every record since Mathew Thomas could be anywhere in the table. This is because

when you add rows to a table it could be in any order. I might first enter the name Welsh

Smith followed by Mathew Thomas. Thus in the table Welsh Smith would be stored first

followed by Mathew Thomas - the data in the table cannot be guaranteed to be ordered. But

now if we were to create an index on the name column, our database program will create an

index structure. We refer to this as the key (name will be the key in our index structure). The

index structure is not part of the actual table. It is like a supplement to the table and it will

contain just 2 columns- the key values (in our case this would be the list of names) and a

pointer to the actual row in the table. What's the point of storing all the names in an index

structure? The names would be stored in an ordered manner (they will be sorted in the index).

The following diagram should make things clear:

Each time you add a new record into the table, the index will be reshuffled (to make it

ordered) but the table will remain the same (the reason for this is that our table could have

many columns. In that case it would become a huge burden on the system to try and reshuffle

the rows of the table. Instead shuffling around the index structure is relatively easier since

there are only two columns). Now where does a binary tree come in? You must have guessed

by now. We could store all the name in a node (with the name that appears somewhere in the

middle as the root). Put the rest of the names in the respective places depending on whether

they are greater than the root value or not. And then it is easy to implement our binary search

algorithm once we have the binary tree in place.

I don't want to get into too much of details about indexes and databases over here in C++. If

you want to know more on Indexes you could check out some tutorials in the following link:

www.techneurons.com/Career/techtutor.asp

554

Data Structures

Page 556: C++ Basics to Advanced

Implementing a Binary Tree

Creating a binary tree is simple once you understand the concept of a tree. The programming is similar to the way we created a linked list using a class and a structure. Since the program will be large we’ll break it down into smaller parts and discuss them separately.

The node:

First of all we need to define our node. The node of a tree is similar to a linked list node except that we will have two pointers in every node of the tree (for the linked list every node had just one pointer). So let’s create a structure to represent our node:

struct node { string data; node *right; node *left; };

• We’ll create a binary tree to hold strings. So the data being held at each node will be of type ‘string’. You can use a character array or the pre-defined string class (I’m using the pre-defined string class, which is now available in all the latest compilers).

• Each node has to have a pointer to point to its right and left children. These pointers will have to be of type node itself.

The basic outline of the tree class:

class tree {

private: struct node { string data; node *right; node *left; }; node *root; void destroy_tree(node*);

public: tree( ) { root=NULL; } ~tree( ) { destroy_tree(root); }

};

555

Data Structures

Page 557: C++ Basics to Advanced

node *root; This is the pointer which will point to the first node of our tree. It defines the starting memory location of our binary tree.

void destroy_tree(node*); We’ll need to destroy the tree (using the delete function) and this function will serve the purpose. We will call this function from the destructor of the tree class. The reason we make this private is because we don’t want to allow a user to access this function. This will be explained later.

The constructor: When a tree is created we don’t have any nodes in the tree. So the root pointer should point to NULL (otherwise it would be pointing to some unknown memory location).

Member Functions

Next we’ll need some member functions. The basic functions are:

• Adding a new node • Destroying the tree

First let’s write the code for adding a new node to our tree:

Before starting with the code let’s write a pseudo algorithm (it is always a good practice to write algorithms before you jump into coding- it’ll save a lot of time).

1. Create a new node and assign the value to be stored to ‘data’. 2. Set the new node’s right and left pointers to NULL. Next we need to decide where to

insert this node. 3. Check if root is NULL. If so then make the root point to this node (i.e. this will now

become the first node of the tree). 4. Else compare the value of the node with the root node (the first node). If the value is

less than the root node value then we’ll have to insert this node into the left side. Else it’ll have to go into the right side.

5. Let’s assume that our value is greater than the root node. So, we’ll have to insert it to the right of the root node. Check if the right pointer of the root node is NULL. If so then we can simply make the right pointer point to our new node. If it is not NULL it means that some node is already present to the right of the root node. So compare the value with the existing node value and repeat the above steps (steps 3, 4 and 5) till you find a vacant place to insert the node.

You must have guessed by now that we’ll need an iterative loop to insert the new node.

void tree::addnode(string str) { node *newnode,*ptr; //Creating a new node newnode=new node; newnode->data=str; newnode->left=NULL; newnode->right=NULL; if (root==NULL) //Determining the position to insert the new node

556

Data Structures

Page 558: C++ Basics to Advanced

{ root=newnode; } else { ptr=root; while (ptr!=NULL) { if (newnode->data < ptr->data) { if (ptr->left==NULL) { ptr->left=newnode; ptr=NULL; } else { ptr=ptr->left; } } else { if (ptr->right==NULL) { ptr->right=newnode; ptr=NULL; } else { ptr=ptr->right; } } } } }

The code might seem complicated but it’s actually easy if you’ve understood the algorithm for inserting a new node.

Remember: Inserting a new node means that all we need to do is make the pointers point to the correct addresses.

The destructor

In our destructor we pass the root pointer as argument to the destroy_tree( ) function (because we want to destroy all the nodes from the root onwards). The algorithm to destroy a tree is as follows:

557

Data Structures

Page 559: C++ Basics to Advanced

• We cannot destroy from top to bottom (i.e. you can’t first delete the root node, then the children and so on).

• Check if the root node has a left child. If it does then destroy that left child. Before destroying the left child we need to check whether that node has a left child of its own. If it doesn’t then check whether it has a right child. If it doesn’t then we can delete the node else we need to go right down to the bottom of the tree to locate the node with no children. From that node onwards we have to delete and approach the top of the tree.

The algorithm may seem a little complex to implement at first sight. But if you analyze the procedure carefully then you’ll find a recurring pattern. Recursion would help us to solve this problem easily. The code will be:

void tree::destroy_tree(node *delnode) { if (delnode->left!=NULL) { destroy_tree(delnode->left); }

if (delnode->right!=NULL) { destroy_tree(delnode->right); } cout<<endl<<"Deleting the node: "<<delnode->data; delete delnode; }

We just keep on recursively calling the same destroy function. Take an example and think like the compiler to understand the logic.

The main( ) function:

int main( ) { tree names; names.addnode("michael"); names.addnode("srinath"); return 0; }

The output will be: Deleting the node: srinath Deleting the node: michael

As expected, the child node (srinath) is deleted first before the root node is deleted.

Remember: The tree structure is decided based on the way we store values in the tree. In the above example, if srinath were inserted first into the tree then michael would have been inserted to the left of srinath.

558

Data Structures

Page 560: C++ Basics to Advanced

Analysis of Algorithms - Part I

The following sections are meant to just provide an introduction to algorithm analysis. We won’t delve deep into various algorithms.

Searching and sorting:

Let’s say we have ten cards lying face down on a table in a single row. Each card has a number between 1 and 100.

If we have to locate the card with number 85, how do you think we would go about the task?

We also know beforehand that the cards are not in sorted order; any number could be present anywhere and the cards have been randomly placed. Because the cards are unsorted we have only one way of searching: pick up each card in the row till we locate number 85.

In computers also, many situations arise where we need to make use of some algorithm for searching through large quantities of data. The simplest form of searching is linear searching (which we have just seen).

An algorithm is a defined sequence of steps for completing a task. Every problem can be solved using different algorithms. Searching data is a problem and linear searching is one algorithm that helps us solve this problem. But not all algorithms are efficient; each one might have its pros and cons. For example in linear searching, if the card we need is at the end of the row then we would have checked all the cards in the row before locating our card. Or suppose the card we are searching for isn’t present in the given set we would have wasted a lot of time in lifting each card. This is the worst case scenario. The best case scenario occurs when the card we require is the first card in the set. Thus in the worst case; if we have ‘n’ cards then we’ll need to make ‘n’ guesses to locate our card. In the best case scenario, we’ll need to make just 1 pick to locate our card.

Note: When we write a computer program we actually implement an algorithm (and before writing any program it is advisable to draft out the algorithm first so that you are clear while coding). For example a program to add two numbers would have the following algorithm:

559

Algorithms - I

Page 561: C++ Basics to Advanced

1.) Obtain the two input numbers.

2.) Perform validations to ensure the sum doesn’t exceed the limits.

3.) Add the two numbers.

4.) Display the result.

In a general sense, algorithm means a sequence of well defined steps. In this section we’ll deal with algorithms that are used to solve particular problems (like searching through data- to achieve this also we require a series of properly defined steps).

Remember: Algorithms are not specific to the type of input data. The linear searching algorithm can be applied while searching for integers, strings, characters, cards (or user defined objects) etc.

A few problems which require algorithms are:

1.) Searching through data.

2.) Sorting of data.

3.) Finding the optimal way to traverse through many places.

4.) Routers connect computers on the internet. They forward data and there are numerous routes possible between two points on the internet. Routers need an algorithm to decide which path to take.

5.) Many scientific problems are based on a set of mathematical equations. Algorithms are required for obtaining the optimal solution for those equations.

Each of these problems has a set of different ways that can be used for solving them (a set of different algorithms are available). But we need some means to measure the performance of an algorithm so that they can be compared. Two parameters that can be used for comparison are space and time.

• Space- the amount of memory that the program (algorithm) will require.

• Time- the execution time of the program (compile time is not included assuming that programs will not be compiled over and over again).

These parameters will depend on the physical hardware; for example an 800Mhz Pentium IV processor will execute a program faster than a 386 machine. The available memory in a computer will also vary from machine to machine. Some machines might have a maximum of 1GB RAM while some might have only 16MB of RAM. But no matter what hardware we use, we would prefer using an algorithm that is efficient and that doesn’t consume unnecessary space. For example: if you had the choice of buying

560

Algorithms - I

Page 562: C++ Basics to Advanced

2 robot dogs; one which is quick and requires only a single 12 Volts battery while the other robot needs two 12 Volts batteries to provide the same performance, which one would you choose? Even if you had a stock of ten batteries you would still prefer the first one; you get to save some batteries which you can use for some other purpose.

Similar is the case with algorithms; even though we might have a more powerful computer we wouldn’t want to use an inefficient algorithm.

Algorithms almost always depend on the size of the input data set. Searching through a stack of 1000 cards is going to consume more time than searching through a stack of 10 cards. The best case scenarios might consume the same time (but we can’t say that an algorithm is great just because in the best case scenario we need very little time).

It would be fair to consider the performance of an algorithm as the input size varies. But we have different scenarios to consider for every algorithm:

1.) Worst case 2.) Best case 3.) Average case

We’ll take up the worst-case scenario (since usually we are interested to know an algorithm’s worst case performance). To measure this, the big-Oh notation is used: denoted as O.

Let’s take some examples to understand big-Oh.

Constant Time:

There are certain instances where the size of the input doesn’t affect the algorithm. Let’s say that we are opening a new store today. We also have a special offer; the first person to come to the shop will get a gift voucher. In this case, irrespective of whether 10 people turn up or 1000 people turn up, we are just going to pick the first person to set foot in our shop. Only the first person gets the voucher. This is the case of a constant-time algorithm; no matter what the size of the input we’ll require the same time to choose the first person. The worst case notation is O(1); i.e. the algorithm depends on a constant (if it depended on the number of inputs then we would have used ‘n’ instead of 1).

Linear-time:

Our linear search algorithm was clearly dependent on the size of the input. If we have 10 cards, then the worst case would be to make 10 picks; if we had 1000 cards, then we would need to pick 1000 times in the worst case. Thus this algorithm depends directly on the size of the input. If ‘n’ is the input then the big-Oh notation is: O(n), i.e. the linear search algorithm depends directly on the size of the input. These are linear-time algorithms because greater the value of ‘n’, greater is the time required.

If n=1 and our algorithm takes 1 minute; then n=10 will require 10 minutes and if n=100 then the algorithm will need 100 minutes (linear dependency).

561

Algorithms - I

Page 563: C++ Basics to Advanced

Binary Search algorithm

Now getting back to our problem of locating the card; how do you think the search can be made more effective? What if the input were in sorted order?

Now the problem is simpler:

Round 1:

Pick the card in the middle; let’s say 60.

Check if the required number (say 85) is greater or less than this.

If the required number is greater then ignore the cards below 60. The remaining set would have 60, 79, 85, 86 and 99.

Round 2:

Again pick the card in the middle; this time it is 85. Since this is what we need, we can stop the search.

If the required card number were less than 60, then we would’ve ignored the higher value cards and restricted our search to 01, 08, 35, 40, 50 and 60.

The idea is simple: Take the middle card in the set; compare with the required value. If this is our required value then quit searching. Else depending on the required value reduce the search set into half. Again repeat the process (of taking the middle card etc.) within this smaller set. If you’ve noticed after each round we’ll effectively reduce our search area by two. From the initial 10 cards, we’ll come down to 5; then we’ll come down to 3 and so on, after each round.

This search algorithm is called binary searching. In the best case scenario, our card will be the middle card in the search space. In the worst case scenario what do you think will happen?

In linear search algorithm our time depended directly on the value of ‘n’. Clearly in binary search the worst case scenario is better than that of linear-searching. In 10 cards, we needn’t make 10 picks to arrive at the solution; instead a maximum of 4 would be

562

Algorithms - I

Page 564: C++ Basics to Advanced

sufficient. The maximum number of times you can divide the search space is log2n (which equals= log10n/log102)

Analysis of Algorithms - Part II

The pictures on this page might take some time to load (I couldn't break them up into smaller pieces).

Big-oh computation in programs:

A programmer can easily determine the order of a code (or calculate the big-oh order for a particular code). Let’s consider some examples:

1.) Constant time:

int n=5; cout<<(n+5);

For such normal statements we consider them as being O(1) because they don’t depend on the input. A statement would depend on the input only if we are using some form a loop.

A question arises: “If we have hundred such normal statements what is the order?”

The order is still denoted as O(1), because it is still constant-time and doesn’t depend on the size of the input.

563

Algorithms - II

Page 565: C++ Basics to Advanced

2.) Linear time:

for(int i=0; i<n; i++) { if (array[i] == required_value) { cout<<endl<< “Required value present at position:”<<i; break; } }

This is a simple code snippet implementing the linear search algorithm. The loop would execute a maximum of ‘n’ times (worst case scenario). The number of iterations depends on the size of the input (i.e. on the value of ‘n’).

This code has an order: O(n).

3.) Combination of loops and normal statements:

When there are a few normal statements (constant time) and a loop which depends on ‘n’, then we consider the worst running time. Between O(1) and O(n), we choose O(n) as the order for that program.

int n=5; cout<<(n+5); for(int i=0; i<n; i++) { if (array[i] == required_value) { cout<<endl<< “Required value present at position:”<<i; break; } }

Overall big-Oh notation for this code: O(n).

Remember: Big-oh notation represents the worst-case scenario.

564

Algorithms - II

Page 566: C++ Basics to Advanced

Sorting

Binary search seems to be more effective than linear searching; but we have a problem. For binary search to work the input should be in sorted order. It is a prerequisite. This brings us to the next problem; that of sorting data. There are many ways of sorting data and we’ll take a look at some of the commonly used sorting algorithms.

A very simple way to sort is to just keep comparing neighbouring two elements and then swap them in case the first element is larger than the second (for descending order it’ll be the reverse but we’ll assume that we want to sort a given set of elements in ascending order).

Let’s assume that we have 5 elements 5, 4, 3, 2 and 1. The idea is to arrange them in ascending order.

1.) Compare 5 and 4. Since 5 is greater than 4 swap their positions. The list would now be: 4 5 3 2 1.

2.) Compare 5 and 3. Sine 5 is greater than 3 swap their positions. The list becomes: 4 3 5 2 1.

3.) Next compare 5 and 2. List is: 4 3 2 5 1.

4.) Finally compare 5 and 1. List is: 4 3 2 1 5.

With this one round of sorting is complete. For the next round we repeat the same process but this time the element chosen will be 4.

After four rounds, the elements will be in sorted ascending order.

565

Algorithms - II

Page 567: C++ Basics to Advanced

As can be seen from the figures, we’ll need a total of (n-1) main sorting rounds. And within each main round, we have (n-1) little rounds. If implemented in a program, we would need to use two loops to implement this algorithm.

566

Algorithms - II

Page 568: C++ Basics to Advanced

You might have noticed from the above figures that the smaller values actually bubble their way to the top. This algorithm for sorting is called bubble sorting.

Improvements:

You might have also noticed that after each main sort round, we can reduce the number of little rounds by one. Just take a look at the earlier figures and you’ll get the idea.

• In ROUND 1, we have 4 inner rounds - the position of the largest element is fixed.

• In ROUND 2, we only need 3 inner rounds- the position of the largest two elements is fixed.

• In ROUND 3, we only need 2 inner rounds.

• In ROUND 4, we only need 1 inner round.

If the input happens to be in sorted order (say after the second round), then we needn’t perform the other rounds. If it’s already sorted why should we continue with the algorithm? To implement this, we can use a variable which counts the number of swaps in each main round. If there are no swaps in a main round, then we can break out of the main sorting loop.

A few other sorting algorithms are selection sort, insertion sort and quick sort. Each one has its own advantages and disadvantages and exploring these algorithms is left as an exercise for the reader.

567

Algorithms - II

Page 569: C++ Basics to Advanced

Appendix

Working in Visual C++:

To start Visual C++ in Windows, go to START->PROGRAMS->Microsoft Visual Studio->Microsoft Visual C++. (The illustrations here are with respect to Microsoft VC++ 6.0 but the procedure is similar even in older versions as well as in .NET).

In the VC++ window, click on FILE->New.

568

Working in VC++

Page 570: C++ Basics to Advanced

After clicking on “new”, we will be presented with many options. We should first create a new project. In the projects tab choose the option of “Win32 console application”, provide the directory where the project should be created and provide a name for the project. In our example we’ve named the project as ‘hello’.

Click OK and on the next screen choose “an empty project”.

569

Working in VC++

Page 571: C++ Basics to Advanced

We choose an empty project because we don’t want to have any default files created by VC++ for our project.

570

Working in VC++

Page 572: C++ Basics to Advanced

Click OK and we’ll get the project window as shown below.

571

Working in VC++

Page 573: C++ Basics to Advanced

572

Working in VC++

Page 574: C++ Basics to Advanced

Now we have a project named “hello” but it is empty (i.e. it doesn’t have any source files within it). Next we need to create a new C++ source file (this should be part of the project). To create a source file, go to FILE->New option. Go to the Files tab and choose C++ source file. Give the name of the source file and also ensure that the option “Add to project” is checked. Every source file should be part of a project in VC++ for compiling and executing. In older versions of VC++, on creating a new source file, the file wouldn’t be added to the open project (i.e. the option “add to project” wouldn’t be present on the screen). In this case we need to go to the project options and then check the option for including the source file in the project.

573

Working in VC++

Page 575: C++ Basics to Advanced

Type the code in the editor window and save the source file as below.

To compile the code, go to the option BUILD->Compile first.cpp.

The result of compilation will appear in the window at the bottom of the screen. There should be no errors to continue further.

574

Working in VC++

Page 576: C++ Basics to Advanced

Next go to BUILD->build hello.exe.

575

Working in VC++

Page 577: C++ Basics to Advanced

This invokes the linker which will create the executable file called hello.exe. Again the messages produced by the linker appear in the window below the editor.

576

Working in VC++

Page 578: C++ Basics to Advanced

If linking is successful then we can run the code through VC++ itself. Just go to BUILD->Execute hello.exe option.

577

Working in VC++

Page 579: C++ Basics to Advanced

In older VC++ versions when creating a new C++ source file, it may not get included into the current project. To include a file into the project, go to PROJECT->Project Settings and insert the file into the project. If you want to exclude a file from the project, then in project settings check the option “Exclude file from build” (shown in figure below).

578

Working in VC++

Page 580: C++ Basics to Advanced

Assembly listing:

If you are curious to see the assembly level code, in the project settings click on the tab titled C/C++. Choose the category “listing files” and choose what output listing you would like to see (there are choices like: machine code, assembly code, assembly code with the source code etc). Now when the file is compiled you will find another file containing the type of listing you chose.

579

Working in VC++