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

Faculty of Computer Science and Engineering

Intensive Programming Lab

CS-102L
Faculty

Ghulam Ishaq Khan Institute of Engineering


Sciences and Technology, Topi.

CS102L: Intensive Programming Lab

Table of Contents

LAB 1 ............................................................................................................................................................. 3
Review of CS101L & Arrays (1-D and 2-D) .................................................................................................... 3
Lab 2: ........................................................................................................................................................... 13
Pointers in C++ ............................................................................................................................................ 13
Lab 3: ........................................................................................................................................................... 25
Strings in C++............................................................................................................................................... 25
Lab 4: ........................................................................................................................................................... 41
Structures I ................................................................................................................................................. 41
Lab 5: ........................................................................................................................................................... 48
Structures II ................................................................................................................................................. 48
Lab 6: ........................................................................................................................................................... 55
Object Orientation in C++ I ........................................................................................................................ 55
Lab 7: ........................................................................................................................................................... 64
Object Orientation in C++ II ....................................................................................................................... 64
Lab 8 ............................................................................................................................................................ 76
Friend Function & Classes, This Pointer, and static Variables .................................................................... 76
Lab 9 ............................................................................................................................................................ 85
Operator Overloading ................................................................................................................................. 85
Lab 10: ......................................................................................................................................................... 87
Inheritance .................................................................................................................................................. 88
Lab 11: ........................................................................................................................................................ 91
Introduction to Polymorphism and Abstract Base Classes ......................................................................... 92
Lab 12: ......................................................................................................................................................... 97
File Handling in C++..................................................................................................................................... 97

FCS&E, GIK Institute Topi, Pakistan

Page 2

CS102L: Intensive Programming Lab

LAB 1
Review of CS101L & Arrays (1-D and 2-D)

1-Dimensional Arrays:
Array basics
Let's start by looking at a single variable used to store a person's age.

Example 1.1:
1: #include <iostream.h>
2:
3: int main()
4: {
5:
short age;
6:
age=23;
7:
cout<<"The age is = <<age<<endl;
8:
return 0;
9: }
Not much to it. The variable age is created at line (5) as a short. A value
is assigned to age. Finally, age is printed to the screen.

Now let's keep track of 4 ages instead of just one. We could create 4 separate variables, but 4
separate variables have limited appeal. (If using 4 separate variables is appealing to you, then
consider keeping track of 93843 ages instead of just 4). Rather than using 4 separate variables,
we'll use an array.
Here's how to create an array and one way to initialize an array:
Example 1.2:
1: #include <iostream.h>
2:
3: int main()
4: {
5:
short age[4];
6:
age[0]=23;
7:
age[1]=34;
8:
age[2]=65;
9:
age[3]=74;
10:
return 0;
FCS&E, GIK Institute Topi, Pakistan

Page 3

CS102L: Intensive Programming Lab

11: }

On line (5), an array of 4 short's is created. Values are assigned to each variable in the array on
line (6) through line (9).

Accessing any single short variable, or element, in the array is straightforward. Simply provide
a number in square braces next to the name of the array. The number identifies which of the 4
elements in the array you want to access.
The program above shows that the first element of an array is accessed with the number 0 rather than
1. Later in the tutorial, We'll discuss why 0 is used to indicate the first element in the array.

Printing arrays
Our program is a bit unrevealing in that we never print the array to screen. Here is the same program
with an attempt to print the array to the screen:

Example 1.3:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:

#include <iostream.h>
int main()
{
short age[4];
age[0]=23;
age[1]=34;
age[2]=65;
age[3]=74;
cout<<"\n" <<age;
return 0;
}

Line (11) is meant to print the 4 ages to the screen. But instead of printing out the four short
variables, what appears to be nonsense prints out instead.
What the "nonsense" output actually is and why the 4 array values were not printed will be addressed
later in the tutorial. For now, the important point to come away with is that simply providing the name of
the array in an output statement will not print out the elements of the array.

How about printing out each of the values separately? Try this:
FCS&E, GIK Institute Topi, Pakistan

Page 4

CS102L: Intensive Programming Lab

Example 1.4:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:

#include <iostream.h>
int main()
{
short age[4];
age[0]=23;
age[1]=34;
age[2]=65;
age[3]=74;
cout<<"Age at
cout<<"Age at
cout<<"Age at
cout<<"Age at
return 0;
}

index[0]=
index[1]=
index[2]=
index[3]=

"<<age[0]<<endl;
"<<age[1]<<endl;
"<<age[2]<<endl;
"<<age[3]<<endl;

Lines (10) through line (13) produce the output we are expecting.
There is no single statement in the language that says "print an entire array to the screen". Each
element in the array must be printed to the screen individually.
Copying arrays
Suppose that after filling our 4 element array with values, we need to copy that array to another
array of 4 short's? Try this:
Example 1.5:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:

#include <iostream.h>
int main()
{
short age[4];
short same_age[4];
age[0]=23;
age[1]=34;
age[2]=65;
age[3]=74;
same_age=age;
cout<<"The
cout<<"The
cout<<"The
cout<<"The
return 0;

age
age
age
age

at
at
at
at

index[0]"<<same_age[0];
index[1]"<<same_age[1];
index[2]"<<same_age[2];
index[3]"<<same_age[3];

Line (12) tries to copy the age array into the same_age array. What happened when you tried to
compile the program above?

FCS&E, GIK Institute Topi, Pakistan

Page 5

CS102L: Intensive Programming Lab

The point here is that simply assigning one array to another will not copy the elements of the array. The
hard question to answer is why the code doesn't compile. Later in the tutorial this example will be reexamined to explain why line (12) doesn't work. This code should not compile on either C or C++
compilers. However, some older C++ compilers may ignore the ISO C++ standard and allow line 12 to
compile. If it does compile on your C++ compiler, make a mental note that it is incorrect behavior.

Let's try copying arrays using a technique similar to the technique used to print arrays (that is,
one element at a time):
Example 1.6:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:

