Вы находитесь на странице: 1из 580

Conquering C++

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

Contents - PART I
Basic C++

1.) Introduction 6.) More Data Types

• a.) Evolution of Programming Languages • Scope of variables, Storage Classes


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

• Loops (Iterations) • Object Oriented Programming


o For loop and its variations Concept and OOP Languages
o While, Do-while loop • Class with example
• Decision statement • Constructors, Constructor with
o If...elseif..., Nested If, Parameters, Overloaded
Conditional Operator Constructors, Default Constructors
o Switch Case and Arrays of Objects
• Loop Control Statements • Scope Resolution Operator,
o Break, Continue, Goto, Destructor, Objects and Functions
Return (passing and returning objects),
Initialializing an object using an
object
• A Practical Use of Classes
5.) Functions • Data Encapsulation...Who are we
hiding data from?
• Declaration, call and definition, • More objects and classes, Private
Parameters and arguments member functions (helper functions)
• Default Arguments, Returning • Friend Function and Friend Classes,
values from a function, Returning Static Class Members, Constant
void, The int main( ) function Objects (and the 'mutable' qualifier)
• An illustration of int main( ) • Copy Constructor, this pointer,
• Types of functions, Using Library pointer to objects, objects in
functions (rand, srand and time), memory
Function overloading, An example • Initializer list, explicit keyword,
using functions declaration and definition, return
• Recursion (calling oneself!), Inline value optimization
Functions

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)
3

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.

Introduction
4
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

Introduction
5
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

Introduction
6
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:

Introduction
7
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).

Introduction
8

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.

Introduction
9
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.

Introduction
10
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.

Introduction
11

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.

Introduction
12

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

Introduction
13

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.

Introduction
14

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:

Introduction
15

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.

Introduction
16

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).

Introduction
17

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

Introduction
18

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.

Introduction
19

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.

Introduction
20

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.

Introduction
21

• 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.

Introduction
22

• 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

Introduction
23

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’.

Introduction
24

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).

Introduction
25

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).

Introduction
26
Your very first C++ Program

All the example programs in this book have been tested in Turbo C++ compiler (the programs were also tested
in 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:

// 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;

Explanation for the above program:

z // 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.

z # 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.

z int main( ) : Every C++ program has to have one ‘main’ function. Functions will be explained later.
Data Types & Variables
Remember that the compiler will execute whatever comes within the ‘main’ function. Hence the 27
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.

z { : 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”.

z 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.

z 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.

z 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.

z 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).

z }: 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 assigned
to 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 define
the 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;
Data Types & Variables
Beware of the direction of the >> and << operators. In the statement

cout<<variable; 28

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. the
value 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.
Data Types & Variables
How to run your first program in the29
compiler?

Saving and compiling the program:

There are many C++ compilers available in the market. Some of them are freeware (meaning that
they are free to use) while others have to be paid for. Turbo C++ compiler might be the simplest to use (but it is
not freeware). Simply choose "New File" and type out the program coding. Suppose you are using some other
compiler, click on File and choose "New". Some compilers may have the option of creating a C++ source file
while other compilers may require a new project to be created. Whatever the method you will ultimately come
to the step of creating a C++ source file. After typing the code in the compiler, save the file by giving it some
name. The "Save As" option will appear under the "File" menu. Give a name (for example: first). Now the file
is saved as first.cpp. All C++ source files are saved in *.cpp format. Just like *.doc represents a Word document
file, *.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 been
typed) and in case there are any errors, the compiler will point out the line where the error was detected. Check
whether 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 the
next stage: building the *.exe file.

*.exe file extension stands for executable files. A *.cpp file cannot be run directly on the computer. This has to
be 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 itself
and check the output. In the compiler there will be another option called "Run". Just click on this and the
program will run from the compiler itself (you needn’t switch back and forth between DOS and the compiler
screen).

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

Data Types & Variables


30

The source code is the program that you type. Source code is converted into object code by the compiler. The
linker will link your object code with other object codes. This process of creating a C++ program is discussed in
the 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 in
Turbo 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 a
character. Once a character has been entered, the program will return to the compiler screen. You won't see your
output! It might appear as if there is some problem with the program. What happens is that the program displays
the character on the screen, immediately terminates the program and returns to the compiler screen. This
happens so fast that you can’t see the output being displayed.

Data Types & Variables


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

// 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;
}

Another header file called conio.h has been added. The function getch( ) is used at the end of the
program. 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 typed
will 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 letter
on the screen. The program will not quit at this point since there is a statement getch ( ). It will wait till the user
types 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 while
displaying 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;

Data Types & Variables


The program flow will be the same as described earlier.
32
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.

z system("PAUSE");

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

z system("CLS");

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

The following topics are covered in this section:

z Introduction

z Integer

z Floating Type

z Double

z Character

z Boolean

z Data Type Ranges and determining the ranges

z 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 be
of 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 decimal
point). Similarly, in programming we have what are called as data types. When a variable is declared, the programmer has to specify
which data type it belongs to. Only then will the compiler know how many bytes it should allocate for that particular variable. Or in
other words, each data type occupies a different memory size and if a variable is declared as belonging to one particular data type it
cannot 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:

z Integer
z Floating Point
z Character
z Double
z 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:

z 0
z 10
z 345
z 6789

Data Types & Variables


z -23 34
z -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:

z 3.5
z 4.8
z 0.23

These numbers come under the second category (floating point type) and not under integers. If the program has to accept such values
from 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, the
program will assign 2 as the value for that integer variable. Similarly, if the user enters 3.2, the program will assign 3 to the integer
variable.

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). From
this it is possible to calculate the maximum and minimum values that an integer can store. 2^16 = 65536 (hence 65536 different
combinations of 16 bits are possible). Divide this by 2 because integers (by default) range from negative to positive values. We have a
0 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 of
65536 different values).

A natural question springs to mind, "What would happen if a value greater than 32,767 is entered?" Since this value cannot be
accommodated 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 a
basic (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 most
significant 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 needs
positive and negative number values for the variable. By declaring a variable as an integer, by default you can specify both positive
and 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;

Data Types & Variables


35
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 the
power 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. Suppose
0.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:

z 0.1276
z 1.23
z 1.0
z 10.2

Data Types & Variables


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

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 can
have values up to 10^38 (in positive and negative direction). The same qualifiers used for an integer can be applied to floating point
numbers 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 variable
of 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 can
take numbers from 0 to 9 only. The following are valid characters:

z A
z B
z 3
z a
z :
z ‘
z /

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 it
encounters and will discard the rest). A character is stored in one byte (as a binary number). Thus whatever the user enters is
converted 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 by
IBM and used in IBM computers. However, ASCII remains the most widely used code in all computers. The table at the end of the
book 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)

Data Types & Variables


37
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.

#include <iostream.h>
int main( )
{
bool check;
check = true;
cout<<check; //output will be 1 (since ‘check’ is true)
return 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

Bytes
Name Description Range (depends on number of bytes)
(Compiler
dependent)
signed: -128 to 127
char 1 Character or 8 bit integer.
unsigned: 0 to 255
Boolean type. It takes only two true or false
bool 1
values.
signed: -32768 to 32767
short 2 16 bit integer.
unsigned: 0 to 65535
long 4 32 bit integer. signed:-2147483648 to 2147483647
Integer. Length depends on the size
of a ‘word’ used by the Operating
int 2/4 system. In MSDOS a word is 2 Depends on whether 2/4 bytes.
bytes and so an integer is also 2
bytes.
float 4 Floating point number. 3.4e + / - 38 (7 digits)
double precision floating point
double 8 1.7e + / - 308
number.

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.

Data Types & Variables


38
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 octal
number 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 in
binary 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. Binary
arithmetic 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. We
discussed 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.

Data Types & Variables


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

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 computer
isn’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 the
numbers 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 sign
bit.

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

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:

Data Types & Variables


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

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 on
digital 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.
Data Types & Variables

Go back to the Contents Page

Copyright © 2004 Sethu Subramanian All rights reserved.


Simple C++ Program 41

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 be
obtained as input from the user) and the constant ‘pi’ (whose value can be provided in the code itself).

// 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;
}

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 the
point of declaration, PI is initialized to a value of 3.14. This means that whenever PI is used in the program, the
compiler 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 screen
just 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 value
of ‘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

Data Types & Variables


Area of the circle is 254.14 42

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


Data Types & Variables
The problem is that in the program we haven’t assigned a value to the variable ‘correct’ and neither have we 43
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:

//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;
}

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 logical
errors) 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 an
integer 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 something
is stored as a character (though it is stored in integer format), it will go through the same process of ASCII
coding and find out what is the equivalent character (and display the character). But when something is stored
Data Types & Variables
as an integer, the number will be displayed as it is (without going through the ASCII coding process). 44

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).
Data Types & Variables
45

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.).

Data Types & Variables


46

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.

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

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.

Data Types & Variables


47

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

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):

Data Types & Variables


48
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

Data Types & Variables


49
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:

Data Types & Variables


50
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).

Data Types & Variables


51

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.

Operators - I
52

#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’.

Operators - I
53

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:

Operators - I
54

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.

Operators - I
55

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.

Operators
56

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;

Operators
57

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.

Operators
58

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

Operators
59

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

Operators
60

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

Operators
61

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).

Operators
62

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.

Operators
63

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:

Operators
64

// 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…
}

Operators
65

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;
}

Operators
66

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;

Operators
67

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).

Operators
68

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: ? :

Operators
69

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 *=, /= , %= , += , -=

Operators
70

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’).

Operators
71

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.

Operators
72

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.

Operators
73

The basics (chapters 1 to 3)

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


2.Q) The following coding:
int main( )
{ a=3;
int a=3,b; int c = ++a;
b=a++ + a++; int d = ++a;
cout<<endl<<"b value = "<<b; int b = c + d;
a = 3; a=3;
b=++a + ++a; b = ++a + ++a;
cout<<endl<<"b value = "<<b;
return 0; 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;
4.Q) ‘cout’ and ‘cin’ are keywords?
int j;
i=i+1;
j=i;
cout<<j;
5a.) For each of the questions below
assume:

int i = 2;
int j = 5; 5b.)
int k;
k= 2++;
Find out the value of ‘k’ (not all will have
values because some are illegal statements).

k= (j++);
5c.) 5d.)

k= i++ + ++j; k = i+++++j;

5e.) 5f.)

k= i = (j = 1); 1=2;
74

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'.
75

-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;
76

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.
77

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.
78

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!!!


79
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


80
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
81
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
82
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.
83
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.
84

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

Program Control Flow


85

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).

Program Control Flow


86

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)?

Program Control Flow


87

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 < =

Program Control Flow


88

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;
}

Program Control Flow


89

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;
}

Program Control Flow


90

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 ;

Program Control Flow


91

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’.

Program Control Flow


92

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.

Program Control Flow


93

//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;
}

Program Control Flow


94
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');

Program Control Flow


95

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.

Program Control Flow


96

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

Program Control Flow


97

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'";
}

Program Control Flow


98

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

Program Control Flow


99

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;

Program Control Flow


100

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!

Program Control Flow


101

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
}
}

Program Control Flow