#include <iostream.h>
int main()
{
short age[4];
short same_age[4];
age[0]=23;
age[1]=34;
age[2]=65;
age[3]=74;
same_age[0]=age[0];
same_age[1]=age[1];
same_age[2]=age[2];
same_age[3]=age[3];
cout<<"The
cout<<"The
cout<<"The
cout<<"The
return 0;

age
age
age
age

at
at
at
at

index[0]=
index[1]=
index[2]=
index[3]=

"<<same_age[0]<<endl;
"<<same_age[1]<<endl;
"<<same_age[2]<<endl;
"<<same_age[3]<<endl;

This technique for copying arrays works fine. Two arrays are created: age and same_age. Each
element of the age array is assigned a value. Then, in order to copy of the four elements in age
into the same_age array, we must do it element by element.

Copy first element

FCS&E, GIK Institute Topi, Pakistan

Page 6

CS102L: Intensive Programming Lab

Copy second element


Like printing arrays, there is no single statement in the language that says "copy an entire array into
another array". The array elements must be copied individually.

The technique used to copy one array into another is exactly the same as the technique used to
copy 4 separate variables into 4 other variables. So what is the advantage to using arrays over
separate variables?
One significant advantage of an array over separate variables is the name. In our examples, using
four separate variables requires 4 unique names. The 4 short variables in our array have the
same name, age. The 4 short's in the array are identical except for an index number used to
access them. This distinction allows us to shorten our code in a way that would be impossible
with 4 variables, each with unique names:
Example 1.7:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:

#include <iostream.h>
int main()
{
short age[4];
short same_age[4];
int i, j;
age[0]=23;
age[1]=34;
age[2]=65;
age[3]=74;
for(i=0; i<4; i++)
same_age[i]=age[i];
for(j=0; j<4; j++)
cout<<"The age is = "<<same_age[j]<<endl;
return 0;
}

Since the only difference between each of the short's in the arrays is their index, a loop and a
counter can be used to more easily copy all of the elements. The same technique is used to
shorten the code that prints the array to the screen.
FCS&E, GIK Institute Topi, Pakistan

Page 7

CS102L: Intensive Programming Lab

Even though arrays give us some convenience when managing many variables of the same type,
there is little difference between an array and variables declared individually. There is no single
statement to copy an array, there is no single statement to print an array.
If we want to perform any action on an array, we must repeatedly perform that action on each element
in the array.

2-Dimensional (2-D) Arrays


So far we have explored arrays with only one dimension. It is also possible for arrays to have two or
more dimensions. The two-dimensional array is also called a matrix.
Here is a sample program that stores roll number and marks obtained by a student side by side in a
matrix.

Example 1.8:
#include<iostream.h>
void main( )
{
int stud[4][2] ;
int i,j ;
for ( i = 0 ; i <= 3 ; i++ )
{
cout<<"\n Enter roll no. and marks "<<endl ;
cin>>stud[i][0]>>stud[i][1];
}
cout<<"The Roll No. and Marks are: "<<endl<<endl;
cout<<"RollNo.\t Marks\n";
for ( i = 0 ; i <= 3 ; i++ )
cout<<stud[i][0]<<" \t "<<stud[i][1]<<endl ;
}
There are two parts to the programin the first part through a for loop we read in the values of roll
no. and marks, whereas, in second part through another for loop we print out these values.
Look at the cin( ) statement used in the first for loop:
cin>>stud[i][0]>>stud[i][1];
In stud[i][0] and stud[i][1] the first subscript of the variable stud, is row number which changes for
every student. The second subscript tells which of the two columns are we talking aboutthe zeroth
FCS&E, GIK Institute Topi, Pakistan

Page 8

CS102L: Intensive Programming Lab

column which contains the roll no. or the first column which contains the marks. Remember the
counting of rows and columns begin with zero. The complete array arrangement is shown below.

Thus, 1234 is stored in stud[0][0], 56 is stored in stud[0][1] and so on. The above arrangement
highlights the fact that a two- dimensional array is nothing but a collection of a number of onedimensional arrays placed one below the other.
In our sample program the array elements have been stored rowwise and accessed rowwise.
However, you can access the array elements columnwise as well. Traditionally, the array elements
are being stored and accessed rowwise; therefore we would also stick to the same strategy.

Initializing a 2-Dimensional Array


How do we initialize a two-dimensional array? As simple as this:
int stud[4][2] = {
{ 1234, 56 },
{ 1212, 33 },
{ 1434, 80 },
{ 1312, 78 }
};
Or we can write like this also..
int stud[4][2] = { 1234, 56, 1212, 33, 1434, 80, 1312, 78 } ;
The above format work also but it is more difficult to read as compared to the first one. It is
important to remember that while initializing a 2-D array it is necessary to mention the second
(column) dimension, whereas the first dimension (row) is optional.
Thus the declarations,
int arr[2][3] = { 12, 34, 23, 45, 56, 45 } ;
int arr[ ][3] = { 12, 34, 23, 45, 56, 45 } ;

are perfectly acceptable, whereas,


int arr[2][ ] = { 12, 34, 23, 45, 56, 45 } ;
int arr[ ][ ] = { 12, 34, 23, 45, 56, 45 } ;
FCS&E, GIK Institute Topi, Pakistan

Page 9

CS102L: Intensive Programming Lab

would never work.

Memory Map of a 2-Dimensional Array


Let see the arrangement of the above student arrays in memory which contains roll number and marks.

Example 1.9:
This example will simply define an array of 2x3 and take input from user and prints scanned
array.
#include<iostream.h>
void main()
{
int arr[2][3],i,j;
for(i=0;i<2;i++)
for(j=0;j<3;j++)
{
cout<<"Enter Value for " <<i+1 << " row " <<j+1 <<" column\n";
cin>>arr[i][j];
}
cout<<"\n!!!Scanning Array Complete!!!\n\n\n";
for(i=0;i<2;i++)
{
for(j=0;j<3;j++)
FCS&E, GIK Institute Topi, Pakistan

Page 10

CS102L: Intensive Programming Lab

cout<<arr[i][j] <<"\t";
cout<<"\n\n";
}
}

Example 1.10:
#include <iostream>
using namespace std;
int main()
{
// A 2-Dimensional array
double distance[2][4];
cout<<"Enter values\n";
for(int i = 0; i < 2; ++i)

// this loop is for Rows

for(int j = 0; j < 4; ++j)

// this loop is for Columns

cin>>distance[i][j];

//getting values from the user

cout << "Members of the array";

// the statement will be print as it is

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

// For Rows

for(int j = 0; j < 4; ++j)

// For Columns

cout << "\nDistance [" << i << "][" << j << "]: " << distance[i][j];

//Printing Rows and Columns

cout << endl;


return 0;

Arrays and Functions:


An array can be passed to a function as argument. An array can also be returned by a function.
To declare and define that a function takes an array as argument, declare the function as you
FCS&E, GIK Institute Topi, Pakistan

Page 11

CS102L: Intensive Programming Lab

would do for any regular function and, in its parentheses, specify that the argument is an array.
Here is an example:
#include <iostream>
using namespace std;
void DisplayTheArray(double member[]) //function
{
for(int i = 0; i < 5; ++i)
cout << "\nDistance " << i + 1 << ": " << member[i];
cout << endl;
}
int main()
{
const int numberOfItems = 5;
double distance[] = {44.14, 720.52, 96.08, 468.78, 6.28};
cout << "Members of the array";
DisplayTheArray(distance);
//calling the function
return 0;
}

This would produce:

Note:
Before doing your lab exercises run the examples.
Exercises will be provided by the instructors in the lab.

FCS&E, GIK Institute Topi, Pakistan

Page 12

CS102L: Intensive Programming Lab

Lab 2:
Pointers in C++
In this lab we will be discussing pointers in detail. This is one of the most important
concepts in C++ language. Pointers are used everywhere in C++, so if you want to use the C++
language fully you have to have a very good understanding of pointers. They have to become comfortable
for you.
C++ uses pointers in three different ways:

C++ uses pointers to create dynamic data structures -- data structures built up from blocks of
memory allocated from the heap at run-time.

C++ uses pointers to handle variable parameters passed to functions.

Pointers in C++ provide an alternative way to access information stored in arrays. Pointer
techniques are especially valuable when you work with strings. There is an intimate link between
arrays and pointers in C++.

To fully grasp the concept of pointers all you need is the concept and practice of pointers. Before
talking about pointers lets talk a bit about computer memory.

Computer Memory
Essentially, the computer's memory is made up of bytes. Each byte has a number, an address,
associated with it. The picture below represents several bytes of a computer's memory. In the
picture, addresses 924 thru 940 are shown.

Variable and Computer Memory


A variable in a program is something with a name, the value of which can vary. The way the
compiler handles this is that it assigns a specific block of memory within the computer to hold the value
of that variable. The size of that block depends on the range over which the variable is allowed to vary.
For example, on 32 bit PC's the size of an integer variable is 4 bytes. On older 16 bit PCs integers were 2
bytes.

Example 2.1:
#include<iostream>
using namespace std;
int main()
FCS&E, GIK Institute Topi, Pakistan

Page 13

CS102L: Intensive Programming Lab

{
float fl=3.14;
cout<<fl;
cin>>fl;
return 0;
}
The output for this program will be

How It Works: (Explanation)


At line (4) in the program above, the computer reserves memory for fl. Depending on the
computer's architecture, a float may require 2, 4, 8 or some other number of bytes. In our
example, we'll assume that a float requires 4 bytes.

When fl is used in line (5), two distinct steps occur:


1. The program finds and grabs the address reserved for fl--in this example 924.
2. The contents stored at that address are retrieved
The illustration that shows 3.14 in the computer's memory can be misleading. Looking at the diagram, it
appears that "3" is stored in memory location 924, "." is stored in memory location 925, "1" in 926, and "4"
in 927. Keep in mind that the computer actually converts the floating point number 3.14 into a set of ones
and zeros. Each byte holds 8 ones or zeros. So, our 4 byte float is stored as 32 ones and zeros (8 per byte
times 4 bytes). Regardless of whether the number is 3.14, or -273.15, the number is always stored in 4
bytes as a series of 32 ones and zeros.

Pointer:
In C++ a pointer is a variable that points to or references a memory location in which data is stored. A
pointer is a variable that points to another variable. This means that a pointer holds the memory address
of another variable. Put another way, the pointer does not hold a value in the traditional sense; instead, it
holds the address of another variable. A pointer "points to" that other variable by holding a copy of its
address. Because a pointer holds an address rather than a value, it has two parts. The pointer itself holds
the address and that address points to a value.

FCS&E, GIK Institute Topi, Pakistan

Page 14

CS102L: Intensive Programming Lab

Pointer declaration:
A pointer is a variable that contains the memory location of another variable. The syntax is as
shown below. You start by specifying the type of data stored in the location identified by the
pointer. The asterisk tells the compiler that you are creating a pointer variable. Finally you give
the name of the variable.
Data_type *variable_name
Such a variable is called a pointer variable (for reasons which hopefully will become clearer a
little later). In C++ when we define a pointer variable we do so by preceding its name with an
asterisk. In C++ we also give our pointer a type which, in this case, refers to the type of data
stored at the address we will be storing in our pointer. For example, consider the variable
declaration:
int *ptr;
int k;
ptr is the name of our variable (just as k is the name of our integer variable). The '*' informs the
compiler that we want a pointer variable, i.e. to set aside however many bytes is required to store
an address in memory. The int says that we intend to use our pointer variable to store the address
of an integer.
Referencing Operator
Suppose now that we want to store in ptr the address of our integer variable k. To do this we use
the unary & operator and write:
ptr = &k;
What the & operator does is retrieve the address of k, and copies that to the contents of our
pointer ptr. Now, ptr is said to "point to" k.
Dereferencing operator
The "dereferencing operator" is the asterisk and it is used as follows:
*ptr = 7;
will copy 7 to the address pointed to by ptr. Thus if ptr "points to" (contains the address of) k,
the above statement will set the value of k to 7. That is, when we use the '*' this way we are
referring to the value of that which ptr is pointing to, not the value of the pointer itself.
Similarly, we could write:
cout<<*ptr<<endl;
to print to the screen the integer value stored at the address pointed to by ptr;.

FCS&E, GIK Institute Topi, Pakistan

Page 15

CS102L: Intensive Programming Lab

Here is graphical representation of Pointers.

This Example will be very helpful in understanding the pointers. Understand it thoroughly how it
works and then proceed.
Example 2.2:
#include<iostream>
using namespace std;
int main ()
{
int firstvalue = 5, secondvalue = 15;
int * p1, * p2;
p1 = &firstvalue;
// p1 = address of firstvalue
p2 = &secondvalue;
// p2 = address of secondvalue
*p1 = 10;
// value pointed by p1 = 10
*p2 = *p1;
// value pointed by p2 = value pointed by p1
p1 = p2;
// p1 = p2 (value of pointer is copied)
*p1 = 20;
// value pointed by p1 = 20
cout<<"First Value is " << firstvalue<<endl;
cout<<"Second Value is " <<secondvalue<<endl;
return 0;
}
Output of this listing is as follows:

How it Works:
FCS&E, GIK Institute Topi, Pakistan

Page 16

CS102L: Intensive Programming Lab

Here in this code we are trying to play with memory and address of our variables for the better
understanding of Pointers. On line number 5 we have two integer variables (i.e firstvalue and
secondvalue). Both are assigned values of 5 and 15 respectively. On line number 6 we have two
integer pointer variables (i.e p1 and p2). Both are assigned addresses of variables in line 5
firstvalue and secondvalue respectively in line 7 and 8.
firstvalue

secondvalue

758

15

754

P1

P2

In line 9 we see that *p1 is assigned value 10. This means that 10 should be copied in the
variable, which is lying on an address to which p1 is pointing. We know that p1 is pointing to
address of firstvalue. So line 9 results in assigning firstvalue the value of 10.

firstvalue

secondvalue

10

758

15

754

P1

P2

In line 10 we encounter another assignment which says that value of variable pointed by p2
should be replaced with the value of variable pointed by p1. So now secondvalue is assigned
with value 10 as well.
firstvalue

secondvalue

10

758

10

754

P1

P2

Well the assignment in line 11 is a bit confusing but very simple, all this assignment is doing is
that now p1 is pointing to the same address as p2. So now we can say p1 and p2 are pointing at
same address.
firstvalue
FCS&E, GIK Institute Topi, Pakistan

secondvalue
Page 17

CS102L: Intensive Programming Lab


15

10

758

754
P2

P1
In line 12 we see that *p1 is assigned value 20. This means that 10 should be copied in the
variable, which is lying on an address to which p1 is pointing. We know that p1 is now pointing
to address of secondvalue because in last line we pointed p1 to the address being pointed by p2.
So line 12 results in assigning secondvalue the value of 20.
firstvalue

secondvalue

10

758

20

754
P2

P1

Now when we print the value of first value and second value it prints 10 for firstvalue and 20 for
secondvalue; which is right due to the reasons explained above.
Pointers: Pointing to the Same Address
Here is a cool aspect of C++: Any number of pointers can point to the same address. For
example, you could declare p, q, and r as integer pointers and set all of them to point to i, as shown here:
int i;
int *p, *q, *r;

p = &i;
q = &i;
r = p;
Note that in this code, r points to the same thing that p points to, which is i. You can assign pointers to
one another, and the address is copied from the right-hand side to the left-hand side during the
assignment. After executing the above code, this is how things would look:

FCS&E, GIK Institute Topi, Pakistan

Page 18

CS102L: Intensive Programming Lab

The variable i now has four names: i, *p, *q and *r. There is no limit on the number of pointers that can
hold (and therefore point to) the same address.

Pointer Arithmetics
Like other variables pointer variables can be used in expressions. For example if p1 and p2 are
properly declared and initialized pointers, then the following statements are valid.
y=*p1 * *p2;
sum=sum+*p1;
z= 5 - *p2/*p1;
*p2= *p2 + 10;
C++ allows us to add integers to or subtract integers from pointers as well as to subtract one
pointer from the other. We can also use short hand operators with the pointers p1+=; sum+=*p2;
etc.,
we can also compare pointers by using relational operators the expressions such as p1 >p2 ,
p1==p2 and p1!=p2 are allowed.
When an integer is added to, or subtracted from, a pointer, the pointer is not simply incremented
or decremented by that integer, but by that integer times the size of the object to which the
pointer refers. The number of bytes depends on the object's data type.
/*Program to illustrate the pointer expression and pointer arithmetic*/
Example 2.3:
1:#include<iostream>
2:using namespace std;
3: int main()
4: {
5:int *ptr1,*ptr2;
6:int a,b,x,y,z;
7:a=30;b=6;
FCS&E, GIK Institute Topi, Pakistan

Page 19

CS102L: Intensive Programming Lab

8:ptr1=&a;
9:ptr2=&b;
10:x=*ptr1 + *ptr2 - 6;
11:y=6 - *ptr1 / *ptr2 +30;
12: cout<<"Address of a: "<<ptr1<<endl;
13: cout<<"Address-Value in ptr1: "<<*ptr1<<endl<<endl;
//The comment value is the value of ptr1 (address of the a)
14: cout<<"Address of b: "<<ptr2<<endl;
15: cout<<"Address-Value in ptr1: "<<*ptr2<<endl<<endl;
//The comment value is the value of ptr2 (address of the b)
16: cout<<"a: " <<a<<" b: "<<b<<endl<<endl;
//Simply prints the value of a and b
17: cout<<"x: " <<x<<" y: "<<y<<endl<<endl;
//Simply prints the value of x and y.
18: ptr1=ptr1 + 1; // adds 4 in address of ptr1. (1*4 = 4)
19: ptr2= ptr2;
20: cout<<"a: " <<a<<" b: "<<b<<endl;
//Simply prints the value of a and b
21: cout<<"Value in ptr1: "<<ptr1<<endl<<endl; // 2293564
//The comment value is the new memory location value of ptr1
22: cout<<"\nAddress-Value in ptr1: "<<*ptr1<<endl<<endl; // garbage value
//The comment value is the new value of ptr1 (garbage value)
23: cout<<"Address of b: "<<ptr2<<endl<<endl;
24: cout<<"\nAddress-Value in ptr1: "<<*ptr2<<endl<<endl;
cin>>a;
}
Here note that adding some thing in *ptr1 changes the value of the address stored
in ptr. However adding some thing in ptr will change the address it is pointing to.
Printing ptr1 after adding 1 in it gives different address as it has changed by 4
Bytes.

FCS&E, GIK Institute Topi, Pakistan

Page 20

CS102L: Intensive Programming Lab

How it Works:
This code explains all the rules related to arithematic of pointers. From line 1 to line 11, it is
simply adding, subtracting and like manipulating with the pointers and variables. After all the
manipulations and arithmetics it started printing values of pointers and other simple variables till
line 12.
a

30

7864

7868

7872

7876

7880

ptr1

7884

7888

ptr2

At line 18 it adds 1 to ptr1. Mostly people think that this will change the address of the pointer,
but they are totally wrong. Remember pointer is pointing to an address. This addition does not
change the address of the pointer, infact it changes the value of the pointer (not the value of the
address pointer is pointing at.). ptr1 has the address of variable of variable a . So it adds 1 *4
Btytes = 4bytes in the address of a which is stored in ptr1. Where as ptr2 points at the same value
as before due to the assignment of line 19.
a
b
30

7864

7868

7872

0XF

7876

ptr1

7880

7884

7888

ptr2

Line 20 prints the same value as was printed by the Line 16, because values of the variable was
never changed, in fact ptr1s value which was address of a was changed. Now Line 21 will print

FCS&E, GIK Institute Topi, Pakistan

Page 21

CS102L: Intensive Programming Lab

the value stored in ptr1; which is address of memory 4 bytes ahead of variable a. Line 22 is
trying to print the value at address, ptr1 is now pointing to, which was never assigned any value.

Sending Pointers as Arguments to Functions


When we pass pointers to some function, the addresses of actual arguments in the calling function are
copied into the arguments of the called function. This means that using these addresses we would have
an access to the actual arguments and hence we would be able to manipulate them. The following
program illustrates this fact. Try this out:

Example 2.4:

#include<iostream>
void swap(int *,int *);
using namespace std;
int main( )
{
int a = 10, b = 20 ;
int *p, *q;
p = &a;
q = &b;
swap( p, q) ;
cout<<"a: "<<a<<"b: "<<b<<endl;
cin>>a;
}
void swap( int *x, int *y )
{
int t = *x ;
*x = *y;
*y = t;
}
The output of the above program would be:

a = 20 b = 10
Note that this program manages to exchange the values of a and b using their addresses stored in x and
y.

Function Pointers
FCS&E, GIK Institute Topi, Pakistan

Page 22

CS102L: Intensive Programming Lab

A function pointer is a variable that stores the address of a function that can later be called through that
function pointer. A useful technique is the ability to have pointers to functions. Their declaration is
easy: write the declaration as it would be for the function, say
int func(int a, float b);
And simply put brackets around the name and a * in front of it: that declares the pointer. Because of
precedence, if you don't parenthesize the name, you declare a function returning a pointer:
/* function returning pointer to int */
int *func(int a, float b); //Wrong
/* pointer to function returning int */
int (*func)(int a, float b);
Once you've got the pointer, you can assign the address of the right sort of function just by using its
name: like an array, a function name is turned into an address when it's used in an expression. You can
call the function as:
(*func)(1,2);

Example 2.5
#include<iostream>
using namespace std;
void func(int);
int main(){
void (*fp)(int);
fp = func;
(*fp)(1);
cout<<endl;
fp(2);
system("PAUSE");
return 0;
}
Void func(int arg)
{
cout<<arg<<endl;
}

Pointers and arrays


The concept of arrays is related to that of pointers. In fact, arrays work very much like pointers to
their first elements, and, actually, an array can always be implicitly converted to the pointer of the
proper type. For example, consider these two declarations:

int myarray [20];


int * mypointer;

FCS&E, GIK Institute Topi, Pakistan

Page 23

CS102L: Intensive Programming Lab

The following assignment operation would be valid:

mypointer = myarray;
After that, mypointer and myarray would be equivalent and would have very similar properties. The
main difference being that mypointer can be assigned a different address, whereas myarray can
never be assigned anything, and will always represent the same block of 20 elements of type int.
Therefore, the following assignment would not be valid:

myarray = mypointer;
Let's see an example that mixes arrays and pointers:

Example 2.6:
#include <iostream>
using namespace std;
int main ()
{
int numbers[5];
int * p;
p = numbers; *p = 10;
p++; *p = 20;
p = &numbers[2]; *p = 30;
p = numbers + 3; *p = 40;
p = numbers; *(p+4) = 50;
for (int n=0; n<5; n++)
cout << numbers[n] << ", ";
return 0;}
Output would be
10, 20, 30, 40, 50,
Pointers and arrays support the same set of operations, with the same meaning for both. The main
difference being that pointers can be assigned new addresses, while arrays cannot.
In the chapter about arrays, brackets ([]) were explained as specifying the index of an element of the
array. Well, in fact these brackets are a dereferencing operator known as offset operator. They
dereference the variable they follow just as * does, but they also add the number between brackets to
the address being dereferenced. For example:

a[5] = 0;
*(a+5) = 0;

// a [offset of 5] = 0

// pointed by (a+5) = 0

These two expressions are equivalent and valid, not only if a is a pointer, but also if a is an array.
Remember that if an array, its name can be used just like a pointer to its first element.

Note:
Before doing your lab exercises run the examples.
Exercises will be provided by the instructors in the lab.

FCS&E, GIK Institute Topi, Pakistan

Page 24

CS102L: Intensive Programming Lab

Lab 3:
Strings in C++
Handling of Character String

A string is a sequence of characters. Any sequence or set of characters defined within


double quotation symbols is a constant string. In C++ it is required to do some meaningful
operations on strings they are:

Reading string
Displaying strings
Combining or concatenating strings
Copying one string to another.
Comparing string & checking whether they are equal
Extraction of a portion of a string

Strings are stored in memory as ASCII codes of characters that make up the string
appended with \0 (ASCII value of null). Normally each character is stored in one byte;
successive characters are stored in successive bytes.

Arrays of Characters
Re-Introduction to Characters
As it happens, strings are the most used items of computers. In fact, anything the user types is a
string. It is up to you to convert it to another, appropriate, type of your choice. This is because
calculations cannot be performed on strings. On the other hand, strings can be a little complex,
FCS&E, GIK Institute Topi, Pakistan

Page 25

CS102L: Intensive Programming Lab

which is why we wanted to first know how to use the other types and feel enough comfortable
with them.
Consider a name such as James. This is made of 5 letters, namely J, a, m, e, and s. Such letters,
called characters, can be created and initialized as follows:
char L1 = 'J', L2 = 'a', L3 = 'm', L4 = 'e', L5 = 's';
To display these characters as a group, you can use the following:
cout << "The name is " << L1 << L2 << L3 << L4 << L5;
Here is such a program:
Example 2.3:

#include <iostream>
using namespace std;
int main()
{
char L1 = 'J', L2 = 'a', L3 = 'm', L4 = 'e', L5 = 's';
cout << "The name is " << L1 << L2 << L3 << L4 << L5;
return 0;
}

This would produce:

Declaring and Initializing an Array of Characters


When studying arrays, we were listing the numeric members of the array between curly
brackets. Here is an example:
int Number[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

FCS&E, GIK Institute Topi, Pakistan

Page 26

CS102L: Intensive Programming Lab

Because a character is initialized by including it in single-quotes, when creating an array of


characters, to initialize it, you must also include each letter accordingly. A name such as
James can be initialized as follows:
char Name[6] = { 'J', 'a', 'm', 'e', 's' };
As done with other arrays, each member of this array of characters can be accessed using
its index. Here is an example:
Example 3.1:
#include <iostream>
using namespace std;
int main()
{
char Name[6] = { 'J', 'a', 'm', 'e', 's' };
cout << "The name is " << Name[0] << Name[1] << Name[2] << Name[3] << Name[4];
return 0;
}

The C/C++ provides another alternative. It allows you to declare and initialize the array as
a whole. To do this, include the name in double-quotes. With this technique, the curly
brackets that delimit an array are not necessary anymore. Here is an example:
char Name[12] = "James";
With this technique, the item between the double-quotes is called a string. It is also
referred to as the value of the string or the value of the variable.
When declaring and initializing an array of characters, the compiler does not need to know
the number of characters of the string. In fact, you can let the compiler figure it out.
Therefore, you can leave the square brackets empty:
char Name[] = "James";
After declaring such an array, the compiler would count the number of characters of the
variable, add one more variable to it and allocate enough space for the variable. The
character added is called the null-terminated character and it is represented as \0.
Therefore, a string such as James would be stored as follows:
FCS&E, GIK Institute Topi, Pakistan

Page 27

CS102L: Intensive Programming Lab

\0

Streaming an Array of Characters


Like any other variable, before using a string, you must first declare it, which is done by
type the char keyword, followed by the name of the variable, followed by square brackets.
When declaring the variable, if/since you do not know the number of characters needed for
the string; you must still provide an estimate number. You can provide a value large
enough to accommodate the maximum number of characters that would be necessary for
the variable. For a person's name, this could be 20. For the title of a book or a web page,
this could be longer. Here are examples:
char Name[20];
char BookTitle[40];
char WebReference[80];
char WeekDay[4];
To request the value of an array of characters, use the cin extractor just like you would
proceed with any other variable, without the square bracket. Here is an example:
char WeekDay[12];
cout << "Enter today's name: ";
cin >> WeekDay;
To display the value of an array of characters, use the cout extractor as we have used it
with all other variables, without the square brackets. Here is an example:
Example 3.2:
#include <iostream>
using namespace std;
int main()
{
char WeekDay[12];
char EndMe[] = "\n";
cout << "Enter today's name: ";
cin >> WeekDay;
cout << "Today is " << WeekDay;
cout << EndMe;
return 0;
}

Here is an example of running the program:


Enter today's name: Thursday
FCS&E, GIK Institute Topi, Pakistan

Page 28

CS102L: Intensive Programming Lab

Today is Thursday

Multidimensional Arrays of Characters


C/C++ treats arrays of characters differently than it does the other arrays. For example, we
have learned to declare a two-dimensional array of integers as follows:
int Number[2][6] = { { 31, 28, 31, 30, 31, 30 },
{ 31, 31, 30, 31, 30, 31 } };
This variable is in fact two arrays and each array contains 6 integers. For a string, if you
want to declare a two-dimension array of characters, the first dimension specifies the
number of string in the variable. The second dimension specifies the number of characters
that each string can hold. Here is an example:
char StudentName[4][10] = { "Hermine", "Paul", "Gertrude", "Leon" };
In this case, the StudentName variable is an array of 4 strings and each string can have a
maximum of 9 characters (+1 for the null-terminated character). To locate a string on this
variable, type the name of the array followed by its index, which is the index of the column.
This means that the first string of the StudentName array can be accessed with
StudentName[0]. The second would be StudentName[1], etc. This allows you to display
each string on the cout extractor:
Example 3.3:
#include <iostream>
using namespace std;
int main()
{ char StudentName[4][10] = { "Hermine", "Paul", "Gertrude", "Leon" };
cout << "Student Names";
cout << "\nStudent 1: " << StudentName[0];
cout << "\nStudent 2: " << StudentName[1];
cout << "\nStudent 3: " << StudentName[2];
cout << "\nStudent 4: " << StudentName[3];
return 0;
}
This would produce:
Student Names
FCS&E, GIK Institute Topi, Pakistan

Page 29

CS102L: Intensive Programming Lab

Student 1: Hermine
Student 2: Paul
Student 3: Gertrude
Student 4: Leon
When declaring and initializing such an array, the compiler does not need to know the
number of strings in the array; it can figure it out on its own. Therefore, you can leave the
first pair square brackets empty. If you are only declaring the array but cannot initialize,
then you must specify both dimensions.

Declaring a Pointer to Characters

Pointers can be quite challenging to learn. The diagram should provide a better insight.
The variable name (a pointer to a char) was located at address 0x007fe78. A pointer is 32 bits, ie. 4
bytes long and holds an address of the string which was 0x00408004. Addresses are always given
in hexadecimal. The arrow shows what the pointer is pointing to, ie what is at the address in the
pointer variable.
At this address there were 13 bytes holding the string "David Bolton" with a terminating NULL, the
same value as '\0'.

Earlier, we declared an array as follows:


char EndMe[] = "";
The name of the variable is a pointer to the beginning of the array. For this reason, the
name of the reason is sufficient to locate its value. Since in this case we do not specify the
number of characters in the array, we can also just use a pointer to the array as the
beginning of the array. The array can therefore be declared as follows:
char *EndMe = "";
Once again, to display the value of such an array, simply call its name on the cout
extractor:
#include <iostream>
using namespace std;
int main()
{

FCS&E, GIK Institute Topi, Pakistan

Page 30

CS102L: Intensive Programming Lab

char *EndMe = "";


cout << EndMe;
return 0;
}

To request the value of an array of characters from the user, you can declare a pointer to char and
initialize it with an estimate number of characters using the new operator. Here is an example:

Example 3.4:
#include <iostream>
using namespace std;
int main()
{
char *StudentName = new char[20];
cout << "Enter Sudent First Name: ";
cin >> StudentName;
cout << "\nStudent First Name: " << StudentName;
return 0;
}

Here is an example of running the program:


Enter Sudent First Name: David
Student First Name: David

Arrays of Characters and Pointers


#include <iostream>
using namespace std;
int main()
{
double *Value;
Value = 12.55;
cout << "Value = " << Value;
return 0;
}

On the other hand, the following declaring of an array of characters and its later initialization is
perfectly legal:

Example 3.8:
#include <iostream>
using namespace std;
int main()
{
char *Country;
Country = "Republique d'Afrique du Sud";
cout << "Country Name: " << Country << "\n\n";
return 0;
}

FCS&E, GIK Institute Topi, Pakistan

Page 31

CS102L: Intensive Programming Lab

String Manipulation Functions


Strings as Variables
To create a variable of type string, simply:
string fullName;
Assignment, as always is the same:
fullName = Aamir Hassan;
Or like before, we could always combine the two with an initialization:
string fullName = Aamir Hassan;

Input/output with Strings


I/O with Strings is as before:
string fullName = ;
cout << Please enter your name: ;
cin >> fullName; //get the name
cout << Your name is << fullName;

String Operators: Assignment


Assignment (=): As with before, assign a string to a variable of type string.
string oneName = Edwin;
string anotherName = oneName;
Both now hold Edwin

String Operators: Concatenation


Concatenation (+): Puts a string on the end of another.
string firstName = Edwin;
string lastName = Dreese;
string fullName = firstName + + lastname;
//the += shorthand also works
string oneString = 2 + 2 = ;
oneString += 5;

FCS&E, GIK Institute Topi, Pakistan

Page 32

CS102L: Intensive Programming Lab

Relational String Operators


== and != are same as before, but the others are not exactly like usage with numbers
For instance, what exactly does it mean if one string is less than another?

String I/O
A common problem with reading strings from user input is that it could contain white
spaces. Remember that white space (e.g. space, tab, newline) is treated as termination for
cin.
Take the following code for example:
cout << Enter your full name: ;
string fullname;
//only the first name will be read in!!
cin >> fullname;

String Processing
In addition to giving us a new data type to hold strings, the string library offers many
useful string processing methods.
You can find most of them of them in the book, but here are a few useful ones.

length () and size()


This method returns the integer length of the string. The length() and size() are the same.
Example:
string s1 = Super!;
//the integer 6 will be output.
cout << s1.length() << endl;

at(index)
This method returns the character at the specified index. Indices start from 0.
Example:
string n = Vikram;
//the character V will be output.
cout << n.at(0) << endl;
FCS&E, GIK Institute Topi, Pakistan

Page 33

CS102L: Intensive Programming Lab

//the character m will be output.


cout << n.at(n.size()-1) << endl;

Shorthand for at (index)


As an alternative, we could have also used the following equivalent shorthand:
string n = Vikram;
//the character V will be output.
cout << n[0] << endl;
//the character m will be output.
cout << n[n.size()-1] << endl;

erase(index)
This method removes all characters from the string starting from the specified index to
the end.
The length of the new string is reset to index!
Example:
string os = Operating Systems;
os.erase(9);
//the string Operating is output
cout << os << endl;
//length is now 9, the index
cout << os.length() << endl;

find(str)
This method returns the integer index of the first occurrence of the specified string
Example:
string d = data data data;
//0 is output
cout << d.find(data) << endl;

FCS&E, GIK Institute Topi, Pakistan

Page 34

CS102L: Intensive Programming Lab

find(str, index)
This method returns the integer index of the first occurrence of the specified string
starting from the specified index.
Returns -1 if pattern is not found.
Example:
string d = data data data;
//5 is output
cout << d.find(data, 1) << endl;
Why? Because by specifying a starting index of 1, we only consider
ata data data

insert(index, str)
Inserts the specified string at the specified index.
Example:
string animal = Hippo;
animal.insert(0, Happy );
//outputs Happy Hippo
cout << animal << endl;

replace(index, n, str)
Removes n characters in the string starting from the specified index, and inserts the
specified
string, str, in its place.
Example:
string transport = Speed Boat;
transport.replace(0, 5, Sail);
//outputs Sail Boat
cout << transport << endl;

substr(index, n)
Returns the string consisting of n characters starting from the specified index.

FCS&E, GIK Institute Topi, Pakistan

Page 35

CS102L: Intensive Programming Lab

Example:
string transport = Cruise Ship;
//outputs Ship
cout << transport.substr(7, 4) << endl;

Some String Functions

strlen()
strcpy()
strncpy()
strcat()
strncat()
strcmp()
strncmp()

Strlen ()
Syntax:
len = strlen(ptr);
where len is an integer and
ptr is a pointer to char
strlen() returns the length of a string, excluding the null. The following code will result in
len having the value 13.
int len;
char str[15];
strcpy(str, "Hello, world!");
len = strlen(str);
Example 3.9:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{

FCS&E, GIK Institute Topi, Pakistan

Page 36

CS102L: Intensive Programming Lab

int len;
char str[15];
strcpy(str, "Hello, world!");
len = strlen(str);
cout<<len<<endl;
system("PAUSE");
return 0;
}
Strcpy ()
strcpy(ptr1, ptr2);
where ptr1 and ptr2 are pointers to char
strcpy() is used to copy a null-terminated string into a variable. Given the following
declarations, several things are possible.
char S[25];
char D[25];

Putting text into a string:


strcpy(S, "This is String 1.");
Copying a whole string from S to D:
strcpy(D, S);
Copying the tail end of string S to D:
strcpy(D, &S[8]);

N.B. If you fail to ensure that the source string is null-terminated, very strange and
sometimes very ugly things may result.
Strncpy ()
strncpy(ptr1, ptr2, n);
where n is an integer and
ptr1 and ptr2 are pointers to char
strncpy() is used to copy a portion of a possibly null-terminated string into a variable. Care
must be taken because the '\0' is put at the end of destination string only if it is within the
part of the string being copied. Given the following declarations, several things are possible.
char S[25];
FCS&E, GIK Institute Topi, Pakistan

Page 37

CS102L: Intensive Programming Lab

char D[25];
Assume that the following statement has been executed before each of the remaining code
fragments.

Putting text into the source string:


strcpy(S, "This is String 1.");
Copying four characters from the beginning of S to D and placing a null at the end:
strncpy(D, S, 4);
D[4] = '\0';
Copying two characters from the middle of string S to D:
strncpy(D, &S[5], 2);
D[2] = '\0';
Copying the tail end of string S to D:
strncpy(D, &S[8], 15);
Which produces the same result as strcpy(D, &S[8]);

Strcat ()
strcat(ptr1, ptr2);
where ptr1 and ptr2 are pointers to char
strcat() is used to concatenate a null-terminated string to end of another string variable.
This is equivalent to pasting one string onto the end of another, overwriting the null
terminator. There is only one common use for strcat().
Char S[25] = "world!";
Char D[25] = "Hello, ";

Concatenating the whole string S onto D:


strcat(D, S);

N.B. If you fail to ensure that the source string is null-terminated, very strange and
sometimes very ugly things may result.
Strncat ()
Syntax: strncat(ptr1, ptr2, n);
where n is an integer and
ptr1 and ptr2 are pointers to char
strncat() is used to concatenate a portion of a possibly null-terminated string onto the end
of another string variable. Care must be taken because some earlier implementations of C
do not append the '\0' at the end of destination string. Given the following declarations,
several things are possible, but only one is commonly used.
Char S[25] = "world!";
FCS&E, GIK Institute Topi, Pakistan

Page 38

CS102L: Intensive Programming Lab

Char D[25] = "Hello, ";

Concatenating five characters from the beginning of S onto the end of D and placing
a null at the end:
strncat(D, S, 5);
strncat(D, S, strlen(S) -1);
Both would result in D containing "Hello, world".

N.B. If you fail to ensure that the source string is null-terminated, very strange and
sometimes very ugly things may result.
Strcmp ()
Syntax: diff = strcmp(ptr1, ptr2);
where diff is an integer and
ptr1 and ptr2 are pointers to char
strcmp() is used to compare two strings. The strings are compared character by character
starting at the characters pointed at by the two pointers. If the strings are identical, the
integer value zero (0) is returned. As soon as a difference is found, the comparison is halted
and if the ASCII value at the point of difference in the first string is less than that in the
second (e.g. 'a' 0x61 vs. 'e' 0x65) a negative value is returned; otherwise, a positive value is
returned. Examine the following examples.
char s1[25] = "pat";
char s2[25] = "pet";
diff will have a negative value after the following statement is executed.
diff = strcmp(s1, s2);
diff will have a positive value after the following statement is executed.
diff = strcmp(s2, s1);
diff will have a value of zero (0) after the execution of the following statement, which
compares s1 with itself.
diff = strcmp(s1, s1);
Strncmp ()
Syntax: diff = strncmp(ptr1, ptr2, n);
where diff and n are integers
ptr1 and ptr2 are pointers to char

FCS&E, GIK Institute Topi, Pakistan

Page 39

CS102L: Intensive Programming Lab

strncmp() is used to compare the first n characters of two strings. The strings are
compared character by character starting at the characters pointed at by the two pointers.
If the first n strings are identical, the integer value zero (0) is returned. As soon as a
difference is found, the comparison is halted and if the ASCII value at the point of difference
in the first string is less than that in the second (e.g. 'a' 0x61 vs. 'e' 0x65) a negative value is
returned; otherwise, a positive value is returned. Examine the following examples.
char s1[25] = "pat";
char s2[25] = "pet";
diff will have a negative value after the following statement is executed.
diff = strncmp(s1, s2, 2);
diff will have a positive value after the following statement is executed.
diff = strncmp(s2, s1, 3);
diff will have a value of zero (0) after the following statement.
diff = strncmp(s1, s2, 1);

Note: Before doing your lab exercises run the examples.


Exercises will be provided by the instructors in the lab.

FCS&E, GIK Institute Topi, Pakistan

Page 40

CS102L: Intensive Programming Lab

Lab 4:
Structures I

Structure Declaration
Structure Definition
Structure Variables
Structure Membership
Arrays of structures

STRUCTURES
Arrays require that all elements be of the same data type. Many times it is necessary to group
information of different data types. An example is a materials list for a product. The list typically
includes a name for each item, a part number, dimensions, weight, and cost.
C and C++ support data structures that can store combinations of character, integer floating point and
enumerated type data. They are called a STRUCTS.
Often multiple variables must be passed from one function to another, and often these variables have
different data types. Thus it is useful to have a container to store various types of variables in. Structs
allow the programmer to do just that
A struct is a derived data type that consists of members that are each fundamental or derived data
types.
struct is used to declare a new data-type. Basically this means grouping variables together.

Structures Definition
Before a structure is created, it is necessary to define its overall composition. The format of the a
structure is provided in the shape of a template or pattern which is then used to creatstructure
variables of the same composition. The template is composed of the names and attributes of the data
items to be included in the structure. The definition begins with the keyword structwhich is followed by
a structure declaration consist of a set of user-defined data names and data types. These entries are
separated by semicolons and enclosed within a pair of curly brackets. The definition ends with a
semicolon. Thus, in general, the structure definition has the form
struct tag-name{
type var-1;
type-var-2;
........
typevar-n;
};
wheretag-name is the user-supplied name that identified the structure template; type refers to any
valid data type such as char, int, float, and so forth; and var-1, var-2, .var-n are user-defined variables

FCS&E, GIK Institute Topi, Pakistan

Page 41

CS102L: Intensive Programming Lab

names, arrays or pointers. The components of a structure are commonly referred to as members or
field.

Example 4.1:
struct employee
{

char name[30];
int age;
float salary;

}
Representation in Memory
struct student
{
char name[20];
introll_no;
};
struct student st;

...
0123

19

roll_no

Structures Variables
This code fragment illustrates how structure variables of the type structemployee are defined.
struct employee{
char name[30];
int age;
float salary;
};
structemployee emp1, emp2, emp3;
In the above example three structure variables emp1, emp2, and emp3 are created. Each structure
consists of three members identified by name, age, and salary.

OR

FCS&E, GIK Institute Topi, Pakistan

Page 42

CS102L: Intensive Programming Lab

struct employee
{char name[30];
int age;
float salary;
} emp1, emp2, emp3;

Structure membership
Members themselves are not variables they should be linked to structure variables in order to make
them meaningful members.
The link between a member and a variable is established using the member operator . i.e. dot
operator or period operator. We access individual members of a structure with the .operator. For
example to assign a value, we can enter:

structx
{int a;
int b;
int c;
};
main()
{
struct x z;
z.a = 10; // assigns member a of structure variable z value 10.
z.b = 20;
z.c = 30;
}

And to retrieve a value from a structure member:


struct x
{
int a;
int b;
int c;
};
main()
{ struct x z;
z.a = 10; // Assigning a value to a member through a structure variable
z.a++; // incrementing the member variable.
cout<<z.a; // Retrieving the value.
FCS&E, GIK Institute Topi, Pakistan

Page 43

CS102L: Intensive Programming Lab

Example 4.2:

structlib_books
{
char title[20];
char author[15];
int pages;
float price;
} book1,book2,book3;

Now
Book1.price
is the variable representing the price of book1 and can be treated like any other ordinary variable.
We can use scanf statement to assign values like
cin>>book1.pages;
Or we can assign variables to the members of book1
strcpy(book1.title,basic);
strcpy(book1.author,Balagurusamy);
book1.pages=250;
book1.price=28.50;

Accessing Structure Variables example:


Example 4.1:
int main ()
{
struct Library
{
int ISBN, copies, PYear;
char bookName[30], AuthorName[30], PublisherName[30];
};
Library libraryVariable;
LibraryVariable.ISBN = 1293;
strcpy (libraryVariable.bookName, "Network Security");
strcpy (libraryVariable.AuthorName, "Martin");
strcpy (libraryVariable.PublisherName, "Waley");
libraryVariable.copies = 4;
libraryVariable.PYear = 1998;
FCS&E, GIK Institute Topi, Pakistan

Page 44

CS102L: Intensive Programming Lab

cout<<"ISBN Number: "<<libraryVariable.ISBN<<endl ;


cout<<"Library book Name: "<<libraryVariable.bookName<<endl;
cout<<"Author :"<<libraryVariable.AuthorName<<endl;
cout<<"Publisher : "<<libraryVariable.PublisherName;
cout<<"No. of Copies : "<<libraryVariable.copies;
cout<<"Year : "<<libraryVariable.PYear;
}
Creating multiple structure variables.
void main (void)
{
struct Library
{
int ISBN, copies, PYear;
char bookName[30], AuthorName[30], PublisherName[30];
};
Library libraryVariable1, libraryVariable2, libraryVariable3, libraryVariable4;
Library LibraryArray [4]; // alternative and easiest way
}

Assignment a structure variable to another Structure Variable


The value of a structure variable can be assigned to another structure variable of the same type, e.g:
Library libraryVariable1, libraryVariable2;
strcpy (libraryVariable1.bookName , C Programming);
libraryVariable1.ISBN = 1293;
libraryVariable2 = libraryVariable1;

STRUCTURE WITHIN A STRUCTURE/ NESTED STRUCTURES


Structures can be implemented with functions and arrays. Moreover, structures can be implemented as
the member of other structure. This is termed as structure within structure.
When a structure is decleared as the member of another structue, it is called Structure within a
structure. It is also known as nested structure.

Accessing Structure in Structure


cout<<universityVariable.libraryVariable.bookName;
cout<<universityVariable.city;
cout<<universityVariable.libraryVariable.bookName);
cout<<universityVariable.libraryVariable.ISBN);
Common Errors in Accessing Structures

FCS&E, GIK Institute Topi, Pakistan

Page 45

CS102L: Intensive Programming Lab

struct x
{
int a;
int b;
int c;
};
main()
{
struct x z;
z.a = 10;
z.a++;
cout<<" first member is << a; // Error!
// a is not a variable. It is only the name of a member in a structure
cout<<first member is "<<x.a; // Error!
// x is not the name of a variable. It is the name of a type
}
Arrays of structures
It is possible to define a array of structures for example if we are maintaining information of all the
students in the college and if 100 students are studying in the college. We need to use an array than
single variables. We can define an array of structures as shown in the following example:
structure information
{
intid_no;
char name[20];
char address[20];
char combination[3];
int age;
}
student[100];
An array of structures can be assigned initial values just as any other array can. Remember that
each element is a structure that must be assigned corresponding initial values as illustrated below.
int main (void)
{

struct info

{
intid_no;
char name[20];
char address[20];
int age;
}std[100];
intI,n;
cout<<"Enter the number of students "<<endl;
FCS&E, GIK Institute Topi, Pakistan

Page 46

CS102L: Intensive Programming Lab

cin>>n;
cout<<endl<<"Enter Id_no,name address combination agem"<<endl;
for(I=0;I <n;I++)
{
cout<<endl<<"\tEnter Record for student "<<I+1<<endl;
cout<<"Student ID:"<<endl;
cin>>std[I].id_no;
cout<<"Student Name: "<<endl;
cin>>std[I].name;
cout<<"Student city: "<<endl;
cin>>std[I].address;
cout<<"Student Age: "<<endl;
cin>>std[I].age;
}
cout<<endl<<"Student information"<<endl;
for (I=0;I<n;I++)
{ cout<<endl<<"Student ID:\n\t"<<std[I].id_no<<endl;
cout<<"Student Name: "<<std[I].name<<endl;
cout<<"Student city: "<<std[I].address<<endl;
cout<<"Student Age: "<<std[I].age;
}
}
Note:
Before doing your lab exercises run the examples.
Exercises will be provided by the instructors in the lab.

FCS&E, GIK Institute Topi, Pakistan

Page 47

CS102L: Intensive Programming Lab

Lab 5:
Structures II
Structures and functions
Pointers to Structure
Enumerations
Bitwise Operations

Structures and functions


A structure can be passed as a function argument just like any other variable. This raises a few
practical issues.
Where we wish to modify the value of members of the structure, we must pass a pointer to that
structure. This is just like passing a pointer to an int type argument whose value we wish to change.
If we are only interested in one member of a structure, it is probably simpler to just pass that
member. This will make for a simpler function, which is easier to re-use. Of course if we wish to
change the value of that member, we should pass a pointer to it.
When a structure is passed as an argument, each member of the structure is copied. This can prove
expensive where structures are large or functions are called frequently. Passing and working with
pointers to large structures may be more efficient in such cases

Example 5.1:
//Pass 'struct' elements to a function.
int main ()
{
struct { char name[20];
int age;
} record;
strcpy(record.name, "Joe Brown");
record.age = 21;
display (record.name, record.age);
return 0;
}
void display(char *name, int age)
{
cout<<"Name is "<<name <<"\nAge is "<<age;
}

Example 5.2:

FCS&E, GIK Institute Topi, Pakistan

Page 48

CS102L: Intensive Programming Lab

// Purpose: Passing a copy of a structure to a function.


#include<iostream>
struct record_format { char name[20]; int age;};
void display(struct record_format);
using namespace std;
int main ()
{
struct record_format record;
strcpy(record.name, "Joe Brown");
record.age = 21;
display (record);
cout<<"name is "<<record.name<<"\nAge is "<<record.age;
return 0;
}
void display(struct record_format rec)
{cout<<"name is "<<rec.name<<"\nAge is " <<rec.age;
rec.age=31;

/* change the value of a structure


* member.

*/

Pointers to Structure
As we have learnt a memory location can be accessed by a pointer variable. In the similar way a
structure is also accessed by its pointer. The syntax for structure pointer is same as for the ordinary
pointer variable. In general, the structure pointer is defined by the statement struct-type *sptr;
where struct-type refers to structure type specifier, and sptr ia a variable that points to the
structure. It must be ensured that the pointer definition must preceed the structure declaration. For
example, a pointer to struct employee may be defined by the statement struct employee *sptr; In
other words the variable sptr can hold the address value for a structure of type struct employee.
We can create several pointers using a single declaration, as follows:
Struct employee *sptr1, *sptr2, *sptr3;
We have a structure:
struct employee{

char name[30];
int age;
float salary;

};
We define its pointer and variable as follow:
struct employee *sptr1, emp1;

FCS&E, GIK Institute Topi, Pakistan

Page 49

CS102L: Intensive Programming Lab

A pointer to a structure must be initialized before it can be used anywhere in program. The address
of a structure variable can be obtained by applying the address operator & to the variable. For
example, the statement sptr1 = &emp1;

Accessing Structure Members Using Arrow Operator


The members of a structure can be accessed using an arrow operator. The arrow operator->
(consisting of minus sign (-) followed by the greater than (>) symbol). Using the arrow
operator a structure member can be accessed by the expression sptr1->member-name Where sptr
holds the address of a structure variable, and member-name is the name of a member belonging to
the structure. For example, the values held by the members belonging to the structure emp1 are
given by the expression:
sptr1->name;
sptr1->age;
sptr1->salary;
Using the pointers the members of the structure variable emp1 can be initialized asfollow:
cout<<"\nEnter Name:";
gets(sptr1->name);
cout<<"\nEnter age:";
cin>>sptr1->age;
cout<<"\nEnter Salary:";
cin>>sptr1->salary;
//Printing Structure fields
cout<<"\nYou have entered:\n";
cout<<sptr1->name<<endl;
cout<<"\t"<<sptr1->age<<endl;
cout<<"\t"<<sptr1->salary<<endl;

Enumerations
An enumeration provides context to describe a range of values. The following example shows an
enumeration that contains the four suits in a deck of cards.
enum Suit { Diamonds, Hearts, Clubs, Spades };
Every name of the enumeration becomes an enumerator and is assigned a value that corresponds
to its place in the order of the values in the enumeration. By default, the first value is assigned 0, the
next one is assigned 1, and so on. You can set the value of an enumerator.
enum Suit { Diamonds = 1,
Hearts,
Clubs,
Spades };
The enumerator Diamonds is assigned the value 1. This affects the values that are assigned to
subsequent enumerators; Hearts is assigned the value 2, Clubs is 3, and so on.

FCS&E, GIK Institute Topi, Pakistan

Page 50

CS102L: Intensive Programming Lab

Example 5.3:
#include<iostream>
#include<conio.h>
using namespace std;
int main()
{
enum Days{Monday=13,Tuesday=8,Wednesday,Thursday};
Days d1=Wednesday;
cout<<d1;
cout<<endl;
int x;
x=int( d1+1);
cout<<x;
getch();
return 0;
}

Bitwise Operations
~

complement

Bit n of ~x is the opposite of bit n of x

&

Bitwise And

Bit n of x&y is 1 if bit n of x and bit n of y is 1.

Bitwise Or

Bit n of x|y is 1 if bit n of x or bit n of y is 1.

Bitwise Exclusive Or

Bit n of x^y is 1 if bit n of x or bit n of y is 1 but not if both are 1.

>> Right Shift (divide by 2)

Bit n of x>>s is bit n-s of x.

<< Left Shift (multiply by 2) Bit n of x<<s is bit n+s of x.

Bitwise Complement: The bitwise complement operator, the tilde, ~, flips every bit. The tilde is
sometimes called a twiddle, and the bitwise complement twiddles every bit: This turns out to be a
great way of finding the largest possible value for an unsigned number.
unsigned int max = ~0;
Bitwise AND: The bitwise AND operator is a single ampersand: &:
01001000 &
10111000 =
---------------00001000
FCS&E, GIK Institute Topi, Pakistan

Page 51

CS102L: Intensive Programming Lab

Bitwise OR: The bitwise OR operator is a |:


01001000 |
10111000 =
----------------11111000
Bitwise Exclusive OR (XOR): The exclusive-or operation takes two inputs and returns a 1 if either one or
the other of the inputs is a 1, but not if both are. That is, if both inputs are 1 or both inputs are 0, it
returns 0. Bitwise exclusive-or, with the operator of a carrot, ^, performs the exclusive-or operation on
each pair of bits. Exclusive-or is commonly abbreviated XOR.
01110010 ^
10101010
-------------11011000
Suppose, we have some bit, either 1 or 0, that we'll call Z. When we take Z XOR 0, then we always get Z
back: if Z is 1, we get 1, and if Z is 0, we get 0. On the other hand, when we take Z XOR 1, we flip Z. If Z is
0, we get 1; if Z is 1, we get 0:

myBits ^ 0 : No change
myBits ^ 1 : Flip

Example 5.4:
#include <iostream>
#include <iomanip>
using namespace std;
void binary(unsigned int u)
{
int upper;
if(u < 256)
upper = 128;
else
upper = 32768;
cout << setw(5) << u << ": ";
// check if bit is set starting from the highest bit
// (ex) upper = 128, 10000000, 01000000, 00100000, ..., 00000001
for(int i = upper; i > 0; i = i/2) {
if(u & i)
cout << "1 ";
else
cout << "0 ";
FCS&E, GIK Institute Topi, Pakistan

Page 52

CS102L: Intensive Programming Lab

}
cout << "\n";
}
int main()
{
binary(5);
binary(55);
binary(255);
binary(4555);
binary(14555);
system("PAUSE");
return 0;
}

Example 5.5:
#include <iostream>
#include <iomanip>
#include <bitset>
using namespace std;
void binary(unsigned int u)
{
cout << setw(5) << u << ": ";
cout << bitset<16>((int)u);
cout << "\n";
}
int main()
{
binary(5);
binary(55);
binary(255);
binary(4555);
binary(14555);
system("PAUSE");
return 0;
}

Note: Before doing your lab exercises run the examples.


Exercises will be provided by the instructors in the lab.

FCS&E, GIK Institute Topi, Pakistan

Page 53

CS102L: Intensive Programming Lab

FCS&E, GIK Institute Topi, Pakistan

Page 54

CS102L: Intensive Programming Lab

Data Types, Objects, Classes and Instances


Lab 6:
Object Orientation in C++ I
Data Types, Objects, Classes and Instances
So far, we've learnt that C++ lets you create variables which can be any of a range of basic data types:
int, long, double and so on. However, the variables of the basic types don't allow you to model realworld objects (or even imaginary objects) adequately. It's hard to model a box in terms of an int, for
example; what we need is something that allows us to collect together the various attributes of an
object. In C++, we can do this very easily using classes.
You could define variables, length, breadth and height to represent the dimensions of the box and bind
them together as members of a Box class, as follows:
class Box
{
public:
double length;
double breadth;
double height;
};
This code actually creates a new type, called Box. The keyword class defines Box as such, and the
elements that make up this class are defined within the curly braces. Note that each line defining an
element of the class is terminated by a semicolon, and that a semicolon also appears after the closing
brace. The elements length, breadth and height are referred to as data members. At the top of the class
definition, you can see we have put the keyword public - this just means that the data members are
generally accessible from outside the class. You may, however, place restrictions on the accessibility of
class members, and we'll see how this works a bit later in this chapter.
With this definition of a new data type called Box, you can go ahead and define variables of this type just
as you did with variables of the basic types:
Box myBox;
//Declare a variable myBox of type Box
Once we've defined the class Box, the declaration of variables of this type is quite standard. The variable
myBox here is also referred to as an object or an instance of the class Box.
With this definition of a new data type called Box, you can go ahead and define variables of this type just
as you did with variables of the basic types. You can then create, manipulate and destroy as many Box
objects as you need to in your program. This means that you can model objects using classes and write
your programs around them. So - that's object-oriented programming all wrapped up then?
Well, not quite. You see, object-oriented programming (OOP) is based on a number of foundations
(famously encapsulation, polymorphism and inheritance) and what we have seen so far doesn't quite fit
the bill. Don't worry about what these terms mean for the moment - we'll be exploring these ideas in
more detail as we learn more about what you can do with classes.

First Class
The notion of class was invented by an Englishman to keep the general population happy. It derives from
the theory that people who knew their place and function in society would be much more secure and
comfortable in life than those who did not. The famous Dane, Bjarne Stroustrup, who invented C++,

FCS&E, GIK Institute Topi, Pakistan

Page 55

CS102L: Intensive Programming Lab

undoubtedly acquired a deep knowledge of class concepts while at Cambridge University in England,
and appropriated the idea very successfully for use in his new language.
The idea of a class in C++ is similar to the English concept, in that each class usually has a very precise
role and a permitted set of actions. However, it differs from the English idea, because class in C++ has
largely socialist overtones, concentrating on the importance of working classes. Indeed, in some ways it
is the reverse of the English ideal, because, as we shall see, working classes in C++ often live on the
backs of classes that do nothing at all.

Operations on Classes
In C++ you can create new data types as classes to represent whatever kinds of objects you like. As you'll
come to see, classes aren't limited to just holding data; you can also define member functions that act
on your objects, or even operations that act between objects of your classes using the standard C++
operators. You can define the class Box, for example, so that the following statements work and have
the meanings you want them to have:
Box Box1;
Box Box2;
if(Box1 > Box2)
// Fill the larger box
Box1.Fill();
else
Box2.Fill();
You could also implement operations as part of the Box class for adding, subtracting or even multiplying
boxes - in fact, almost any operation to which you could ascribe a sensible meaning in the context of
boxes.
We're talking about incredibly powerful medicine here and it constitutes a major change in the
approach that we can take to programming. Instead of breaking down a problem in terms of what are
essentially computer-related data types (integer numbers, floating point numbers and so on) and then
writing a program, we're going to be programming in terms of problem-related data types, in other
words classes. These classes might be named Employee, or Cowboy, or Cheese or Chutney, each defined
specifically for the kind of problem that you want to solve, complete with the functions and operators
that are necessary to manipulate instances of your new types.
Program design now starts with deciding what new application-specific data types you need to solve the
problem in hand and writing the program in terms of operations on the specifics that the problem is
concerned with, be it Coffins or Cowpokes.

Terminology
Let's summarize some of the terminology that we will be using when discussing classes in C++:
A class is a user-defined data type

Object-oriented programming is the programming style based on the idea of defining your own
data types as classes

Declaring an object of a class is sometimes referred to as instantiation because you are creating
an instance of a class

Instances of a class are referred to as objects

The idea of an object containing the data implicit in its definition, together with the functions
that operate on that data, is referred to as encapsulation
When we get into the detail of object-oriented programming, it may seem a little complicated in places,
but getting back to the basics of what you're doing can often help to make things clearer, so always keep
in mind what objects are really about. They are about writing programs in terms of the objects that are
FCS&E, GIK Institute Topi, Pakistan

Page 56

CS102L: Intensive Programming Lab

specific to the domain of your problem. All the facilities around classes in C++ are there to make this as
comprehensive and flexible as possible. Let's get down to the business of understanding classes.

Understanding Classes
A class is a data type that you define. It can contain data elements, which can either be variables of the
basic types in C++ or other user-defined types. The data elements of a class may be single data
elements, arrays, pointers, arrays of pointers of almost any kind or objects of other classes, so you have
a lot of flexibility in what you can include in your data type. A class can also contain functions which
operate on objects of the class by accessing the data elements that they include. So, a class combines
both the definition of the elementary data that makes up an object and the means of manipulating the
data belonging to individual instances of the class.
The data and functions within a class are called members of the class. Funnily enough, the members of a
class that are data items are called data members and the members that are functions are called
function members or member functions. The member functions of a class are also sometimes referred
to as methods, although we will not be using this term.
When you define a class, you define a blueprint for a data type. This doesn't actually define any data,
but it does define what the class name means, that is, what an object of the class will consist of and
what operations can be performed on such an object. It's much the same as if you wrote a description of
the basic type double. This wouldn't be an actual variable of type double, but a definition of how it's
made up and how it operates. To create a variable, you need to use a declaration statement. It's exactly
the same with classes, as you will see.

Defining a Class
Let's look again at the class we started talking about at the start of the chapter - a class of boxes. We
defined the Box data type using the keyword class as follows:
class Box
{
public:
double length;
// Length of a box in inches
double breadth;
// Breadth of a box in inches
double height;
// Height of a box in inches
};
The name that we've given to our class appears following the keyword and the three data members
are defined between the curly braces. The data members are defined for the class using the
declaration statements that we already know and love, and the whole class definition is terminated
with a semicolon. The names of all the members of a class are local to a class. You can therefore use
the same names elsewhere in a program without causing any problems.

Access Control in a Class


The keyword public looks a bit like a label, but in fact it is more than that. It determines the
access attributes of the members of the class that follow it. Specifying the data members as public
means that these members of an object of the class can be accessed anywhere within the scope of
the class object. You can also specify the members of a class as private or protected. In fact, if
you omit the access specification altogether, the members have the default attribute, private. We
shall look into the effect of these keywords in a class definition a bit later.
Remember that all we have defined so far is a class, which is a data type. We haven't declared any
objects of the class. When we talk about accessing a class member, say height, we're talking about
accessing the data member of a particular object, and that object needs to be declared somewhere.

Declaring Objects of a Class


We declare objects of a class with exactly the same sort of declaration that we use to declare objects
of basic types. We saw this at the beginning of the chapter. So, we could declare objects of our class,
Box, with these statements:
FCS&E, GIK Institute Topi, Pakistan

Page 57

CS102L: Intensive Programming Lab

Box Box1;
// Declare Box1 of type Box
Box Box2;
// Declare Box2 of type Box
Both of the objects Box1 and Box2 will, of course, have their own data members. This is illustrated
here:

The object name Box1 embodies the whole object, including its three data members. They are not
initialized to anything, however - the data members of each object will simply contain junk values,
so we need to look at how we can access them for the purpose of setting them to some specific
values.

Accessing the Data Members of a Class


The data members of objects of a class can be referred to using the direct member access
operator (.). So, to set the value of the data member height of the object Box2 to, say, 18.0, we
could write this assignment statement:
Box2.height = 18.0;
// Setting the value of a data member
We can only access the data member in this way, in a function outside the class, because the
member height was specified as having public access. If it wasn't defined as public, this
statement would not compile. We'll see more about this shortly.

Try It Out - Your First Use of Classes


Let's have a go at creating a Box class and setting the values of it's data members. We'll try it out in
the following console application:

Example 6.1:
// Creating and using boxes
#include <iostream>
using namespace std;
class Box
// Class definition at global scope
{
public:
double length; // Length of a box in inches
double breadth; // Breadth of a box in inches
double height; // Height of a box in inches
};
int main(void)
{
Box Box1;
// Declare Box1 of type Box
Box Box2;
// Declare Box2 of type Box
double volume = 0.0; // Store the volume of a box here
Box1.height = 18.0; // Define the values
Box1.length = 78.0; // of the members of
Box1.breadth = 24.0; // the object Box1
Box2.height = Box1.height - 10; // Define Box2
Box2.length = Box1.length/2.0; // members in
Box2.breadth = 0.25*Box1.length; // terms of Box1
FCS&E, GIK Institute Topi, Pakistan

Page 58

CS102L: Intensive Programming Lab

// Calculate volume of Box1


volume = Box1.height * Box1.length * Box1.breadth;
cout << endl
<< "Volume of Box1 = " << volume;
cout << endl
<< "Box2 has sides which total "
<< Box2.height + Box2.length + Box2.breadth
<< " inches.";
cout << endl // Display the size of a box in memory
<< "A Box object occupies "
<< sizeof Box1 << " bytes.";
cout <<endl;
return 0;
}

How It Works
Everything here works as we would have expected from our experience with structures. The definition
of the class appears outside of the function main() and, therefore, has global scope. This enables objects
to be declared in any function in the program and causes the class to show up in the ClassView once the
program has been compiled.
We've declared two objects of type Box within the function main(), Box1 and Box2. Of course, as with
variables of the basic types, the objects Box1 and Box2 are local to main(). Objects of a class obey the
same rules with respect to scope as variables declared as one of the basic types (such as the variable
volume, which is used in this example).
The first three assignment statements set the values of the data members of Box1. We define the values
of the data members of Box2 in terms of the data members of Box1 in the next three assignment
statements.
We then have a statement which calculates the volume of Box1 as the product of its three data
members. This value is then output to the screen. Next, we output the sum of the data members of
Box2 by writing the expression for the sum of the data members directly in the output statement. The
final action in the program is to output the number of bytes occupied by Box1, which is produced by the
operator sizeof.
If you run this program, you should get this output:

The last line shows that the object Box1 occupies 24 bytes of memory, which is a result of it having 3
data members of 8 bytes each. The statement which produced the last line of output could equally well
have been written like this:
cout << endl
// Display the size of a box in memory
<< "A Box object occupies "
<< sizeof (Box) << " bytes.";

FCS&E, GIK Institute Topi, Pakistan

Page 59

CS102L: Intensive Programming Lab

Here, we've used the type name between parentheses, rather than a specific object name - this is
standard syntax for the sizeof operator.
This example has demonstrated the mechanism for accessing the public data members of a class. It also
shows that they can be used in exactly the same way as ordinary variables.

Using Pointers to Objects


As you might expect, we can create a pointer to variable of a Box type. The declaration for this is just
what you might expect:
Box* pBox = &aBox
Assuming we have already declared an object, aBox, of type Box, then this line simply declares a pointer
to type Box and initializes it with the address of aBox. We could now access the members of aBox
through the pointer pBox, using statements like this:
(*pBox).length = 10;
The parenthesis to de-reference the pointer here are essential, since the member access operator takes
precedence over the de-reference operator. Without the parenthesis, we would be attempting to treat
the pointer like an object and to de-reference the member, so the statement would not compile.
This method of accessing a member of the class via a pointer looks rather clumsy. Since this kind of
operation crops up a lot in C++, the language includes a special operator to enable you to express the
same thing in a much more readable and intuitive form. It is called the indirect member access
operator, and it is specifically for accessing members of an object through a pointer. We could use it to
re-write the statement to access the length member of aBox through the pointer pBox:
pBox->length = 10;
The operator looks like a little arrow and is formed from a minus sign followed by the symbol for
'greater than'. It's much more expressive of what's going on, isn't it? We'll be seeing a lot more of this
operator as we go along and learn more about classes. We're now ready to break new ground by taking
a look at member functions of a class.

Member Functions of a Class


A member function of a class is a function that has its definition or its prototype within the class
definition. It operates on any object of the class of which it is a member, and has access to all the
members of a class for that object.

Try It Out - Adding a Member Function to Box


To see how accessing the members of the class works, let's create an example extending the Box class to
include a member function.

Example 6.2:
// Calculating the volume of a box with a member function
#include <iostream>
using namespace std;
class Box
// Class definition at global scope
{
public:
double length;
// Length of a box in inches
double breadth;
// Breadth of a box in inches
double height;
// Height of a box in inches
// Function to calculate the volume of a box
double Volume(void)
{
FCS&E, GIK Institute Topi, Pakistan

Page 60

CS102L: Intensive Programming Lab

return length * breadth * height;


}
};
int main(void)
{
Box Box1;
// Declare Box1 of type Box
Box Box2;
// Declare Box2 of type Box
double volume = 0.0; // Store the volume of a box here
Box1.height = 18.0; // Define the values
Box1.length = 78.0; // of the members of
Box1.breadth = 24.0; // the object Box1
Box2.height = Box1.height - 10; // Define Box2
Box2.length = Box1.length/2.0; // members in
Box2.breadth = 0.25*Box1.length; // terms of Box1
volume = Box1.Volume();
// Calculate volume of Box1
cout << endl
<< "Volume of Box1 = " << volume;
cout << endl
<< "Volume of Box2 = "
<< Box2.Volume();
cout << endl
<< "A Box object occupies "
<< sizeof Box1 << " bytes.";
cout << endl;
return 0;
}

How It Works
The new code that we've added to the class definition is highlighted. It's just the definition of the
function Volume(), which is a member function of the class. It also has the same access attribute as the
data members: public. This is because every class member declared following an access attribute will
have that access attribute, until another one is specified within the class definition. The function
Volume() returns the volume of a Box object as a value of type double. The expression in the return
statement is just the product of the three data members of the class.

There's no need to qualify the names of the class members in any way when accessing them in
member functions. The unqualified member names automatically refer to the members of the
object that is current when the member function is executed.
The member function Volume() is used in the highlighted statements in main(), after initializing the data
members (as in the first example). Using the same name for a variable in main() causes no conflict or
problem. You can call a member function of a particular object by writing the name of the object to be
processed, followed by a period, followed by the member function name. As we noted above, the
function will automatically access the data members of the object for which it was called, so the first use
of Volume() calculates the volume of Box1. Using only the name of a data member will always refer to
the member of the object for which the member function has been called.
The member function is used a second time directly in the output statement to produce the volume of
Box2. If you execute this example, it will produce this output:

FCS&E, GIK Institute Topi, Pakistan

Page 61

CS102L: Intensive Programming Lab

Note that the Box object is still the same number of bytes. Adding a function member to a class doesn't
affect the size of the objects. Obviously, a member function has to be stored in memory somewhere, but
there's only one copy regardless of how many class objects have been declared, and it isn't counted
when the operator sizeof produces the number of bytes that an object occupies.
The names of the class data members in the member function automatically refer to the data members
of the specific object used to call the function, and the function can only be called for a particular object
of the class. In this case, this is done by using the direct member access operator (.) with the name of an
object.

If you try to call a member function without specifying an object name, your program will not
compile.
Positioning a Member Function Definition
A member function definition need not be placed inside the class definition. If you want to put it outside
the class definition, you need to put the prototype for the function inside the class. If we rewrite the
previous class with the function definition outside, then the class definition looks like this:
class Box
// Class definition at global scope
{
public:
double length;
// Length of a box in inches
double breadth; // Breadth of a box in inches
double height;
// Height of a box in inches
double Volume(void); // Member function prototype
};
Now we need to write the function definition, but there has to be some way of telling the compiler that
the function belongs to the class Box. This is done by prefixing the function name with the name of the
class and separating the two with the scope resolution operator, ::, which is formed from two
successive colons. The function definition would now look like this:
// Function to calculate the volume of a box
double Box::Volume(void)
{
return length * breadth * height;
}
It will produce the same output as the last example. However, it isn't exactly the same program. In the
second case, all calls to the function are treated in the way that we're already familiar with. However,
when we defined the function within the definition of the class in Ex6_02.cpp, the compiler implicitly
treated the function as an inline function.

Inline Functions
With an inline function, the compiler tries to expand the code in the body of the function in place of a
call to the function. This avoids much of the overhead of calling the function and, therefore, speeds up
your code. This is illustrated here:

FCS&E, GIK Institute Topi, Pakistan

Page 62

CS102L: Intensive Programming Lab

Of course, the compiler takes care of ensuring that expanding a function inline doesn't cause any
problems with variable names or scope.
The compiler may not always be able to insert the code for a function inline (such as with recursive
functions or functions for which you have obtained an address), but generally it will work. It's best used
for very short, simple functions, such as our function Volume(), because they will execute faster and will
not significantly increase the size of the executable module.
With the function definition outside of the class definition, the compiler treats the function as a normal
function and a call of the function will work in the usual way. However, it's also possible to tell the
compiler that, if possible, we would like the function to be considered as inline. This is done by simply
placing the keyword inline at the beginning of the function header. So, for our function, the definition
would be as follows:
// Function to calculate the volume of a box
inline double Box::Volume(void)
{
return length * breadth * height;
}
With this definition for the function, the program would be exactly the same as the original. This allows
you to put the member function definitions outside of the class definition, if you so choose, and still
retain the speed benefits of inlining.
You can apply the keyword inline to ordinary functions in your program that have nothing to do with
classes and get the same effect. However, remember that it's best used for short, simple functions.
We now need to understand a little more about what happens when we declare an object of a class.
Note:
Before doing your lab exercises run the examples.
Exercises will be provided by the instructors in the lab.

FCS&E, GIK Institute Topi, Pakistan

Page 63

CS102L: Intensive Programming Lab

Lab 7:
Object Orientation in C++ II
Class Constructors
In the previous program, we declared our Box objects, Box1 and Box2, and then laboriously
worked through each of the data members for each object, in order to assign an initial value to
it. This is unsatisfactory from several points of view. First of all, it would be easy to overlook
initializing a data member, particularly in the case of a class which had many more data
members than our Box class. Initializing the data members of several objects of a complex class
could involve pages of assignment statements. The final constraint on this approach arises
when we get to defining data members of a class that don't have the attribute public - we
won't be able to access them from outside the class anyway. There has to be a better way, and
of course there is - it's known as the class constructor.
What is a Constructor?
A class constructor is a special function in a class that is called when a new object of the class is
declared. It therefore provides the opportunity to initialize objects as they are created and to
ensure that data members only contain valid values.
You have no leeway in naming a class constructor - it always has the same name as the class in
which it is defined. The function Box(), for example, is a constructor for our Box class. It also
has no return type. It's wrong to specify a return type for a constructor; you must not even write
it as void. The primary function of a class constructor is to assign initial values to the data
elements of the class, and no return type is necessary or, indeed, permitted.
Try It Out - Adding a Constructor to the Box class
Let's extend our Box class to incorporate a constructor.
Example 7.1:
// Using a constructor
#include <iostream>
using namespace std;
class Box
{
public:
double length;
double breadth;
double height;

// Class definition at global scope


// Length of a box in inches
// Breadth of a box in inches
// Height of a box in inches

// Constructor definition
Box(double lv, double bv, double hv)
{
cout << endl << "Constructor called.";
length = lv;
// Set values of
breadth = bv;
// data members
height = hv;
}
FCS&E, GIK Institute Topi, Pakistan

Page 64

CS102L: Intensive Programming Lab

// Function to calculate the volume of a box


double Volume()
{
return length * breadth * height;
}
};
int main(void)
{
Box Box1(78.0,24.0,18.0);
Box CigarBox(8.0,5.0,1.0);
double volume = 0.0;
volume = Box1.Volume();
cout <<
<<
cout <<
<<
<<
cout <<

//
//
//
//

Declare and initialize Box1


Declare and initialize CigarBox
Store the volume of a box here
Calculate volume of Box1

endl
"Volume of Box1 = " << volume;
endl
"Volume of CigarBox = "
CigarBox.Volume();
endl;

return 0;
}

How It Works
The constructor, Box(), has been written with three parameters of type double, corresponding to
the initial values for the length, breadth and height members of a Box object. The first
statement in the constructor outputs a message so that we can tell when it's been called. You
wouldn't do this in production programs, but, since it's very helpful in showing when a
constructor is called, it's often used when testing a program. We'll use it regularly for the
purposes of illustration. The code in the body of the constructor is very simple. It just assigns the
arguments passed to the corresponding data members. If necessary, we could also include checks
that valid, non-negative arguments are supplied and, in a real context, you probably would want
to do this, but our primary interest here is in seeing how the mechanism works.
Within main(), we declare the object Box1 with initializing values for the data members length,
breadth, and height, in sequence. These are in parentheses following the object name. This
uses the functional notation for initialization, which can also be applied to initializing ordinary
variables of basic types. We also declare a second object of type Box, called CigarBox, which
also has initializing values.
The volume of Box1 is calculated using the member function Volume() as in the previous
example and is then displayed on the screen. We also display the value of the volume of
CigarBox. The output from the example is:

The first two lines are output from the two calls of the constructor, Box(), once for each object
declared. The constructor that we've supplied in the class definition is automatically called when
a Box object is declared, so both Box objects are initialized with the initializing values appearing
in the declaration. These are passed to the constructor as arguments, in the sequence that they are
written in the declaration. As you can see, the volume of Box1 is the same as before and
FCS&E, GIK Institute Topi, Pakistan

Page 65

CS102L: Intensive Programming Lab

CigarBox

has a volume looking suspiciously like the product of its dimensions, which is quite a

relief.
The Default Constructor
Try modifying the last example by adding the declaration for Box2 that we had previously:
Box Box2;

// Declare Box2 of type Box

Here, we've left Box2 without initializing values. When you rebuild this version of the program,
you'll get the error message:
error C2512: 'Box': no appropriate default constructor available
This means that the compiler is looking for a default constructor for Box2, either one that needs
no arguments, because none are specified in the constructor definition, or one whose arguments
are all optional, because we haven't supplied any initializing values for the data members. Well,
this statement was perfectly satisfactory in Ex6_02.cpp, so why doesn't it work now?
The answer is that the previous example used a default constructor that was supplied by the
compiler, because we didn't supply one. Since in this example we did supply a constructor, the
compiler assumed that we were taking care of everything and didn't supply the default. So, if you
still want to use declarations for Box objects which aren't initialized, you have to include the
default constructor yourself. What exactly does the default constructor look like? In the simplest
case, it's just a constructor that accepts no arguments, it doesn't even need to do anything:
Box()
{}

// Default constructor
// Totally devoid of statements

Try It Out - Supplying a Default Constructor


Let's add our version of the default constructor to the last example, along with the declaration for
Box2, plus the original assignments for the data members of Box2. We must enlarge the default
constructor just enough to show that it is called. Here is the next version of the program:
// Supplying and using a default constructor
#include <iostream >
using namespace std;
class Box
{
public:
double length;
double breadth;
double height;

// Class definition at global scope


// Length of a box in inches
// Breadth of a box in inches
// Height of a box in inches

// Constructor definition
Box(double lv, double bv, double hv)
{
cout << endl << "Constructor called.";
length = lv;
// Set values of
breadth = bv;
// data members
height = hv;
}
// Default constructor definition
Box()
{ cout << endl << "Default constructor called."; }
// Function to calculate the volume of a box
double Volume()
{
FCS&E, GIK Institute Topi, Pakistan

Page 66

CS102L: Intensive Programming Lab

return length * breadth * height;


}
};
int main(void)
{
Box Box1(78.0,24.0,18.0);
Box Box2;
Box CigarBox(8.0,5.0,1.0);
double volume = 0.0;
volume = Box1.Volume();

//
//
//
//
//

Declare and initialize Box1


Declare Box2 - no initial values
Declare and initialize CigarBox
Store the volume of a box here
Calculate volume of Box1

cout << endl


<< "Volume of Box1 = " << volume;
Box2.height = Box1.height - 10;
Box2.length = Box1.length/2.0;
Box2.breadth = 0.25*Box1.length;

// Define Box2
// members in
// terms of Box1

cout << endl


<< "Volume of Box2 = "
<< Box2.Volume();
cout <<
<<
<<
cout <<

endl
"Volume of CigarBox = "
CigarBox.Volume();
endl;

return 0;
}

How It Works
Now that we have included our own version of the default constructor, there are no error
messages from the compiler and everything works. The program produces this output:

All that our default constructor does is to display a message. Evidently, it was called when we
declared the object Box2. We also get the correct value for the volumes of all three Box objects,
so the rest of the program is working as it should.
One aspect of this example that you may have noticed is that we now know we can overload
constructors just as we overloaded functions. We've just run an example with two constructors
that differ only in their parameter list. One has three parameters of type double and the other has
no parameters at all.
Assigning Default Values in a Constructor
When we discussed functions in C++, we saw how we could specify default values for the
parameters to a function in the function prototype. We can also do this for class member
functions, including constructors. If we put the definition of the member function inside the class
definition, we can put the default values for the parameters in the function header. If we only

FCS&E, GIK Institute Topi, Pakistan

Page 67

CS102L: Intensive Programming Lab

include the prototype of a function in the class definition, the default parameter value should go
in the prototype.
If we decided that the default size for a Box object was a unit box with all sides of length 1, we
could alter the class definition in the last example to this:
class Box
{
public:
double length;
double breadth;
double height;

// Class definition at global scope


// Length of a box in inches
// Breadth of a box in inches
// Height of a box in inches

// Constructor definition
Box(double lv = 1.0, double bv = 1.0, double hv = 1.0)
{
cout << endl << "Constructor called.";
length = lv;
// Set values of
breadth = bv;
// data members
height = hv;
}
// Default constructor definition
Box()
{ cout << endl << "Default constructor called."; }
// Function to calculate the volume of a box
double Volume()
{
return length * breadth * height;
}
};

If we make this change to the last example, what happens? We get another error message from
the compiler, of course. We get these useful comments:
warning C4520: 'Box': multiple default constructors specified
error C2668: 'Box::Box': ambiguous call to overloaded function
This means that the compiler can't work out which of the two constructors to call - the one for
which we have set default values for the parameters or the constructor that doesn't accept any
parameters. This is because the declaration of Box2 requires a constructor without parameters,
and either constructor can now be called in this case. The immediately obvious solution to this is
to get rid of the constructor that accepts no parameters. This is actually beneficial. Without this
constructor, any Box object that is declared without being explicitly initialized will automatically
have its members initialized to 1.
Try It Out - Supplying Default Values for Constructor Arguments
We can demonstrate this with the following simplified example:
Example 7.2:
// Supplying default values for constructor arguments
#include <iostream>
using namespace std;
class Box
{
public:
double length;
double breadth;

// Class definition at global scope


// Length of a box in inches
// Breadth of a box in inches

FCS&E, GIK Institute Topi, Pakistan

Page 68

CS102L: Intensive Programming Lab

double height;

// Height of a box in inches

// Constructor definition
Box(double lv=1.0, double bv=1.0, double hv=1.0)
{
cout << endl << "Constructor called.";
length = lv;
// Set values of
breadth = bv;
// data members
height = hv;
}
// Function to calculate the volume of a box
double Volume()
{
return length * breadth * height;
}
};
int main(void)
{
Box Box2;
// Declare Box2 - no initial values
cout << endl
<< "Volume of Box2 = "
<< Box2.Volume();
cout << endl;
return 0;
}

How It Works
We only declare a single uninitialized Box variable, Box2, because that's all we need for
demonstration purposes. This version of the program produces the following output:

This shows that the constructor with default parameter values is doing its job of setting the
values of objects that have no initializing values specified.
You shouldn't assume from the above example that this is the only, or even the recommended,
way of implementing the default constructor. There will be many occasions where you won't
want to assign default values in this way, in which case you'll need to write a separate default
constructor. There will even be times when you don't want to have a default constructor
operating at all, even though you have defined another constructor. This would ensure that all
declared objects of a class must have initializing values explicitly specified in their declaration.

Using an Initialization List in a Constructor


Previously, we initialized the members of an object in the class constructor using explicit
assignment. We could also have used a different technique, using what is called an initialization
list. We can demonstrate this with an alternative version of the constructor for the class Box:
// Constructor definition using an initialization list
Box(double lv=1.0, double bv=1.0, double hv=1.0): length(lv),
breadth(bv),
height(hv)
{
FCS&E, GIK Institute Topi, Pakistan

Page 69

CS102L: Intensive Programming Lab

cout << endl << "Constructor called.";


}

Now the values of the data members are not set in assignment statements in the body of the
constructor. As in a declaration, they are specified as initializing values using functional notation
and appear in the initializing list as part of the function header. The member length is initialized
by the value of lv, for example. This can be rather more efficient than using assignments as we
did in the previous version. If you substitute this version of the constructor in the previous
example, you will see that it works just as well.
Note that the initializing list for the constructor is separated from the parameter list by a colon
and that each of the initializers is separated by a comma. This technique for initializing
parameters in a constructor is important, because, as we shall see later, it's the only way of
setting values for certain types of data members of an object.

Private Members of a Class


Having a constructor that sets the values of the data members of a class object, but still admits
the possibility of any part of a program being able to mess with what are essentially the guts of
an object, is almost a contradiction in terms. To draw an analogy, once you have arranged for a
brilliant surgeon such as Dr. Kildare, whose skills were honed over years of training, to do things
to your insides, letting the local plumber, bricklayer or the folks from Hill Street Blues have a go
hardly seems appropriate. We need some protection for our class data members.
We can get it by using the keyword private when we define the class members. Class members
which are private can, in general, only be accessed by member functions of a class. There's one
exception, but we'll worry about that later. A normal function has no direct means of accessing
the private members of a class. This is shown here:

Having the possibility of specifying class members as private also enables you to separate the
interface to the class from its internal implementation. The interface to a class is composed of the
public members and the public member functions in particular, since they can provide indirect
access to all the members of a class, including the private members. By keeping the internals of
a class private, you can later modify them to improve performance, for example, without
necessitating modifications to the code that uses the class through its public interface. To keep
them safe from unnecessary meddling, it's good practice to declare data and function members of
a class that don't need to be exposed as private. Only make public what is essential to the use
of your class.
Try It Out - Private Data Members
We can rewrite the Box class to make its data members private.
// A class with private members
FCS&E, GIK Institute Topi, Pakistan

Page 70

CS102L: Intensive Programming Lab

#include <iostream>
using namespace std;
class Box
// Class definition at global scope
{
public:
// Constructor definition
Box(double lv=1.0, double bv=1.0, double hv=1.0)
{
cout << endl << "Constructor called.";
length = lv;
// Set values of
breadth = bv;
// data members
height = hv;
}
// Function to calculate the volume of a box
double Volume()
{
return length * breadth * height;
}
private:
double length;
double breadth;
double height;

// Length of a box in inches


// Breadth of a box in inches
// Height of a box in inches

};
int main(void)
{
Box Match(2.2, 1.1, 0.5); // Declare Match box
Box Box2;
// Declare Box2 - no initial values
cout << endl
<< "Volume of Match = "
<< Match.Volume();
// Uncomment the following line to get an error
// Box2.length = 4.0;
cout <<
<<
<<
cout <<

endl
"Volume of Box2 = "
Box2.Volume();
endl;

return 0;
}

How It Works
The definition of the class Box now has two sections. The first is the public section containing
the constructor and the member function Volume(). The second section is specified as private
and contains the data members. Now the data members can only be accessed by the member
functions of the class. We don't have to modify any of the member functions - they can access all
the data members of the class anyway. However, if you uncomment the statement in the function
main(), assigning a value to the member length of the object Box2, you'll get a compiler error
message confirming that the data member is inaccessible.
A point to remember is that using a constructor or a member function is now the only way to get
a value into a private data member of an object. You have to make sure that all the ways in
which you might want to set or modify private data members of a class are provided for through
member functions.
FCS&E, GIK Institute Topi, Pakistan

Page 71

CS102L: Intensive Programming Lab

We could also put functions into the private section of a class. In this case, they can only be
called by other member functions. If you put the function Volume() in the private section, you
will get a compiler error from the statements that attempt to use it in the function main(). If you
put the constructor in the private section, you won't be able to declare any members of the
class.
The above example generates this output:

This demonstrates that the class is still working satisfactorily, with its data members defined as
having the access attribute private. The major difference is that they are now completely
protected from unauthorized access and modification.
If you don't specify otherwise, the default access attribute which applies to members of a class is
private. You could, therefore, put all your private members at the beginning of the class
definition and let them default to private by omitting the keyword. However, it's better to take the
trouble to explicitly state the access attribute in every case, so there can be no doubt about what
you intend.
Of course, you don't have to make all your data members private. If the application for your
class requires it, you can have some data members defined as private and some as public. It
all depends on what you're trying to do. If there's no reason to make members of a class public,
it is better to make them private as it makes the class more secure. Ordinary functions won't be
able to access any of the private members of your class.

Accessing private Class Members


On reflection, declaring all the data members of a class as private might seem rather extreme.
It's all very well protecting them from unauthorized modification, but that's no reason to keep
their values a secret. What we need is a Freedom of Information Act for private members.
You don't need to start writing to your state senator to get it - it's already available to you. All
you need to do is to write a member function to return the value of a data member. Look at this
member function for the class Box:
inline double Box::GetLength(void)
{
return length;
}

Just to show how it looks, this has been written as a member function definition which is external
to the class. We've specified it as inline, since we'll benefit from the speed increase, without
increasing the size of our code too much. Assuming that you have the declaration of the function
in the public section of the class, you can use it by writing this statement:
len = Box2.GetLength();

// Obtain data member length

All you need to do is to write a similar function for each data member that you want to make
available to the outside world, and their values can be accessed without prejudicing the security
of the class. Of course, if you put the definitions for these functions within the class definition,
they will be inline by default.

The Default Copy Constructor


Suppose we declare and initialize a Box object Box1 with this statement:
FCS&E, GIK Institute Topi, Pakistan

Page 72

CS102L: Intensive Programming Lab

Box Box1(78.0, 24.0, 18.0);

We now want to create another Box object, identical to the first. We would like to initialize the
second Box object with Box1.

Try It Out - Copying Information Between Instances


We can try this out with the main() function which follows:
Example 7.3:
// Initializing an object with an object of the same class
#include <iostream>
using namespace std;
class Box
// Class definition at global scope
{
public:
// Constructor definition
Box(double lv=1.0, double bv=1.0, double hv=1.0)
{
cout << endl << "Constructor called.";
length = lv;
// Set values of
breadth = bv;
// data members
height = hv;
}
// Function to calculate the volume of a box
double Volume()
{
return length * breadth * height;
}
private:
double length;
double breadth;
double height;

// Length of a box in inches


// Breadth of a box in inches
// Height of a box in inches

};
int main(void)
{
Box Box1(78.0, 24.0, 18.0);
Box Box2 = Box1;
// Initialize Box2 with Box1
cout <<
<<
<<
<<

endl
"Box1 volume = " << Box1.Volume()
endl
"Box2 volume = " << Box2.Volume();

cout << endl;


return 0;
}

How It Works
This example will produce this:

FCS&E, GIK Institute Topi, Pakistan

Page 73

CS102L: Intensive Programming Lab

Clearly, the program is working as we would want, with both boxes having the same volume.
However, as you can see from the output, our constructor was called only once for the creation
of Box1. But how was Box2 created? The mechanism is similar to the one that we experienced
when we had no constructor defined and the compiler supplied a default constructor to allow an
object to be created. In this case, the compiler generates a default version of what is referred to as
a copy constructor.
A copy constructor does exactly what we're doing here - it creates an object of a class by
initializing it with an existing object of the same class. The default version of the copy
constructor creates the new object by copying the existing object, member by member.
This is fine for simple classes such as Box, but for many classes - classes that have pointers or
arrays as members for example - it won't work properly. Indeed, with such classes it can create
serious errors in your program. In these cases, you need to create your own class copy
constructor.

Destructor:
A destructor is a special member function of a class that is executed whenever an object of it's
class goes out of scope or whenever the delete expression is applied to a pointer to the object of
that class.
A destructor will have exact same name as the class prefixed with a tilde (~) and it can neither
return a value nor can it take any parameters. Destructor can be very useful for releasing
resources before coming out of the program like closing files, releasing memories etc.
Example 7.4:
#include <iostream>
using namespace std;
class CRectangle {
int width, height;
public:
CRectangle (int a,int b)
{ width = a;
height = b;
}
int area ()
{return (width * height);}
~CRectangle () {
cout<<"deleting object..."<<endl;

FCS&E, GIK Institute Topi, Pakistan

Page 74

CS102L: Intensive Programming Lab

}
};
int main () {
CRectangle rect (3,4);
CRectangle rectb (5,6);
cout << "rect area: " << rect.area() << endl;
cout << "rectb area: " << rectb.area() << endl;
return 0;
}

Note:
Before doing your lab exercises run the examples.
Exercises will be provided by the instructors in the lab.

FCS&E, GIK Institute Topi, Pakistan

Page 75

CS102L: Intensive Programming Lab

Lab 8
Friend Function & Classes, This Pointer, and static Variables
Friend Classes
C++ provides the friend keyword to do just this. Inside a class, you can indicate that other classes (or
simply functions) will have direct access to protected and private members of the class. When granting
access to a class, you must specify that the access is granted for a class using the class keyword:
friend class aClass;
Note that friend declarations can go in either the public, private, or protected section of a class--it
doesn't matter where they appear. In particular, specifying a friend in the section marked protected
doesn't prevent the friend from also accessing private fields.
Here is a more concrete example of declaring a friend:
class Node
{
private:
int data;
int key;
// ...
friend class BinaryTree; // class BinaryTree can now access data directly
};
Now, Node does not need to provide any means of accessing the data stored in the tree. The BinaryTree
class that will use the data is the only class that will ever need access to the data or key. (The BinaryTree
class needs to use the key to order the tree, and it will be the gateway through which other classes can
access data stored in any particular node.)
Now in the BinaryTree class, you can treat the key and data fields as though they were public:
class BinaryTree
{
private:
Node *root;

FCS&E, GIK Institute Topi, Pakistan

Page 76

CS102L: Intensive Programming Lab

int find(int key);


};
int BinaryTree::find(int key)
{
// check root for NULL...
if(root->key == key)
{
// no need to go through an accessor function
return root->data;
}
// perform rest of find

Friend Functions
Similarly, a class can grant access to its internal variables on a more selective basis--for instance,
restricting access to only a single function. To do so, the entire function signature must be replicated
after the friend specifier, including the return type of the function--and, of course, you'll need to give
the scope of the function if it's inside another class:
friend return_type class_name::function(args);
For instance, in our example Node class, we would use this syntax:
class Node
{
private:
int data;
int key;
// ...
friend int BinaryTree::find(); // Only BinaryTree's find function has access
};
Now the find function of BinaryTree could access the internals of the Node class, but no other function
in BinaryTree could. (Though we might want a few others to be able to!)
FCS&E, GIK Institute Topi, Pakistan

Page 77

CS102L: Intensive Programming Lab

Note that when friends are specified within a class, this does not give the class itself access to the friend
function. That function is not within the scope of the class; it's only an indication that the class will grant
access to the function.
Functions which are friends of a class and are defined within the class definition are also by default
inline.

Friend functions are not members of the class, and therefore the access attributes do not apply to
them. They are just ordinary global functions with special privileges.
Let's suppose that we wanted to implement a friend function in the Box class to compute the surface
area of a Box object.

Try It Out - Using a friend to Calculate the Surface Area


We can see how this works in the following example:
Example 8.1
// Creating a friend function of a class
#include <iostream>
using namespace std;
class Box
// Class definition at global scope
{
public:
// Constructor definition
Box(double lv=1.0, double bv=1.0, double hv=1.0)
{
cout << endl << "Constructor called.";
length = lv;
// Set values of
breadth = bv;
// data members
height = hv;
}
// Function to calculate the volume of a box
double Volume()
{
return length * breadth * height;
}
private:
double length;
double breadth;
double height;

// Length of a box in inches


// Breadth of a box in inches
// Height of a box in inches

//Friend function
friend double BoxSurface(Box aBox);
};
// friend function to calculate the surface area of a Box object
double BoxSurface(Box aBox)
{
return 2.0*(aBox.length*aBox.breadth +
aBox.length*aBox.height +
aBox.height*aBox.breadth);
}
int main(void)
{
Box Match(2.2, 1.1, 0.5); // Declare Match box
FCS&E, GIK Institute Topi, Pakistan

Page 78

CS102L: Intensive Programming Lab

Box Box2;

// Declare Box2 - no initial values

cout << endl


<< "Volume of Match = "
<< Match.Volume();
cout << endl
<< "Surface area of Match = "
<< BoxSurface(Match);
cout << endl
<< "Volume of Box2 = "
<< Box2.Volume();
cout <<
<<
<<
cout <<

endl
"Surface area of Box2 = "
BoxSurface(Box2);
endl;

return 0;
}

How It Works
We declare the function BoxSurface() as a friend of the Box class by writing the function prototype with
the keyword friend at the front. Since the BoxSurface() function itself is a global function, it makes no
difference where we put the friend declaration within the definition of the class, but it's a good idea to
be consistent when you position this sort of declaration. You can see that we have chosen to position
ours after all the public and private members of the class. Remember that a friend function isn't a
member of the class, so access attributes don't apply.
The definition of the function follows that of the class. Note that we specify access to the data members
of the object within the definition of BoxSurface(), using the Box object passed to the function as a
parameter. Because a friend function isn't a class member, the data members can't be referenced just
by their names. They each have to be qualified by the object name in exactly the same way as they
might in an ordinary function, except, of course, that an ordinary function can't access the private
members of a class. A friend function is the same as an ordinary function except that it can access all the
members of a class without restriction.
The example produces this output:

This is exactly what you would expect. The friend function is computing the surface area of the Box
objects from the values of the private members.

Placing friend Function Definitions Inside the Class


We could have combined the definition of the function with its declaration as a friend of the Box class
within the class definition - the code would run as before. However, this has a number of disadvantages
relating to the readability of the code. Although the function would still have global scope, this wouldn't
be obvious to readers of the code, since the function would be hidden in the body of the class definition,
and particularly since the function would no longer show up in the ClassView.

FCS&E, GIK Institute Topi, Pakistan

Page 79

CS102L: Intensive Programming Lab

The this pointer


Every object in C++ has access to its own address through an important pointer called this pointer. The
this pointer is an implicit parameter to all member functions. Therefore, inside a member function, this
may be used to refer to the invoking object.
Friend functions do not have a this pointer, because friends are not members of a class. Only member
functions have a this pointer.
Let us try the following example to understand the concept of this pointer:
#include <iostream>
using namespace std;
class Box
{
public:
// Constructor definition
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
}
double Volume()
{
return length * breadth * height;
}
int compare(Box box)
{
return this->Volume() > box.Volume();
}
private:
double length;
double breadth;
double height;

// Length of a box
// Breadth of a box
// Height of a box

};
int main(void)
{
Box Box1(3.3, 1.2, 1.5);

// Declare box1

Box Box2(8.5, 6.0, 2.0);

// Declare box2

if(Box1.compare(Box2))
{
FCS&E, GIK Institute Topi, Pakistan

Page 80

CS102L: Intensive Programming Lab

cout << "Box2 is smaller than Box1" <<endl;


}
else
{
cout << "Box2 is equal to or larger than Box1" <<endl;
}
return 0;
}

When the above code is compiled and executed, it produces following result:
Constructor called.
Constructor called.
Box2 is equal to or larger than Box1

Static keyword in C++


Static variables keep their values and are not destroyed even after they go out of scope. For example:
int GenerateID()
{
static int s_nID = 0;
return s_nID++;
}
int main()
{
std::cout << GenerateID() << std::endl;
std::cout << GenerateID() << std::endl;
std::cout << GenerateID() << std::endl;
return 0;
}

This program prints:


0
1
2

Note that s_nID has kept its value across multiple function calls.

FCS&E, GIK Institute Topi, Pakistan

Page 81

CS102L: Intensive Programming Lab

The static keyword has another meaning when applied to global variables it changes them
from global scope to file scope. Because global variables are typically avoided by competent
programmers, and file scope variables are just global variables limited to a single file, the static
keyword is typically not used in this capacity.

Static member variables


When we instantiate a class object, each object gets its own copy of all normal member variables.
Member variables of a class can be made static by using the static keyword. Static member variables
only exist once in a program regardless of how many class objects are defined! One way to think about it
is that all objects of a class share the static variables. Consider the following program:
class Something
{
public:
static int s_nValue;
};
int Something::s_nValue = 1;
int main()
{
Something cFirst;
cFirst.s_nValue = 2;
Something cSecond;
std::cout << cSecond.s_nValue;
return 0;
}

This program produces the following output:


2

Because s_nValue is a static member variable, s_nValue is shared between all objects of the class.
Consequently, cFirst.s_nValue is the same as cSecond.s_nValue. The above program shows that the
value we set using cFirst can be accessed using cSecond!
Although you can access static members through objects of the class type, this is somewhat misleading.
cFirst.s_nValue implies that s_nValue belongs to cFirst, and this is really not the case. s_nValue does not
belong to any object. In fact, s_nValue exists even if there are no objects of the class have been
instantiated!

FCS&E, GIK Institute Topi, Pakistan

Page 82

CS102L: Intensive Programming Lab

Consequently, it is better to think of static members as belonging to the class itself, not the objects of
the class. Because s_nValue exists independently of any class objects, it can be accessed directly using
the class name and the scope operator:
class Something
{
public:
static int s_nValue;
};
int Something::s_nValue = 1;
int main()
{
Something::s_nValue = 2;
std::cout << Something::s_nValue;
return 0;
}

In the above snippet, s_nValue is referenced by class name rather than through an object. Note that we
have not even instantiated an object of type Something, but we are still able to access and use
Something::s_nValue. This is the preferred method for accessing static members.

Initializing static member variables


Because static member variables are not part of the individual objects, you must explicitly define the
static member if you want to initialize it to a non-zero value. The following line in the above example
initializes the static member to 1:
int Something::s_nValue = 1;

This initializer should be placed in the code file for the class (eg. Something.cpp). In the absense of an
initializing line, C++ will initialize the value to 0.

An example of static member variables


Why use static variables inside classes? One great example is to assign a unique ID to every instance of
the class. Heres an example of that:
class Something
{
private:
static int s_nIDGenerator;
int m_nID;
public:
Something() { m_nID = s_nIDGenerator++; }
FCS&E, GIK Institute Topi, Pakistan

Page 83

CS102L: Intensive Programming Lab

int GetID() const { return m_nID; }


};
int Something::s_nIDGenerator = 1;
int main()
{
Something cFirst;
Something cSecond;
Something cThird;
using namespace std;
cout << cFirst.GetID() << endl;
cout << cSecond.GetID() << endl;
cout << cThird.GetID() << endl;
return 0;
}

This program prints:


1
2
3

Because s_nIDGenerator is shared by all Something objects, when a new Something object is
created, its constructor grabs the current value out of s_nIDGenerator and then increments
the value for the next object. This guarantees that each Something object receives a unique id
(incremented in the order of creation). This can really help when debugging multiple items in an
array, as it provides a way to tell multiple objects of the same class type apart!
Static member variables can also be useful when the class needs to utilize an internal lookup
table (eg. to look up the name of something, or to find a pre-calculated value). By making the
lookup table static, only one copy exists for all objects, rather than a copy for each object
instantiated. This can save substantial amounts of memory.

Note:
Before doing your lab exercises run the examples.
Exercises will be provided by the instructors in the lab.

FCS&E, GIK Institute Topi, Pakistan

Page 84

CS102L: Intensive Programming Lab

Lab 9
Operator Overloading
Operator Overloading in C++
In C++ the overloading principle applies not only to functions, but to operators too. That is, of operators
can be extended to work not just with built-in types but also classes. A programmer can provide his or
her own operator to a class by overloading the built-in operator to perform some specific computation
when the operator is used on objects of that class. Is operator overloading really useful in real world
implementations? It certainlly can be, making it very easy to write code that feels natural (we'll see
some examples soon). On the other hand, operator overloading, like any advanced C++ feature, makes
the language more complicated. In addition, operators tend to have very specific meaning, and most
programmers don't expect operators to do a lot of work, so overloading operators can be abused to
make code unreadable. But we won't do that.

An Example of Operator Overloading


Complex a(1.2,1.3); //this class is used to represent complex numbers
Complex b(2.1,3);
//notice the construction taking 2 parameters for the real and imaginary part
Complex c = a+b;
//for this to work the addition operator must be overloaded
The addition without having overloaded operator + could look like this:
Complex c = a.Add(b);
This piece of code is not as readable as the first example though--we're dealing with numbers, so doing
addition should be natural. (In contrast to cases when programmers abuse this technique, when the
concept represented by the class is not related to the operator--ike using + and - to add and remove
elements from a data structure. In this cases operator overloading is a bad idea, creating confusion.)
In order to allow operations like Complex c = a+b, in above code we overload the "+" operator. The
overloading syntax is quite simple, similar to function overloading, the keyword operator must be
followed by the operator we want to overload:
class Complex
{
public:
Complex(double re,double im)
:real(re),imag(im)
{};
Complex operator+(const Complex& other);
Complex operator=(const Complex& other);
private:
double real;
double imag;
};
Complex Complex::operator+(const Complex& other)
{
double result_real = real + other.real;
FCS&E, GIK Institute Topi, Pakistan

Page 85

CS102L: Intensive Programming Lab

double result_imaginary = imag + other.imag;


return Complex( result_real, result_imaginary );}

The assignment operator can be overloaded similarly. Notice that we did not have to call any accessor
functions in order to get the real and imaginary parts from the parameter other since the overloaded
operator is a member of the class and has full access to all private data. Alternatively, we could have
defined the addition operator globally and called a member to do the actual work. In that case, we'd
also have to make the method a friend of the class, or use an accessor method to get at the private data:
friend Complex operator+(Complex);
Complex operator+(const Complex &num1, const Complex &num2)
{
double result_real = num1.real + num2.real;
double result_imaginary = num1.imag + num2.imag;
return Complex( result_real, result_imaginary );
}

Why would you do this? when the operator is a class member, the first object in the expression must be
of that particular type. It's as if you were writing:
Complex a( 1, 2 );
Complex a( 2, 2 );
Complex c = a.operator=( b );
when it's a global function, the implicit or user-defined conversion can allow the operator to act even if
the first operand is not exactly of the same type:
Complex c = 2+b;
//if the integer 2 can be converted by the Complex class, this expression is valid
By the way, the number of operands to a function is fixed; that is, a binary operator takes two operands,
a unary only one, and you can't change it. The same is true for the precedence of operators too; for
example the multiplication operator is called before addition. There are some operators that need the
first operand to be assignable, such as : operator=, operator(), operator[] and operator->, so their use is
restricted just as member functions(non-static), they can't be overloaded globally. The operator=,
operator& and operator, (sequencing) have already defined meanings by default for all objects, but their
meanings can be changed by overloading or erased by making them private.
Another intuitive meaning of the "+" operator from the STL string class which is overloaded to do
concatenation:
string prefix("de");
string word("composed");
string composed = prefix+word;

Using "+" to concatenate is also allowed in Java, but note that this is not extensible to other classes, and
it's not a user defined behavior. Almost all operators can be overloaded in C++:
+
~

*
,

&

++

--

<<

>>

==

!=

&&

||

+=

-=

/=

%=

^=

&=

|=

*=

<<=

>>=

[]

()

->

FCS&E, GIK Institute Topi, Pakistan

->*

new

delete

Page 86

CS102L: Intensive Programming Lab

The only operators that can't be overloaded are the operators for scope resolution (::), member
selection (.), and member selection through a pointer to a function(.*). Overloading assumes you specify
a behavior for an operator that acts on a user defined type and it can't be used just with general
pointers. The standard behavior of operators for built-in (primitive) types cannot be changed by
overloading,
that
is,
you
can't
overload
operator+(int,int).
The logic(boolean) operators have by the default a short-circuiting way of acting in expressions with
multiple boolean operations. This means that the expression:
if(a && b && c)
will not evaluate all three operations and will stop after a false one is found. This behavior does not
apply
to
operators
that
are
overloaded
by
the
programmer.
Even the simplest C++ application, like a "hello world" program, is using overloaded operators. This is
due to the use of this technique almost everywhere in the standard library (STL). Actually the most basic
operations in C++ are done with overloaded operators, the IO(input/output) operators are overloaded
versions of shift operators(<<, >>). Their use comes naturally to many beginning programmers, but their
implementation is not straightforward. However a general format for overloading the input/output
operators must be known by any C++ developer. We will apply this general form to manage the
input/output for our Complex class:
friend ostream &operator<<(ostream &out, Complex c)

//output

{
out<<"real part: "<<real<<"\n";
out<<"imag part: "<<imag<<"\n";
return out;
}
friend istream &operator>>(istream &in, Complex &c)
{

//input

cout<<"enter real part:\n";


in>>c.real;
cout<<"enter imag part: \n";
in>>c.imag;
return in;}

Notice the use of the friend keyword in order to access the private members in the above
implementations. The main distinction between them is that the operator>> may encounter unexpected
errors for incorrect input, which will make it fail sometimes because we haven't handled the errors
correctly. A important trick that can be seen in this general way of overloading IO is the returning
reference for istream/ostream which is needed in order to use them in a recursive manner:
Complex a(2,3);
Complex b(5.3,6);
cout<<a<<b;

Note: Before doing your lab exercises run the examples.


Exercises will be provided by the instructors in the lab.

Lab 10:
FCS&E, GIK Institute Topi, Pakistan

Page 87

CS102L: Intensive Programming Lab

Inheritance
One of the most important concepts in object-oriented programming is that of inheritance.
Inheritance allows us to define a class in terms of another class, which makes it easier to create
and maintain an application. This also provides an opportunity to reuse the code functionality
and fast implementation time.
When creating a class, instead of writing completely new data members and member functions,
the programmer can designate that the new class should inherit the members of an existing class.
This existing class is called the base class, and the new class is referred to as the derived class.
The idea of inheritance implements the is a relationship. For example, mammal IS-A animal, dog
IS-A mammal hence dog IS-A animal as well and so on.

Base & Derived Classes:


A class can be derived from more than one classes, which means it can inherit data and functions
from multiple base classes. To define a derived class, we use a class derivation list to specify the
base class(es). A class derivation list names one or more base classes and has the form:
class derived-class: access-specifier base-class

Where access-specifier is one of public, protected, or private, and base-class is the name of a
previously defined class. If the access-specifier is not used, then it is private by default.
Consider a base class Shape and its derived class Rectangle as follows:
#include <iostream>
using namespace std;
// Base class
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// Derived class
class Rectangle: public Shape
{
public:

FCS&E, GIK Institute Topi, Pakistan

Page 88

CS102L: Intensive Programming Lab

int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
Rect.setWidth(5);
Rect.setHeight(7);
// Print the area of the object.
cout << "Total area: " << Rect.getArea() << endl;
return 0;
}

When the above code is compiled and executed, it produces following result:
Total area: 35

Access Control and Inheritance:


A derived class can access all the non-private members of its base class. Thus base-class
members that should not be accessible to the member functions of derived classes should be
declared private in the base class.
We can summarize the different access types according to who can access them in the following
way:
Access
Same class
Derived classes
Outside classes

public
yes
yes
yes

protected
yes
yes
no

private
yes
no
no

A derived class inherits all base class methods with the following exceptions:

Constructors, destructors and copy constructors of the base class.


Overloaded operators of the base class.
The friend functions of the base class.

Type of Inheritance:
FCS&E, GIK Institute Topi, Pakistan

Page 89

CS102L: Intensive Programming Lab

When deriving a class from a base class, the base class may be inherited through public,
protected or private inheritance. The type of inheritance is specified by the access-specifier as
explained above.
We hardly use protected or private inheritance but public inheritance is commonly used. While
using different type of inheritance, following rules are applied:

Public Inheritance: When deriving a class from a public base class, public members of
the base class become public members of the derived class and protected members of
the base class become protected members of the derived class. A base class's private
members are never accessible directly from a derived class, but can be accessed through
calls to the public and protected members of the base class.
Protected Inheritance: When deriving from a protected base class, public and
protected members of the base class become protected members of the derived class.
Private Inheritance: When deriving from a private base class, public and protected
members of the base class become private members of the derived class.

Multiple Inheritances:
A C++ class can inherit members from more than one class and here is the extended syntax:
class derived-class: access baseA, access baseB....

Where access is one of public, protected, or private and would be given for every base class
and they will be separated by comma as shown above. Let us try the following example:
#include <iostream>
using namespace std;
// Base class Shape
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// Base class PaintCost
class PaintCost
{
public:
int getCost(int area)
{
return area * 70;
FCS&E, GIK Institute Topi, Pakistan

Page 90

CS102L: Intensive Programming Lab

}
};
// Derived class
class Rectangle: public Shape, public PaintCost
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
int area;
Rect.setWidth(5);
Rect.setHeight(7);
area = Rect.getArea();
// Print the area of the object.
cout << "Total area: " << Rect.getArea() << endl;
// Print the total cost of painting
cout << "Total paint cost: $" << Rect.getCost(area) << endl;
return 0;
}

When the above code is compiled and executed, it produces following result:
Total area: 35
Total paint cost: $2450

Note: Before doing your lab exercises run the examples.


Exercises will be provided by the instructors in the lab.

Lab 11:
FCS&E, GIK Institute Topi, Pakistan

Page 91

CS102L: Intensive Programming Lab

Introduction to Polymorphism and Abstract Base Classes


Polymorphism
Polymorphism is a mechanism that allows you to implement a function in different ways.
Polymorphism is by far the most important and widely used concept in object oriented
programming.

Pointers to base class


We have seen that it is possible to derive a class from a base class and that we can add
functionality to member functions.
One of the features of derived classes is that a pointer to a derived class is type-compatible with a
pointer to its base class. Polymorphism takes advantage of this feature.
Lets take a look at an example of a base class and derived classes:
#include <iostream>
using namespace std;
class CPolygon
{
protected:
int width, height;
public:
void setup (int first, int second)
{
width= first;
height= second;
}
};
class CRectangle: public CPolygon
{
public:
int area()
{
return (width * height);
}
};
class CTriangle: public CPolygon
{
public:
int area()
{
return (width * height / 2);
}
};
int main ()
{
CRectangle rectangle;
FCS&E, GIK Institute Topi, Pakistan

Page 92

CS102L: Intensive Programming Lab

CTriangle triangle;
CPolygon * ptr_polygon1 = &rectangle;
CPolygon * ptr_polygon2 = &triangle;
ptr_polygon1->setup(2,2);
ptr_polygon2->setup(2,2);
cout << rectangle.area () << endl;
cout << triangle.area () << endl;
return 0;
}

As you can see, we create two pointers (ptr_polygon1 and ptr_polygon2) that point to the objects
of class CPolygon. Then we assign to these pointers the address of (using the reference
ampersand sign) the objects rectangle and triangle. Both rectangle and triangle are objects of
classes derived from CPolygon.
In the cout statement we use the objects rectangle and triangle instead of the pointers
ptr_polygon1 and ptr_polygon2. We do this because ptr_polygon1 and ptr_polygon2 are of the
type CPolygon. This means we can only use the pointers to refer to members that CRectangle
and CTriangle inherit from Cpolygon.
If we want to use the pointers to class CPolygon then area() should be declared in the class
CPolygon and not only in the derived classes CRectangle and Ctriangle.
The problem is that we use different versions of area() in the derived classes CRectangle and
Ctriangle so we cant implement one version of area() in the base class CPolygon. (If they were
the same we had no problem.)
We can fix this by using virtual members.

Virtual Members
A virtual member is a member of a base class that we can redefine in its derived classes. To
declare a member as virtual we must use the keyword virtual.
Lets change our previous example:
#include <iostream>
using namespace std;
class CPolygon
{
protected:
int width, height;
public:
void setup (int first, int second)
{
width= first;
height= second;
FCS&E, GIK Institute Topi, Pakistan

Page 93

CS102L: Intensive Programming Lab

}
virtual int area()
{
return (0);
}
};
class CRectangle: public CPolygon
{
public:
int area()
{
return (width * height);
}
};
class CTriangle: public CPolygon
{
public:
int area()
{
return (width * height / 2);
}
};
int main ()
{
CRectangle rectangle;
CTriangle triangle;
CPolygon polygon;
CPolygon * ptr_polygon1 = &rectangle;
CPolygon * ptr_polygon2 = &triangle;
CPolygon * ptr_polygon3 = &polygon;
ptr_polygon1->setup(2,2);
ptr_polygon2->setup(2,2);
ptr_polygon3->setup(2,2);
cout << ptr_polygon1->area () << endl;
cout << ptr_polygon2->area () << endl;
cout << ptr_polygon3->area () << endl;
return 0;
}

Because of the change adding area() as a virtual member of CPolygon now all the three
classes have all the same members (width, height, setup() and area().)
A class that declares or inherits a virtual function is called a polymorphic class.

Abstract Base Classes:


At the design level, an abstract base class (ABC) corresponds to an abstract concept. For
instance: if you ask to draw a shape, I will probably ask what kind of shape. The term shape is
FCS&E, GIK Institute Topi, Pakistan

Page 94

CS102L: Intensive Programming Lab

an abstract concept, it could be a circle, a square, etc, etc. You could say in C++ that class
CShape is an abstract base class (ABC) and class circle (etc) could be a derived class.
As we look at the C++ language we could say that an abstract base class has one or more pure
virtual member functions.
In the example above we put an implementation (return (0);) in the virtual member function
area(). If we want to change it into a pure virtual member function we use =0; instead of the
return (0). So the class will look like this:
class CPolygon
{
protected:
int width, height;
public:
void setup (int first, int second)
{
width= first;
height= second;
}
virtual int area() = 0;
};

This pure virtual function area() makes CPolygon an abstract base class. But you have to
remember the following: by adding a pure virtual member to the base class, you are forced to
also add the member to any derived class.
So our example should now look like this:
#include <iostream>
using namespace std;
class CPolygon
{
protected:
int width, height;
public:
void setup (int first, int second)
{
width= first;
height= second;
}
virtual int area() = 0;
};
class CRectangle: public CPolygon
{
public:
int area(void)
{
return (width * height);
}
};
FCS&E, GIK Institute Topi, Pakistan

Page 95

CS102L: Intensive Programming Lab

class CTriangle: public CPolygon


{
public:
int area(void)
{
return (width * height / 2);
}
};
int main ()
{
CRectangle rectangle;
CTriangle triangle;
CPolygon * ptr_polygon1 = &rectangle;
CPolygon * ptr_polygon2 = &triangle;
ptr_polygon1->setup(2,2);
ptr_polygon2->setup(2,2);
cout << ptr_polygon1->area () << endl;
cout << ptr_polygon2->area () << endl;
return 0;
}

Note: there is also an extra void in the derived classes CRectangle and CTriangle.
Using a unique type of pointer (CPolygon*) we can point to objects of different but related
classes. We can make use of that. For instance: we could implement an extra function member in
the abstract base class CPolygon that can print the result of the area() function. (Remember that
CPolygon itself has no implementation for the function area() and still we can use it, isnt it
cool.) After implementation of such a function the example will look like this:
#include <iostream>
using namespace std;
class CPolygon
{
protected:
int width, height;
public:
void setup (int first, int second)
{
width= first;
height= second;
}
virtual int area(void) = 0;
void onscreen(void)
{
cout << this->area() << endl;
}
};

FCS&E, GIK Institute Topi, Pakistan

Page 96

CS102L: Intensive Programming Lab

class CRectangle: public CPolygon


{
public:
int area(void)
{
return (width * height);
}
};
class CTriangle: public CPolygon
{
public:
int area(void)
{
return (width * height / 2);
}
};
int main ()
{
CRectangle rectangle;
CTriangle triangle;
CPolygon * ptr_polygon1 = &rectangle;
CPolygon * ptr_polygon2 = &triangle;
ptr_polygon1->setup(2,2);
ptr_polygon2->setup(2,2);
ptr_polygon1->onscreen();
ptr_polygon2->onscreen();
return 0;
}

Note: Before doing your lab exercises run the examples.


Exercises will be provided by the instructors in the lab.

Lab 12:
File Handling in C++
FCS&E, GIK Institute Topi, Pakistan

Page 97

CS102L: Intensive Programming Lab

Input/output with files


C++ provides the following classes to perform output and input of characters to/from files:
ofstream: Stream class to write on files
ifstream: Stream class to read from files
fstream: Stream class to both read and write from/to files.
These classes are derived directly or indirectly from the classes istream, and ostream. We have already
used objects whose types were these classes: cin is an object of class istream and cout is an object of class
ostream. Therfore, we have already been using classes that are related to our file streams. And in fact, we
can use our file streams the same way we are already used to use cin and cout, with the only difference
that we have to associate these streams with physical files. Let's see an example:

// basic file operations


#include <iostream>
#include <fstream>
using namespace std;
int main ()
{
ofstream myfile;
myfile.open ("example.txt");
myfile << "Writing this to a file.\n";
myfile.close();
return 0;
}

This code creates a file called example.txt and inserts a sentence into it in the same way we are used to do
with cout, but using the file stream myfile instead.
But let's go step by step:

Open a file
The first operation generally performed on an object of one of these classes is to associate it to a real file.
This procedure is known as to open a file. An open file is represented within a program by a stream object
(an instantiation of one of these classes, in the previous example this was myfile) and any input or output
operation performed on this stream object will be applied to the physical file associated to it.
In order to open a file with a stream object we use its member function open():

open (filename, mode);


Where filename is a null-terminated character sequence of type const char * (the same type that string
literals have) representing the name of the file to be opened, and mode is an optional parameter with a
combination of the following flags:

FCS&E, GIK Institute Topi, Pakistan

Page 98

CS102L: Intensive Programming Lab

All these flags can be combined using the bitwise operator OR (|). For example, if we want to open the
file example.bin in binary mode to add data we could do it by the following call to member function
open():

ofstream myfile;
myfile.open ("example.bin", ios::out | ios::app | ios::binary);

Each one of the open() member functions of the classes ofstream, ifstream and fstream has a default mode
that is used if the file is opened without a second argument:

For ifstream and ofstream classes, ios::in and ios::out are automatically and respectively assumed, even if
a mode that does not include them is passed as second argument to the open() member function.
The default value is only applied if the function is called without specifying any value for the mode
parameter. If the function is called with any value in that parameter the default mode is overridden, not
combined.
File streams opened in binary mode perform input and output operations independently of any format
considerations. Non-binary files are known as text files, and some translations may occur due to
formatting of some special characters (like newline and carriage return characters).
Since the first task that is performed on a file stream object is generally to open a file, these three classes
include a constructor that automatically calls the open() member function and has the exact same
parameters as this member. Therefore, we could also have declared the previous myfile object and
conducted the same opening operation in our previous example by writing:

ofstream myfile ("example.bin", ios::out | ios::app | ios::binary);


Combining object construction and stream opening in a single statement. Both forms to open a file are
valid and equivalent.

FCS&E, GIK Institute Topi, Pakistan

Page 99

CS102L: Intensive Programming Lab

To check if a file stream was successful opening a file, you can do it by calling to member is_open() with
no arguments. This member function returns a bool value of true in the case that indeed the stream object
is associated with an open file, or false otherwise:

if (myfile.is_open()) { /* ok, proceed with output */ }


This will open the file without destroying the current contents and allow you to append new data. When
opening files, be very careful not to use them if the file could not be opened. This can be tested for very
easily:
ifstream a_file ( "example.txt" );
if ( !a_file.is_open() ) {
// The file could not be opened
}
else {
// Safely use the file stream

Closing a file
When we are finished with our input and output operations on a file we shall close it so that its resources
become available again. In order to do that we have to call the stream's member function close(). This
member function takes no parameters, and what it does is to flush the associated buffers and close the file:

myfile.close();
Once this member function is called, the stream object can be used to open another file, and the file is
available again to be opened by other processes.
In case that an object is destructed while still associated with an open file, the destructor automatically
calls the member function close().

Text files
Text file streams are those where we do not include the ios::binary flag in their opening mode.
These files are designed to store text and thus all values that we input or output from/to them can
suffer some formatting transformations, which do not necessarily correspond to their literal
binary value.
Data output operations on text files are performed in the same way we operated with cout:
// writing on a text file
#include <iostream>
#include <fstream>
using namespace std;
int main () {
ofstream myfile ("example.txt");
if (myfile.is_open())
{
myfile << "This is a line.\n";
FCS&E, GIK Institute Topi, Pakistan

Page 100

CS102L: Intensive Programming Lab

myfile << "This is another line.\n";


myfile.close();
}
else cout << "Unable to open file";
return 0;
}
[file example.txt]
This is a line.
This is another line.

Data input from a file can also be performed in the same way that we did with cin:
// reading a text file
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main () {
string line;
ifstream myfile ("example.txt");
if (myfile.is_open())
{
while (! myfile.eof() )
{
getline (myfile,line);
cout << line << endl;
}
myfile.close();
}
else cout << "Unable to open file";
return 0;
}
This is a line.
This is another line.

This last example reads a text file and prints out its content on the screen. Notice how we have used a new
member function, called eof() that returns true in the case that the end of the file has been reached. We
have created a while loop that finishes when indeed myfile.eof() becomes true (i.e., the end of the file has
been reached).

Checking state flags


In addition to eof(), which checks if the end of file has been reached, other member functions exist to
check the state of a stream (all of them return a bool value):

bad()
Returns true if a reading or writing operation fails. For example in the case that we try to write to a file
that is not open for writing or if the device where we try to write has no space left.
FCS&E, GIK Institute Topi, Pakistan

Page 101

CS102L: Intensive Programming Lab

fail()
Returns true in the same cases as bad(), but also in the case that a format error happens, like when an
alphabetical character is extracted when we are trying to read an integer number.
eof()
Returns true if a file open for reading has reached the end.
good()
It is the most generic state flag: it returns false in the same cases in which calling any of the previous
functions would return true.
In order to reset the state flags checked by any of these member functions we have just seen we can use
the member function clear(), which takes no parameters.

get and put stream pointers


All i/o streams objects have, at least, one internal stream pointer:
ifstream, like istream, has a pointer known as the get pointer that points to the element to be read in the
next input operation.
ofstream, like ostream, has a pointer known as the put pointer that points to the location where the next
element has to be written.
Finally, fstream, inherits both, the get and the put pointers, from iostream (which is itself derived from
both istream and ostream).
These internal stream pointers that point to the reading or writing locations within a stream can be
manipulated using the following member functions:

tellg() and tellp()


These two member functions have no parameters and return a value of the member type pos_type, which
is an integer data type representing the current position of the get stream pointer (in the case of tellg) or
the put stream pointer (in the case of tellp).

seekg() and seekp()


These functions allow us to change the position of the get and put stream pointers. Both functions are
overloaded with two different prototypes. The first prototype is:
seekg ( position );
seekp ( position );
Using this prototype the stream pointer is changed to the absolute position position (counting from the
beginning of the file). The type for this parameter is the same as the one returned by functions tellg and
tellp: the member type pos_type, which is an integer value.
The other prototype for these functions is:
seekg ( offset, direction );
seekp ( offset, direction );
Using this prototype, the position of the get or put pointer is set to an offset value relative to some specific
point determined by the parameter direction. offset is of the member type off_type, which is also an
integer type.
And direction is of type seekdir, which is an enumerated type (enum) that determines the point from
where offset is counted from, and that can take any of the following values:
FCS&E, GIK Institute Topi, Pakistan

Page 102

CS102L: Intensive Programming Lab

The following example uses the member functions we have just seen to obtain the size of a file:
// obtaining file size
#include <iostream>
#include <fstream>
using namespace std;
int main () {
long begin,end;
ifstream myfile ("example.txt");
begin = myfile.tellg();
myfile.seekg (0, ios::end);
end = myfile.tellg();
myfile.close();
cout << "size is: " << (end-begin) << " bytes.\n";
return 0;
}
size is: 40 bytes.

Note: Before doing your lab exercises run the examples.


Exercises will be provided by the instructors in the lab.

FCS&E, GIK Institute Topi, Pakistan

Page 103

CS102L: Intensive Programming Lab

FCS&E, GIK Institute Topi, Pakistan

Page 104

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