102
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

Program Control Flow


103
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.

Program Control Flow


104

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;
}

Program Control Flow


105

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:

Program Control Flow


106

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:

Program Control Flow


107

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.

Program Control Flow


108

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:

Program Control Flow


109

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.

Program Control Flow


110

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.

Program Control Flow


111

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.

Functions
112

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.

Functions
113

// 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;

Functions
114

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;
}

Functions
115

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 :

Functions
116

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.

Functions
117

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

Functions
118

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;

Functions
119

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;

Functions
120

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).

Functions
121

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.

Functions
122

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).

Functions
123

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

Functions
124

}
}
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.

Functions
125

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.

Functions
126
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

Functions
127
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:

Functions
128
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.

Functions
129

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.

Functions
130
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

Functions
131
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.

Functions
132
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

Functions
133
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

Functions
134
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( )
{

Functions
135
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.

Functions
136

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;
}

Functions
137
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.

Functions
138
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:

Functions
139
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.

Functions
140
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:

Functions
141
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:

Functions
142
// 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.

Functions
Functions 143

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.
144

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.

More on data types & variables


145
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;
}

More on data types & variables


146
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;
}

More on data types & variables


147
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.

More on data types & variables


148
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.

More on data types & variables


149
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".

More on data types & variables


150
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.

More on data types & variables


151

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.

More on data types & variables


152
#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;

More on data types & variables


153
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.

More on data types & variables


154
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.

More on data types & variables


155
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.

More on data types & variables


More on data types & variables 156

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 157

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.
158
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.

More on data types & variables


159

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:

More on data types & variables


160
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

More on data types & variables


161
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.

More on data types & variables


162
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’.

More on data types & variables


163
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).

More on data types & variables


164

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:

More on data types & variables


165
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};

More on data types & variables


166
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).

More on data types & variables


167
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},

More on data types & variables


168
{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] )

More on data types & variables


169
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 ];

More on data types & variables


170
}
}
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.

More on data types & variables


171

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.

More on data types & variables


172
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;
};

More on data types & variables


173
// 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.

More on data types & variables


174
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.

More on data types & variables


175
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;
}

More on data types & variables


176
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:

More on data types & variables


More on data types & variables 177

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.
178

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

More on data types & variables


179
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.

More on data types & variables


180
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( )
{

More on data types & variables


181
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:

More on data types & variables


182

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)


{

More on data types & variables


183
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.

More on data types & variables


184

Chapters 4 to 6
1.Q) If an array is initialized as shown Answer.)
below:
A.) Array elements will be initialized to 0.
int ar[5]={1,2,3}; Hence ar[3] and ar[4] will be 0.
What will be the value for ar[3]?
Answer.)
2. Q.) int ar[5]; A.) All the array elements will have
What will be the value for ar[3]? unpredictable values (commonly called
garbage values).
3. Q.) Will the following code work (if it Answer.)
will, what's the output)?:
A.) i[ar] is like writing 1[ar], 2[ar] etc. This
int ar[5]={1,2,3}; is legal and i[ar] is equivalent to ar[i]. Thus
for (int i=0;i<5;i++) the output will be: 12300
{
cout<<i[ar]; 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.
Answer.)

A.) This might seem really confusing. First


of all just rewrite the coding as shown
below:

struct st1
{
struct
4. Q.) In the below structure how can we
{
access ‘x’?
struct
{
struct st1 {struct{struct{int x;} st2;}
int x;
st3;}y;
} 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

Q&A - Chapter 4-6


185

5. Q.) Can you predict what will happen?

# include <iostream.h>
int main( )
{ Answer.)
unsigned int j=2;
cout<<j--; A.) The program will run forever because
while(j>=0) ‘j’ is an unsigned integer and thus it will
{ never become negative.
cout<<j--;
}
return 0;
}

Answer.)
6. Q.) Can main( ) be recursive?
A.) C++ doesn’t permit the program to call
the main ( ) function.

7. Q.) What will happen?

int main( )
Answer.)
{
A.) You will get a compilation error
int array1[]={1,2,3};
because you cannot assign arrays using the
int array2[]={10,20,30};
= operator.
array2=array1;
return 0;
}
8. Q.) What is the result of the code given
below:

static int x=2;

int main( )
{ Answer.)
int sum=0; A.) A run-time error (divide by zero error).
do
{
sum+=(1/x);
}while(0<x--);
return 0;
}

Q&A - Chapter 4-6


186

Answer.)

Answer is 4. Conditional operator has


associativity from right to left. Thus start
9. Q.) What would be the output for the
simplifying from the rightmost side of the
following code:
expression. The conversion would be as
below:
int main( )
{
j>k ? 2 : i>j ? 3 : 4;
int i=5; int j=6; int k=7;
int result;
For the part i>j ? 3 : 4; the result will be
result = j>k ? 2 : i>j ? 3 : 4;
4. Now plug this into the original
cout<<result;
problem and you will get:
return 0;
}
j>k ? 2 : 4;

The result of this is 4 (because j is not


greater than k).
Answer.)

In some other programming languages, this


notation is used to access elements of a
multi dimensional array. But in C++ the
10. Q.) What does the code fragment
expression within the square bracket is
yield:
evaluated as:
const int SIZE = 5;
2,3
int array1[SIZE]={1,2,3,4,5};
cout<<array1[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;
Answer.)
cout<<"\nEnter the radius:";
cin>>rad;
A.) Yes. In C++ we are allowed to declare
if(rad>0)
variables anywhere in the code (it is not
{
mandatory that all variables have to be
double
declared in the beginning).
area=3.14*3.14*rad;
cout<<area;
}
return 0;
}

Q&A - Chapter 4-6


Q&A - Chapter 4-6 187

12. Q.) Explain the following code


snippet:

switch(choice) A.) This is an illustration of a fall-through


{ switch case. If the value of choice is ‘y’ or
case 'y': ‘Y’, the same set of statements will be
case 'Y': executed:
cout<<"\nYou
entered yes"; cout<<"\nYou entered yes";
break; break;

case 'n': The programmer needn’t code separately for


case 'N': case ‘y’ and case ‘Y’ since in either case the
cout<<"\nYou entered no"; same set of statements have to be executed.
break;
default:
cout<<"\nWrong choice";
break;
}
187

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):

*
***
*****
*******
*********
***********
188
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
232
34543
4567654
567898765
67890109876

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:
189
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.
190
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).
191

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 ‘&’.

Pointers
192
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;

Pointers
193
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.

Pointers
194
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.

Pointers
195
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

Pointers
196
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;

Pointers
197
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;


Pointers
Use whichever method you are comfortable with.
198

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:

Pointers
199
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.

Pointers
200
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?

Pointers
201
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;

Pointers
202
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.

Pointers
203
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).

Pointers
204
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).

Pointers
205

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;
}

Pointers
206
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);

Pointers
207
}

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’.

Pointers
208
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.

Pointers
209
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:

55
66
77

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).

Pointers
210
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;
}

Pointers
211

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).

Pointers
212
• 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) void square (int &x )
{ {
*x = (*x) * (*x); x = x * x;
}
}

The code is clean and reader-friendly when passing by reference.

Pointers
213
• 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’).

Pointers
214
• 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.

Pointers
215

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;

Pointers
216
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.

Pointers
217
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

Pointers
218
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.

Pointers
219
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:

Pointers
220
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.

Pointers
221

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);

Pointers
222
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;
}

Pointers
223
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?

Pointers
224
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
pointer to a function that accepts pointer to character
int (*p)(char *str);
as argument and returns an integer.
p is a function that accepts pointer to character as
int *p(char *str);
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
pointer to function that accepts pointer to character
void (*p)(char (*str)[] );
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.

Pointers
225

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;
}

Pointers
226
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] ).

Pointers
227
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’)

Pointers
228
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).

Pointers
229
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;

Pointers
230
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).

Pointers
231

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;

Pointers
232
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.

Pointers
233
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.

Pointers
234
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)

Pointers
235
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.

Pointers
236
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?

Pointers
237

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]

Pointers
238
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.

Pointers
239

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

Classes & OOP


240
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.

Classes & OOP


241
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’.

Classes & OOP


242
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

Classes & OOP


243
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.

Classes & OOP


244
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).

Classes & OOP


245
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.

Classes & OOP


246

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;
}

};

Classes & OOP


247
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’.


{

As part of data hiding, we’ve put the member data


private:
(count) within the private section of the class. Only
int count;
the public functions of the class can access ‘count’.

public:
The keyword public indicates that everything
void reset( )
following this label is part of the public section of the
{
class.
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
void initialize( ) { cout<<"timer!"; count=0; }
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’).

Classes & OOP


248

void display( ) { cout<<"remaining:"<<count; } We’ve defined 2 more functions display ( ) and


increment ( ) which also access the member data
void increment( ) { count=count+100; } ‘count’.

}; Signals the end of our class declaration.

Next comes the main( ) function in which we’ve


created two objects. ‘t1’ is an object or an instance of
int main( )
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
Timer t1,t2;
create variables from the fundamental data types (ex:
int x, y; creates two integer variables).

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
t1.display( );
value of the t1’s variable ‘count’. Since it hasn’t been
initialized, you’ll find a garbage value on your
screen.

t1.initialize( );
Set member data count to 0.
t1.display( );

We’ve incremented the value of count from 0 by 100.


t1.increment( );
Thus the last display would yield: Seconds
t1.display( );
remaining:100

t2.initialize( );
These will all act on the ‘count’ of object t2. Thus at
t2.increment( );
the end of our program: count (t1) will be 100 count
t2.increment( );
(t2) will be 200
t2.display( );

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.

Classes & OOP


249
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.

Classes & OOP


250
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( ).

Classes & OOP


251
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).

Classes & OOP


252

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.

Classes & OOP


253
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.

Classes & OOP


254
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.

Classes & OOP


255
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.

Classes & OOP


256
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;
}

Classes & OOP


257
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;
};

Classes & OOP


258
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.

Classes & OOP


259

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;
}

Classes & OOP


260
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.

Classes & OOP


261
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
{

Classes & OOP


262
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;
}

Classes & OOP


263
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;
}

Classes & OOP


264
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";
}
};

Classes & OOP


265
//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.

Classes & OOP


266

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
100
be as below: Maxi
limit

Solution
50 Space
Y-
axis

50 100
0 X axis

Classes & OOP


267
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).

Classes & OOP


268
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.

Classes & OOP


269
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( );
};

Classes & OOP


270
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];

Classes & OOP


271
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;
}

Classes & OOP


272
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).

Classes & OOP


273
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).

Classes & OOP


274

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

Classes & OOP


275
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.

Classes & OOP


276

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

Classes & OOP


277
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.

Classes & OOP


278
// 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;
}
}

Classes & OOP


279
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.

Classes & OOP


280

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;
}

Classes & OOP


281
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( )

Classes & OOP


282
{
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;

Classes & OOP


283
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);
}

};

Classes & OOP


284
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;
}

};

Classes & OOP


285
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.

Classes & OOP


286
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.

Classes & OOP


287
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.

Classes & OOP


288
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.

Classes & OOP


289

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).

Classes & OOP


290
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.

Classes & OOP


291

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.

Classes & OOP


292
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";

Classes & OOP


293
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

Classes & OOP


294
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

Classes & OOP


295
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( )

Classes & OOP


296
{
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.

Classes & OOP


297
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:

Classes & OOP


298
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.

Classes & OOP


299
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).

Classes & OOP


300

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
class base
{
{
private:
private:
int val;
int val;
public:
public:
base(int x=0)
base(int x=0)
{
{
val=x;
val=x;
cout<<endl<<"Constructor;value:"<<val;
cout<<endl<<"Constructor;value:"<<val;
}
}
~base( )
~base( )
{
{
cout<<endl<<"Destructor; value:"<<val;
cout<<endl<<"Destructor;value:"<<val;
}
}
base ret_base( )
base ret_base( )
{
{
base btemp(val+1);
return base(val+1); //unnamed object
return btemp;
}
}
base(const base &rc)
base(const base &rc)
{
{
cout<<endl<<"Copy constructor"; val=rc.val;
cout<<endl<<"Copy constructor"; val=rc.val;
}
}
};
};
int main( )
int main( )
{
{
base b1(5);
base b1(5);
b1.ret_base( );
b1.ret_base( );
return 0;
return 0;
}
}

Classes & OOP


301

OUTPUT:
OUTPUT:
Constructor; value:5
Constructor; value:6 Constructor; value:5
Copy constructor Constructor; value:6
Destructor; value:6 Destructor; value:6
Destructor; value:6 Destructor; value:5
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;
}

Classes & OOP


302
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

Classes & OOP


303
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.

Classes & OOP


304
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.

Classes & OOP


305
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)
{}

Classes & OOP


306
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.

Classes & OOP


307
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.

Classes & OOP


308
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.

Classes & OOP


309

Chapters 7 and 8 (Classes and Pointers)


Workshop Quiz

Q.) Predict the output: Answer.) 7 9 11 13 15

void change( int *b, int n) A.) I don’t think this should pose much of a
{
problem. We are passing the address of the first
int i;
for( i = 0; i < n; i++) element of an array to the function (through a
{ pointer). Within the change ( ) function, we
*(b+i) = *(b+i) + 5; modify each element by adding 5 to it. Hence
} the output will be:
}
7 9 11 13 15
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;
}
2. Q.) What will the program below Answer.) Nothing
do?
On seeing a temporary variable and assignment
void func(int *x, int *y) statement, people jump to the conclusion that
{
the above program will swap the values of two
int *t;
t = x; integers. But this is WRONG. The program
x = y; actually does nothing.
y = t;
} void func(int *x, int *y)
{
int main( ) int *t;
{ t = x;
int a=2; x = y;
int b=3; y = t;
func(&a,&b); }
cout<<endl<<a<<" , "<<b;
return 0; 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.
310
3. Q.) What is the value of ‘sum’ in the Answer.) 25
program below:
There are a few important points to note in the
int getNewValue(int x) above program. First of all you should note that
{
static int div=1;
we can pass individual array elements to
return(x/++div); 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
int main( ) non-zero), the loop will keep repeating.
{ Thirdly, you should know about static variables
int a[ ]={12,24,45,0,6};
and also about the prefix ++ operator. The ‘for
int i;
int sum=0; loop’ will terminate for the case of a[3]
for(i = 0; a[i]; i++) because a[3] is 0. The value of sum will be:
{
sum+=getNewValue(a[i]); sum = (12/2) + (24/3) + (45/4) = 25
}
cout<<sum;
(because of integer division).
return 0;
}
Answer.) Returns the number of
characters in the string

The use of *str++ might seem


confusing. ++ has a higher precedence than *
4. Q.) What is the use of this function? (but it’s better to use parentheses to make it
clear- the parentheses have been dropped out
int len(char *str) here just to make the code appear more
{ complex! Some C++ tests might present similar
int length=0; code fragments). Postfix ++ will increment its
while(*str++!='\0')
{
operand only after the assignment has been
length++; done (i.e. it will occur only in the next
} statement). Thus when * (the dereferencing
return length; 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).
311
Answer.) Garbage values

A.) If you are expecting that the output will be


5. Q.) Can you predict what will happen? 6,7,8,9,10 then you are wrong. Why? ptr was
initialized to the address of ar[0]. But within
int main( )
{
the ‘for’ loop we have kept on incrementing
int const max=5; ‘ptr’. Each time the corresponding value at the
int ar[max]={1,2,3,4,5}; location is incremented by 5. Once the first
int *ptr=&ar[0]; loop is complete, ptr is now pointing to the
for (int i=0;i<max;i++) next location beyond ar[4]. Thus in the second
{
*ptr=ar[i] + 5;
loop we are actually displaying garbage values
ptr++; (instead of getting the display of 6,7,8,9,10). If
} the 2nd loop were:

for (int i=0;i<max;i++) for (i=0;i<max;i++)


{
cout<<endl<<ptr[i];
}
{
} cout<<endl<<ar[i];
}

then it would be fine.


Answer.) No

Without the statement


6. Q.) Will this compile? print(y);
void print(int &x)
{ the code might compile on some compilers.
cout<<endl<<"Reference"; But this statement will produce a compile-time
} error because the compiler wouldn’t be able to
decide which print( ) function to call. Some
void print(int x) compilers will flag an error saying “parameter
{ list of print(int) is not sufficiently different
cout<<endl<<"By value";
} from print(int &)”.

int main( ) And this is logically true since there is no way


{ in which the compiler will know which
int y=5; function to call.
print(y);
return 0;
}
312
7. Q.) Is there an error? What would be the
output?
Answer.) Output will be a garbage
class dummy value
{
private: The code will compile and execute even though
int data; there is no constructor explicitly defined for the
public:
int get_data( ) class dummy. The compiler will provide with a
{ default constructor. But some beginners are
return data; 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
int main( )
{ member data correctly, the class designer
dummy d1; should define a constructor and not rely on the
cout<<d1.get_data( ); implicitly created default constructor.
return 0;
}
Answer.) Compile Error

A.) The compiler will complain at the


statement:

increment(y)=2;

8. Q.) Will the following code compile? If it saying that increment(y) is not an L-value. The
will, what is the output? function increment( ) returns an integer which
we can use as an R-value, not an L-value. An
int increment(int &x) L-value is one which can be used on the left
{ side of an assignment expression. This in turn
x++;
return x; 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
int main ( ) code fragment, the statement:
{
int y=5; increment(y)=2;
increment(y)=2;
cout<<y;
return 0; 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.
313
Answer.) Output is 2.

A.) This will compile since we are returning a


reference (and we aren’t returning a reference
9. Q.) By changing the increment function
to a local variable; so the compiler won’t flag a
will it work?
warning). So how does this work and what is
int& increment(int &x) the output?
{
x++; ‘y’ is passed as reference to the function
return x; 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
int main ( )
{ x (which is equivalent to returning a reference
int y=5; to ‘y’). Thus the value 2 gets assigned to ‘y’
increment(y)=2; and the output will be 2.
cout<<y;
return 0; 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;
Answer.) 2
public: //multiple use of public
void modify( ) A.) The code will compile (we’ve used ‘public’
{
count=count+1; twice but this isn’t an error). The output of the
cout<<endl<<"The new count is : code will be 2 (i.e. count will be 2). Since
"<<count; ‘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.
int bacteria::count=5;

int main( )
{
bacteria b;
bacteria::count=1;
b.modify( );
return 0;
}
314

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?
315

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.
Conquering C++ - Part II/II

Contents - PART II
Advanced C++
9.) Operator Overloading 12.) Useful Classes and Functions

• Overloading a unary operator • Manipulators


(with return data type) • Buffering and Flushing Streams
• Overloading a binary operator • Formatting flags for manipulators
(with arguments) • Creating new manipulators
• Operator overloading restrictions (custom manipulators)
• Operator overloading through • Strings (string objects and
friend functions character arrays)
• Overloading a Unary Operator • Mathematical calculations
using Friend Function • Good programming practices
• Overloading the Assignment • Typedef
Operator (=) Returning Reference • Bitwise Operators
• Adding Objects with Built-in
(fundamental) data types
• Overloading ‘new’ and ‘delete’
operators 13.) Advanced Topics

• Preprocessor directives
• Namespaces and Headers
10.) Inheritance • Multiple file programs
• Extern (as a storage and linker
• Concept of Inheritance specifier)
• Access specifiers • Creating Libraries in C++
• Overloading and overriding • Exception Handling
• Virtual Functions • Templates
• Pure Virtual Functions and • Casting and cast operators
Abstract classes • An introduction to STL (Standard
• Can a Destructor be Virtual? Template Library)
• Pointers to base and derived
classes
• Multiple Inheritance (multiple
base classes) 14.) General Question and Answers
• Virtual Base Classes
• Relationships between classes-
object composition and private
inheritance
• More polymorphism (poly
polymorphism!)
11.) Files and Streams 15.) Data Structures

• Need for Streams • Linked List


• Overloading << and >> • Linked List using STL
• Predefined streams • Stacks
• Stream Status and status flags • Queues
• Reading the stream status • A closer look at STL
• Opening and Closing a file • Sequence Containers (Vectors)
• Writing and Reading a Text file • Subscripts and Vectors
• Accessing other devices (printer • More functions on Vectors
and monitor) through streams • Lists
• Structures in Files • Member functions in Lists
• Padding/Packing of structures • Deque
• Random Access of Files • Associative Containers
• Sequential Access and Random • Set Container
Access Files • MultiSet Container
• Command Line Arguments • Map Container
• Multimap Container
Workshop (for units 9-11) • Container Adaptors
• Algorithms
• Quiz-IV • Trees
• Bonus Questions-IV • Implementing a Binary Tree
(extra programs)

• An intro to analysis of
algorithms-I (searching and
sorting)
• Analysis of algorithms-II

• Appendix (Working in VC++)


318
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 (++)

Operator Overloading
319
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.

Operator Overloading
320

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’.

Operator Overloading
321
--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.

Operator Overloading
322
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;

Operator Overloading
323
}
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.

Operator Overloading
324
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.

Operator Overloading
325
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;
}

Operator Overloading
326
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)

Operator Overloading
327
{
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)
{}

Operator Overloading
328
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:

Operator Overloading
329
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;
}

Operator Overloading
330
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!

Operator Overloading
331
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;
}

Operator Overloading
332
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);

Operator Overloading
333
};
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:

Operator Overloading
334
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’
}

Operator Overloading
335
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

Operator Overloading
336
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.

Operator Overloading
337
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.

Inheritance
338
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’.

Inheritance
339

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;
}
};

Inheritance
340
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

Inheritance
341
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.

Inheritance
342
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";
}
};

Inheritance
343
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.

Inheritance
344
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;
}

Inheritance
345
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

Inheritance
346
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;
}

Inheritance
347
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".

Inheritance
348
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;
}

Inheritance
349
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++.

Inheritance
350
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;
}
};

Inheritance
351
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:

Inheritance
352
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.

Inheritance
353
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;
}};

Inheritance
354
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

Inheritance
355
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.

mainbase mainbase

base1 base2

derived

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.

Inheritance
356
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’.

Inheritance
357
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.

Inheritance
358
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”.

Inheritance
359
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.

Inheritance
360
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;
}

Inheritance
361
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.

Inheritance
362
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;

Inheritance
363
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

Inheritance
364
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 class door


{ {
protected: protected:
void shut( ) void shut( )
{ {
cout<<endl<<"Door closed"; cout<<endl<<"Door closed";
} }
}; };

class room class room:private door


{ {
private: public: void close( )
door d1; {

Inheritance
365
public: cout<<endl<<"Closing the room";
void close( ) door::shut( );
{ }
cout<<endl<<"Closing the room"; };
d1.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.

Inheritance
366
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.

Inheritance
367
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.

Inheritance
368
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 *);

Inheritance
369
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.

Inheritance
370
//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( )”.

Inheritance
371
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.

Inheritance
372
The class villager would now be:

class villager:public person Every villager created would have obj_type


{ as 1. Similarly in our archer class we would
public: code: obj_type=2 in the constructor. Thus
villager(int x=100) if at any point of time we want to check
{ whether an object is of type archer or
obj_type=1; villager, we will simply check the value of
life=x; obj_type.
}
void draw( )
{cout<<"\nDrawing villager:"<<life;}
};

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:

Inheritance
373
• 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( );

Inheritance
374
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).

Files & Streams - C++


375
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;
}

Files & Streams - C++


376
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;

Files & Streams - C++


377
}
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).

Files & Streams - C++


378
• 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).

Files & Streams - C++


379
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
{

Files & Streams - C++


380
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

Files & Streams - C++


381
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;
}

Files & Streams - C++


382
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

Files & Streams - C++


383
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

Files & Streams - C++


384
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.

Files & Streams - C++


385
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.

Files & Streams - C++


386
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:

Files & Streams - C++


387
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.

Files & Streams - C++


388
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).

Files & Streams - C++


389
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;

Files & Streams - C++


390
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.

Files & Streams - C++


391
//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.

Files & Streams - C++


392
// 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)

Files & Streams - C++


393
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++.

Files & Streams - C++


394
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];

Files & Streams - C++


395
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 : ajay@yahoo.com
Contents of file are :
ajay
ajay@yahoo.com

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)

Files & Streams - C++


396
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
}

Files & Streams - C++


397
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';

Files & Streams - C++


398
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

Files & Streams - C++


399
#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.

Files & Streams - C++


400
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;

Files & Streams - C++


401
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.

Files & Streams - C++


402
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).

Files & Streams - C++


403
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:

Files & Streams - C++


404

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).

Files & Streams - C++


405
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[ ] )

Files & Streams - C++


406
{
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.

Files & Streams - C++


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
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
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
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
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
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
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
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
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
416
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.

Useful Classes & Funtions


417
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).

Useful Classes & Funtions


418
# 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.

Useful Classes & Funtions


419
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;
}

Useful Classes & Funtions


420
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:

Useful Classes & Funtions


421
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;

Useful Classes & Funtions


422
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’.

Useful Classes & Funtions


423
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;
}

Useful Classes & Funtions


424
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

Useful Classes & Funtions


425
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):

Useful Classes & Funtions


426
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:

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)

Useful Classes & Funtions


427
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);

Useful Classes & Funtions


428
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!).

Useful Classes & Funtions


429

Useful Classes & Funtions


430
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).

Useful Classes & Funtions


431
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’

Useful Classes & Funtions


432
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 Purpose Syntax Similar Remarks


Name functions
Trignormetric calculate the double cos(double sin, tan, Remember to
functions (and cosine of a angle) cosh, sinh, give the angle in
hyperbolic) given angle tanh radians (and not
in degrees).
Radians are in
terms of the
constant ‘pi’ (one
radian equal 180
degrees).
Inverse returns the double acos(double asin, atan It is the opposite
trigonometric value of the value) of the cos, sin and
functions angle in tan functions.
radians Value should be
between –1 and 1
when using acos (
) or asin ( )
functions.
Hypotenuse Pass two double hypot For example: if
arguments, (double val1, we code:
this function double val2)
will find the cout<<hypot(3,4);
hypotenuse.
the output will be
5.
Square root Returns the double sqrt (double
square root val)
of the given
number.
Logarithm Functions to double log (double double log10(10) = 1
calculate val) log10
natural (double val) log(10) = 2.3
logarithm To calculate natural
and also base logarithm of ‘val’. To calculate
10 logarithm of
logarithms. ‘val’ to the
base 10.
Absolute value Two int abs (int val) double fabs fabs(2.1) = 2.1
functions to (double val)
obtain the The abs( ) function fabs(-2.1) = 2.1
absolute will operate on To retain the
value of a integers and return decimal

Useful Classes & Funtions


433
number. integers. places use abs (2.1) = 2.
Absolute the fabs( )
value means function. abs (-2.1) = 2
only positive
numbers. abs( ) will ignore
decimal places.
Raising to Raise a base double pow(double pow(2,3) is equal
power to the power base, double to 8.
of an exponent)
exponent. If
we say, 23,
then 2 is the
base and 3 is
the exponent.
Exponent This function double exp(double cout<<exp(1);
will raise the power)
natural The result will be:
logarithm 2.718
base ‘e’ to
the power of
the argument
provided.
The value of
e is 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.

Useful Classes & Funtions


434
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.

Useful Classes & Funtions


435
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;
}
}

Useful Classes & Funtions


436
• 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;
}

Useful Classes & Funtions


437
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.

Useful Classes & Funtions


438
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:

Useful Classes & Funtions


439
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

Useful Classes & Funtions


440
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

|10000000

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

1 1 0 0 0 0 0 0 Resultant value (192)

0 1 0 0 0 0 0 0 EXOR operation performed

^10000000

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

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)

Useful Classes & Funtions


441
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)

Useful Classes & Funtions


442
= 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);

Useful Classes & Funtions


443
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

Preprocessor Directives
444
#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;
}

Preprocessor Directives
445
#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.

Preprocessor Directives
446
#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).

Preprocessor Directives
447
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_ ).

Preprocessor Directives
448

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 Purpose


header

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 objects


string class).

Namespaces & Newheaders


449

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.

Namespaces & Newheaders


450

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

Namespaces & Newheaders


451

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).

Namespaces & Newheaders


452

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.

Namespaces & Newheaders


453

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;
}

Namespaces & Newheaders


454

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;
}

Namespaces & Newheaders


455

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).

Namespaces & Newheaders


456

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:

Creating Libraries
457
//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?

Creating Libraries
458
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...

Creating Libraries
459
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):

Creating Libraries
460
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:

Creating Libraries
461

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.

Creating Libraries
462

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

Creating Libraries
463

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.

Creating Libraries
464

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:

Creating Libraries
465

//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.

Creating Libraries
466

//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:

Creating Libraries
467

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:

Creating Libraries
468

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);

Creating Libraries
469

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

Creating Libraries
Creating Libraries 470

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
}
471

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;
}

Advanced C++ - IV
472

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);

Advanced C++ - IV
473

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

Advanced C++ - IV
474

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.

Advanced C++ - IV
475

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;

Advanced C++ - IV
476

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;

Advanced C++ - IV
477

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.

Advanced C++ - IV
478

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;
}

Advanced C++ - IV
479

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.

Advanced C++ - IV
480

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.

Advanced C++ - V
481

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";

Advanced C++ - V
482

}
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.

Advanced C++ - V
483

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.

Advanced C++ - V
484

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.

Advanced C++ - V
485

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’.

Advanced C++ - V
486

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.

Standard Template Library


487

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;
}

Standard Template Library


488

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:

1234

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:

14

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.

Standard Template Library


489

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).

FAQ
490
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:

FAQ
491
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


Instruction
terms of bytes)

0 Move 1 to location allotted for x (location 10)

Add 5 to the value stored at location allotted for x and store the
6
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

FAQ
492
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


Instruction
terms of bytes)

8000 Move 1 to location allotted for x (location 8010)

Add 5 to the value stored at location allotted for x and store the
8006
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.

FAQ
493
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.

FAQ
494
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

FAQ
495
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

FAQ
496
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

FAQ
497
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

FAQ
498
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.

FAQ
499

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.

FAQ
500
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.

FAQ
501

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

Data Structures
502
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).

Data Structures
503
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).

Data Structures
504
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

Data Structures
505
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.

Data Structures
506
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;

Data Structures
507
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;
}
}

Data Structures
508
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

Data Structures
509

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( );

Data Structures
510
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.

Data Structures
511

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.

Data Structures
512
//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

Data Structures
513
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:


Order in which Order in which elements
elements enter are serviced/removed

TOP
4
3
2
1

BOTTOM 0

Stack

Data Structures
514

FRONT
0
Order in which elements
1 are serviced/removed

2
3

BACK
4

Order in which
elements enter

Queue

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( );

Data Structures
515
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.

Data Structures
516
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.

Data Structures
517
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).

input output

forward

bidirectional

random access

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.

Data Structures
518
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)

Data Structures
519

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

Data Structures
520
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;
}

Data Structures
521
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;

Data Structures
522
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:
VECTOR 1 10 20

VECTOR 2
20

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

Data Structures
523

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;

Data Structures
524
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;

Data Structures
525
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);

Data Structures
526
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).

Data Structures
527

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( )

Data Structures
528
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);
}

Data Structures
529
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;
}

Data Structures
530
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.

Data Structures
531
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).

Data Structures
532
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

Data Structures
533
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).

Data Structures
534
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.

Data Structures
535

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;
}

Data Structures
536
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).

Data Structures
537

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

Data Structures
538
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

Data Structures
539

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:

1a
2b
3c
4d
5e

Data Structures
540
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

Data Structures
541

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:

Data Structures
542
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

Data Structures
543
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’.

Data Structures
544
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.

Data Structures
545
//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

Data Structures
546
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;
}

Data Structures
547
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:

43210

Data Structures
548
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);
}

Data Structures
549
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.

Data Structures
550
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.

Data Structures
551
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’.

Data Structures
552

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):

Data Structures
553

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

Data Structures
554
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

Data Structures
555
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);
}
};

Data Structures
556
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

Data Structures
557
{
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:

Data Structures
558
• 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.

Data Structures
559

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:

Algorithms - I
560

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

Algorithms - I
561

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).

Algorithms - I
562

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

Algorithms - I
563

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.

Algorithms - II
564

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.

Algorithms - II
565

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.

Algorithms - II
566

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.

Algorithms - II
567

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.

Algorithms - II
568

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.

Working in VC++
569

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”.

Working in VC++
570

We choose an empty project because we don’t want to have any default files created by VC++ for our project.

Working in VC++
571

Click OK and we’ll get the project window as shown below.

Working in VC++
572

Working in VC++
573

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.

Working in VC++
574

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.

Working in VC++
575

Next go to BUILD->build hello.exe.

Working in VC++
576

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.

Working in VC++
577

If linking is successful then we can run the code through VC++ itself. Just go to BUILD->Execute hello.exe option.

Working in VC++
578

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).

Working in VC++
579

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.

Working in VC++

Вам также может понравиться