Sie sind auf Seite 1von 107

OOP/ Part 2

Classes and Data Abstraction


A class is a logical method to organize data and operations (functions) on
that data in the same unit (called class). They are declared using keyword
class, functionality is similar to that of the C keyword struct with some
difference. Its general form is:
class class_name { permission_label_1:
member1;
permission_label_2:
member2;
... } object_name;
where class_name is a name for the class (user defined type) and the
optional field object_name is one, or several, valid object identifiers.
l The body of the declaration can contain members, that can be either
data or function declarations, and optionally permission labels, that
can be any of these three keywords: private:, public: or protected:.
A reference to the permission which the following members acquire:
l private members of a class are accessible only from other members
of their same class or from their "friend" classes.
lprotected members are accessible from members of their same class
.and friend classes, and also from members of their derived classes
.lpublic members are accessible from anywhere the class is visible

When declaring members of a class before including any


permission label, the members are considered private,
since it is the default permission that the members of a
class declared with the class keyword acquire. For
example:
class CRectangle {
int x, y; //x,y are considered private members
public:
void set_values (int,int);
int area (void); } rect ; 4
l Declares class CRectangle and an object called rect with four members:
two variables of type int (x and y) in the private section (because private
is the default permission) and two functions in the public section:
set_values() and area(), of which only the prototype are included.
l Any of the public members of the object rect could be referred to just by
putting the object's name followed by a point and then the class member
l rect.set_values(3,4);
myarea = rect.area();
l but it is not possible to refer to x or y since they are private members of
the class and they could only be referred to from other members of that
same class.

6
l The operator :: of scope (called scope of resolution) included in
the definition of set_values(). It is used to declare a member of a
class outside it. Notice that we have defined the behavior of
function area() within the definition of the CRectangle class
given its extreme simplicity. Whereas set_values() has only its
prototype declared within the class but its body definition is
outside. In this outside declaration we must use the operator of
scope ::

l The scope operator (::) specifies the class to which the


member being declared belongs. 7

l The reason why x and y are made private members is because


sometimes it is important that values cannot be modified in an
unexpected way from outside the class (i.e. via client code). To
access these variables this can be done through member
functions that are declared as public.
l In the previous example, we have already defined a function to
introduce those values in the object (set_values()) and
therefore the rest of the program does not have a way to
directly access them.
Several different objects can be defined from one class.
8
#include< iostream.h> Example
#include <cstring>
class student {
char name[20];
int First,second,final,total,ID;

void calculate_total( );

public:
student()
{ strcpy(name,"AHMAD");
ID=900901123;
First=second=final=0; }

int get_total() { calculate_total () ; return total; }


void print(void)
{cout<<name<<"\t"<<ID<<"\t"<<total<<endl;}
}; // end of class definition
9

void student::calculate_total( ){ total=First+second+final; }

void main()
{student st;
cout<<st.get_total();
st.print();
st.print();
}

10
The call to rect.area( ) does not give the same result as the call to rectb.area( ), each object of class
CRectangle has its own variables x and y, and its own functions set_value() and area(). functions are
properties of the object, instead of the usual view of objects as function parameters in structured
programming. In this concrete case, the class (type of object) is CRectangle, of which there are two
instances, or objects: rect and rectb, each one with its own member variables and member functions.11

Quiz
l Design a class named Triangle, that has Base
and the Height
l Build a method named Area
l Build a method named Set_Dim, that takes
Base,Height.
l Build A constructer that sets the Base and the
Height to zero.
l Build the main ()
l to create an object T
l Set the values to 10 ,5
l Print the area 12
Quiz
l Design a class named Student that has a name, ID,
first,second, final and Total marks.
l This class has a calculate_total method which is has a
private permission
l The class has set_Name_ID( char[ ],int) method that
stores the name of the student
l The class has set_marks(int,int,int) method
l The class has print( ) method that calls the
calculate_total and then prints the student name ,
student ID and student total mark.
l The main function looks like this:
void main( ){
Student St;
St.set_Name_ID(“Ahmad”,1000901123);
St.set_marks(19,21,41);
13
St.print(); }

// Example on class 22-feb-2012


#include< iostream.h>
#include <cstring>
class Student {
char name[20];
int first,second,final,total,ID;

void calculate_total( );

public:
void Set_Name_ID(char n[ ],int id)
{ strcpy(name,n);
ID=id;}
void Set_marks(int ,int ,int );

void print(void)
{ calculate_total( );
cout<<name<<"\t"<<ID<<"\t"<<total<<endl;}
}; // end of class definition
14
void Student::calculate_total( )
{total=first+second+final;}

void Student::Set_marks(int x,int y,int z)


{ first=x;
second=y;
final=z;}

void main( ){
Student St;
St.Set_Name_ID("Ahmad",1000901123);
St.Set_marks(19,21,41);
St.print( ); } 15

Constructors and Destructors

l Objects generally need to initialize variables or assign dynamic memory


during their process of creation to become totally operative and to avoid
returning unexpected values during their execution.
l what would happen if in the previous example the function area() is
called before having called function set_values? Probably an
undetermined result since the members x and y would have never been
assigned a value.
l In order to avoid that, a class can include a special function: a
constructor, which can be declared by naming a member function with
the same name as the class. This constructor function will be called
automatically when a new instance of the class is created 16
“A constructor
never returns a
value nor does the
void have to be
specified”

l For the student example add a constructor


method that prints a message indicating that
an object has been constructed.

18
class Student {
char name[20]; Example on
int first,second,final,total,ID;
Constructors
void calculate_total( );
public:
Student( )
{ cout<<"An object has been constructed\n";}

void Set_Name_ID(char n[ ],int id)


{ strcpy(name,n);
ID=id;}
void Set_marks(int ,int ,int );

void print(void)
{ calculate_total( );
cout<<name<<"\t"<<ID<<"\t"<<total<<endl;}
}; // end of class definition
19

void Student::calculate_total( )
{total=first+second+final;}

void Student::Set_marks(int x,int y,int z)


{ first=x;
second=y;
final=z;}

void main( ){
Student St;
St.Set_Name_ID("Ahmad",1000901123);
St.Set_marks(19,21,41);
St.print( ); }

20
Default Constructors
Commonly, a default constructor is declared without any parameters,
but it is also possible for a constructor with parameters to be a default
constructor if all of those parameters are given default values.

l Modify the constructer so that it gives a


default values to name “ No name”, ID=0,
first,second and final are also 0

Student( )
{strcpy(name,”No name”) ;
ID=0;
first=second=final=0;}
21

Note:
In order to create an array of objects of a class type, the class must have an accessible default
constructor; C++ has no syntax to specify constructor arguments for array elements

l Overload the constructor so it can take the name


and the ID at the creation of the instance.
l Also let id has a default value equal to zero

Student (char n[ ],int id=0)


{ strcpy(name,n);
ID=id;
first=second=final=0;}

In the main

Student St2("Ali");
St2.print(); 22
Empty Constructor
l If we didn’t define a constructor then there is an
empty constructor as default
l Also there is a default copy_constructor that allow
copying one constructor to another.

void main( ){
Student St1;
St1.print();
St1.Set_Name_ID("Ahmad",1000901123);
St1.Set_marks(19,21,41);
St1.print( );
Student St2;
St2=St1;
St2.print();
}
23

l With no constructors, the compiler automatically assumes that it has empty constructor:
l Empty constructor: It is a constructor with no parameters defined as empty block of
instructions. It does nothing. The empty construction exists only if no other constructor
is explicitly declared. In case that any constructor with any number of parameters is
declared, the default constructor is not called.
l CExample::CExample () { };
l To copy one instance of class to another, Copy constructor is needed: when ever one
object assigned to another we are making a copy of it. There are two types of copy
constructor.
l Perform simple variable by variable assignment: copy of all the components of
one object to another.
l Passing an object to a function. It is a constructor with one parameter of same type
that assigns to every non-static class member variable of the object a copy of the
passed object.
CExample::CExample (const CExample& rv)
create a new object of
class CExample (rv) {a=rv.a; b=rv.b; c=rv.c;}
l In the example above, a new object of class CExample is created
and which is a copy of the passed object, then in the
implementation part will initialize the data members of the current
object with the rv data members.

25

Overloading Constructors
l A constructor can also be overloaded with several functions that have the
same name but different types or numbers of parameters.
l In the cases where a class is declared and no constructor is specified, the
compiler automatically assumes two overloaded constructors ("default
constructor" and "copy constructor"). For example, for the class:
class CExample { public:
int a,b,c;
void multiply (int n, int m) { a=n; b=m; c=a*b; } };

26
‫واﺋﻞ ﻗﺼﺎص‬ 27

l In this case rectb was declared without parameters, so it has been


initialized with the constructor that has no parameters, which declares
both width and height with a value of 5.
l Notice that if we declare a new object and we do not want to pass
parameters to it we do not include parentheses ( ):

l CRectangle rectb; // right


l CRectangle rectb(); // will not call the constructor

28
Summary
l Constructors can be
l Empty Constructor
l Constructors that take parameters
l Copy Constructor

29

Destructor
l The Destructor is automatically called when an object is
released from the memory in two cases:
1. Its scope of existence has finished (i.e, if it was defined as a
local object within a function and the function ends)
2. Object dynamically assigned and it is released using delete
operator.
l The destructor must have the same name as the class with a
tilde (~) as prefix.
l Destructor receives no parameter and returns no value.
l A class may have only one destructor.
l The use of destructors is specially suitable when an object
assigns dynamic memory.
l Build a Destructor that prints a messege denoting
when an object is being destructed

~Student()
{
cout<<name<<"\nAn object has been destructed\n";
}

31

Example on destruction
void F(Student S)
{Student S1;
S1=S;
S.Set_marks(25,25,50);
S.print();
S1.print();}

void main( ){
Student St1,St2;
St1.Set_Name_ID("Ahmad",1000901123);
St2.Set_Name_ID("Ali",1000902321);
F(St1);
cout<<"back from function\n";
}
32
new , delete keywords
l The new keyword allocate memory
dynamically at run time , so that the records
are allocated while execution
l There is a direct relationship between new
and pointer.

33

#include <iostream.h>
void main()
{ int *p1, *p2;
p1=new int;
*p1=10;
cout<<&p1<<endl; // location of the pointer
cout<<"Memory location "<<p1<<" contains " <<*p1<<endl;
p2=new int;
*p2=10;
cout<<&p2<<endl; // location of the pointer
cout<<"Memory location "<<p2<<" contains "<<*p2<<endl;
delete p1;
delete p2;
34
}
Output
0x0012FF7C
Memory location 0x00901F80 contains 10
0x0012FF78
Memory location 0x00901D20 contains 10
Press any key to continue

35

#include <iostream.h>
void main()
{ int *p1, *p2;
p1=new int;
*p1=10;
cout<<&p1<<endl; // location of the pointer
cout<<"Memory location "<<p1<<" contains "
; <<*p1<<endl
moving ; delete p1
delete P1 ;p2=new int
to here *p2=10;
cout<<&p2<<endl; // location of the pointer
cout<<"Memory location "<<p2<<" contains
"<<*p2<<endl;
delete p2;
} 36
l 0x0012FF7C
l Memory location 0x00901F80 contains 10
l 0x0012FF78
l Memory location 0x00901F80 contains 10
l Press any key to continue

37

// example on constructors and destructors


#include <iostream.h>
class CRectangle {
int *width, *height;
public:
CRectangle (int,int); // constructor
~CRectangle (); //destructor
int area (void) {return (*width * *height);} };
CRectangle::CRectangle (int a, int b) {
width = new int;
height = new int;
*width = a;
*height = b;}
CRectangle::~CRectangle () {
delete width;
delete height;}

void main ( ) {
CRectangle rect (3,4), rectb (5,6);
cout << "rect area: " << rect.area() << endl;
‫واﺋﻞ ﻗﺼﺎص‬
cout << "rectb area: " << rectb.area() << endl;
} 38
Inline functions

An inline function is a function that is expanded in line at the point at


which it is invoked, instead of actually being called. The reason for
inline function is efficiency. There are two ways to create an inline
function.
The first : to use the inline modifier.
inline int f()
{
//……..
}

39

l Every time a function is called, a series of instructions must be


executed, both to set up the function call, including pushing
any arguments onto the stack, and to return from the function.
In some cases, many CPU cycles are used to perform these
procedures. However, when a function is expanded in line, no
such overhead exists, and the overall speed of your program
will increase.
l Where the inline function is large, the overall size of your
program will also increase. For this reason, the best inline
functions are those that are very small.

40
class c1 {
int i; l If you compile this version of the program,
public: save its object code, and then compile it again
intexample
get_i(); with the inline specifier removed, you will see
void put_i(int j);}; the inline version is several bytes smaller.
inline int c1::get_i() l Inline is a request not a command, that the
{ return i;} compiler generate inline code.
inline void c1::put_i(int j)
{ i=j;}
l Some compilers will not generate inline code if
a function contains a loop, a switch, or a goto.
void main() l no inline recursive functions.
{ c1 s;
s.put_i(10); l Inline functions that contain static variables
cout<<s.get_i();} are disallowed.

output
10

l Second :Creating Inline Functions inside a Class


Any function that is defined inside a class definition is automatically made
into an inline function. It is not necessary to precede its declaration with
the keyword inline.
class c1 {
int i;
public:
//automatic inline functions.
int get_i() {return i;}
void put_i(int j) {i=j;} };
int main()
{ c1 s;
s.put_i(10); Output
cout<<s.get_i(); 10

return 0;}

42
Enumeration in C++
l The keyword enum creates a user-defined type
l Examples:
enum semesters {first, second, summer};
enum months{jan=1,feb,mar,apr,may,jun,jul,aug,sept, oct,nov,dec};

l If you print enum variable, the output will be an integer that


represents the order of the value in the definition starting from 0
l For the second example the counting will start from 1
l You cant read an enum by cin
l You cant make arithmatic operations on enum
l You can only assign a value for the enum by using = , and make
relational operations on enum 43

#include <iostream.h>
void main(){
enum mark {pass, fail};
mark m;
int score;
cout<<"Enter your mark ";
cin>>score;
if (score>=50)
m=pass;
else
m=fail;

if( m==pass)
cout<<" you passed the exam\n";
else
cout<<"you failed in the exam\n";
} 44
3.3 Array of object and Pointers to Objects

Array of objects can be created in the same way that you create arrays of
any other data types.

45
It is perfectly valid to
create pointers
pointing to classes, in
order to do that,
simply consider that
once declared, the
class becomes a valid
type, so use the class
name as the type for
the pointer.

47

l (*p).show_num(); and
p->show_num(); are the same
l *p can be read: pointed by p
l &x can be read: address of x
l x.y can be read: member y of object x
l (*p).y can be read: member y of object pointed by p
l p->y can be read: member y of object pointed by
p (equivalent to the previous one)
l x[0] can be read: first object pointed by x
l x[1] can be read: second object pointed by x
l x[n] can be read: (n+1)th object pointed by x

48
If there is array of objects are to be defined, and there exist a constructor, then the
declaration will be:
class_name obj[n]={values of initialization}

#include <iostream.h> 10
class p_example { 20
int num;
public:
p_example(int a){num=a;}
void set_num(int val) {num=val;}
void show_num( );};
void p_example::show_num()
{cout<<num<<“\n”;}

void main()
{ p_example ob[2]={10,20};
ob[0]. show_num();
ob[1]. show_num();
49
}

class Student {
char name[20];
int first,second,final,total,ID; Another
public:
Student( ) //constructor example on
{strcpy(name,"No name") ;
ID=0; array
first=second=final=0;
}

Student(char n[ ],int id) //constructor overloading


{strcpy(name,n) ;
ID=id;
first=second=final=0;}

void print(void)
{cout<<name<<"\t"<<ID<<"\t"<<total<<endl;}
}; // end of class definition
50
void main( )
{
Student S1("Ali",123),S2("Ahmad",456);

Student St[3]={S1,S2};
for (int i=0;i<3;i++)
St[i].print();
}
51

Rewrite the main in the prev.


example with pointers
void main( )
{
Student S1("Ali",123),S2("Ahmad",456), *SP;

Student St[3]={S1,S2};
SP=St;
for (int i=0;i<3;i++)
(SP+i)->print();
}
52
Rewrite the main in the prev.
example with pointers
void main( )
{
Student S1("Ali",123),S2("Ahmad",456), *SP;

Student St[3]={S1,S2};
SP=St;
for (int i=0;i<3;i++)
SP++->print();
}
53

Static Class Member

v Each object has its own copy of all data members of the class.

v If adata item in a class defined as static, then only one such


item is created for the entire class, no matter how many
objects there are.

54
Example on class -
<#include< iostream.h
<include <cstring#
;class Student { static int count ;
char name[20];
int first,second,final,total,ID;
public:
Student( ) //constructor
{strcpy(name,"No name") ;
ID=0;
first=second=final=0;
count++;
cout<<"Numbers of students constructed :"<<count<<endl;}
}; // end of class
int Student::count=0;

void main( )
{ cout<<"\nConstruct 2 objects\n";
Student S1,S2;
cout<<"\nConstruct 3 objects\n";
Student St[3]; } // end of main
55

The Output will be:

Construct 2 objects
Numbers of students constructed : 1
Numbers of students constructed : 2

Construct 3 objects
Numbers of students constructed : 3
Numbers of students constructed : 4
Numbers of students constructed : 5
Press any key to continue
56
Example:

#include <iostream.h>

class st_example {
static int count;

public:
st_example (){count++;}
int get_count(){return count;}}; count is1
count is2
int st_example::count=0; count is2
count is3
void main()
{ st_example ob1;
cout<<”\n count is”<<ob1.get_count();
st_example ob2;
cout<<”\n count is”<<ob1.get_count();
cout<<”\n count is”<<ob2.get_count();
st_example ob3;
cout<<”\n count is”<<ob3.get_count(); }
57

Static Member Function

8 This type of function can be accessed in two ways:

1. Through any object of that class (ob1.get_count()).


2. Or through the class name using the binary scope of
resolution (::) directly
(cout<<st_example ::get_count();).

58
class Student { static int count;
char name[20];
int first,second,final,total,ID;
public:
static void print_count(){cout<<“Students constructed: “ <<count<<endl;}
Student( ) //constructor
{strcpy(name,"No name") ;
ID=0;
first=second=final=0;
count++;
print_count(); }
}; // end of class
int Student::count=0;

void main( )
{ Student::print_count();
cout<<"\nConstruct 2 objects\n";
Student S1,S2;
cout<<"\nConstruct 3 objects\n";
Student St[3];}

59

Numbers of students constructed : 0

Construct 2 objects
Numbers of students constructed : 1
Numbers of students constructed : 2

Construct 3 objects
Numbers of students constructed : 3
Numbers of students constructed : 4
Numbers of students constructed : 5
Press any key to continue
60
Example:
#include <iostream.h>

class st_example {
static int count;
public:
st_example (){count++;} count is1
static int get_count(){return count;}}; count is2
int st_example::count=0; count is2
count is3
void main()
{ st_example ob1;
cout<<”\n count is”<<ob1.get_count();
st_example ob2;
cout<<”\n count is”<< st_example ::get_count();
cout<<”\n count is”<<ob2.get_count();
st_example ob3;
cout<<”\n count is”<<ob3.get_count(); }

61

Constant Object &

Constant Member Function

The keyword const is used in C language to


define a data values that cannot be changed
during program execution. In OOP, const
could be used to specify constant object and /
.or constant member function

62
#include <iostream.h>
class time {
private:
int h,m,s;
public:
void print() const; //constant function
time(int i, int j, int k){h=i; m=j; s=k;}};
void main()
{ const time noon(12,0,0); //constant object
:
}

l Keyword const is used to specify non modifiable objects,


and any attempt to modify the const object cause syntax
error.
l const time noon(12,0,0);// indicates constant object noon of
class time which is initialized to 12 noon.
63

Constant Member Function

v Constant function could be defined as follows:

At prototype: type function_name() const ;

At implementation : type function_name() const { };

Notes:
* It is not allowed for any member_function calls for const
object unless the member functions also constant.

* Constant member functions cannot modify the object.

* It is not allowed to declare constructors as constant


since it allow to modify objects 64
Example:

#include <iostream.h>
class time {
private:
int h,m,s;
public: 12:0:0
void print() const {cout<<h<<”:”<<m<<”:”<<s; }
time(int i, int j, int k) {h=i; m=j; s=k;}
int get_h() {return h;}};
void main()
{ const time noon(12,0,0);
noon.print();
// cout<<noon.get_h(); //error since get_h not const
}

65

Friends (friend keyword)


l There are three levels of internal protection for the
.different members of a class: public, protected and private
l In the case of members protected and private,
these could not be accessed from outside the same class
at which they are declared. Nevertheless, this rule can be
transgressed with the use of the friend keyword in a
class, so we can allow an external function to gain
.access to the protected and private members of a class
l In order to allow an external function to have
access to the private and protected
members of a class, declare the prototype
of the external function that will gain access
by the keyword friend within the class 66

declaration that shares its members.


67

Example:
class myclass {
int a, b;
public:
myclass(int i, int j) {a=i;b=j;}
friend int sum(myclass x); };
// note: sum() is not a member function of any class

int sum(myclass x) {
//sum() is a friend of myclass, it can access a and b through object x
return x.a+x.b;}

int main()
{ myclass n(3,4);
cout<<sum(n);
return 0;}

68
#include <iostream.h>
class CRectangle {
int width, height; 24
public:
void set_values (int, int);
int area (void) {return (width * height);}
friend CRectangle duplicate (CRectangle); }; // End of Class def.

void CRectangle::set_values (int a, int b) {


width = a; height = b; }
CRectangle duplicate (CRectangle R){
CRectangle T;
T.width = R.width*2;
T.height = R.height*2;
return (T); }

void main () {
CRectangle rect, rectb;
rect.set_values (2,3);
rectb = duplicate (rect);
cout << rectb.area(); }
69

l The use of friend functions is out of an OOP


methodology, so it is preferable to use
members of the same class to make the
process.

l As in the previous example, it would have


been shorter to integrate duplicate() within
the class CRectangle.
l A function may be a friend of more than
one class.
70
#include <iostream.h>
const int IDLE =0;
const int INUSE=1;
class c2; //forward reference
class c1 { int status;
public:
void set_status(int state); Screen can be used
friend int idle(c1 a, c2 b); }; Pop up in use
class c2 { int status;
public:
void set_status(int state);
friend int idle(c1 a, c2 b); };
void c1::set_status(int state) { status=state; }
void c2::set_status(int state) { status=state; }
int idle(c1 a, c2 b) {if(a.status || b.status) return 0;
else return 1; }
void main()
{ c1 x; c2 y;
x.set_status(IDLE); y.set_status(IDLE);
if ( idle(x,y) ) cout<<“screen can be used\n”;
else cout << “Pop-up In use\n”;
x.set_status(INUSE);
if ( idle(x,y) ) cout <<“Screen can be used\n”;
71
else cout <<“Pop-up in use\n”;}

Friend Classes (friend)

l we can define a class as friend of another


one, allowing that the second one access to
the protected and private members of the
first one.

72
#include <iostream.h>
class CSquare;
class CRectangle {
int width, height;
public:
int area (void) {return (width * height);}
void convert (CSquare a); };
16
class CSquare {
private:
int side;
public:
void set_side (int a) {side=a;}
friend class CRectangle; };

void CRectangle::convert (CSquare a) { width = a.side; height = a.side; }

void main () {
CSquare sqr;
CRectangle rect;
sqr.set_side(4);
rect.convert(sqr);
cout << rect.area(); }
73

l CRectangle is declared as a friend of CSquare so that


CRectangle can access the protected and private
members of CSquare, more concretely CSquare::side,
that defines the square side width. In the first instruction of
the program, the empty prototype of class CSquare is
used. This is necessary because within the declaration of
CRectangle, we refer to CSquare (as a parameter in
convert()).

74
Topic covered up to now
l Functions
l Function Overloading
:l Struct
:l Classes
,l Public, private
l scope operator )::(
l Empty constructor , Copy constructor
l Constructor Destructor
l New delete keywords and its relation with Constructor and destructor
l Inline function
l Enum
l Arrays of objects and relation with pointers
l Static object
l Static function
l Const Object
.l Const member function
l Friend function
l Friend Class 75

Dynamic initialization
l In c++, both local and global variables can initialized at run
time. This process is sometimes referred to as Dynamic
initialization.
l Most initializations have used constants.
l Under Dynamic initialization, a variable can be initialized at
run time using any c++ expression valid at the time the
variable is declared.
l You can initialize a variable using other variables.
The following are all perfectly valid variables initializations in
c++:
int n = strlen(str);
double arc = sin(theta);
float d= 1.02 * count / deltax;
76
#include <iostream.h>
class myclass { Applying Dynamic Initialization to
int a, b;
public:
Constructors
void setab(int i, int j) {a=i; b=j;}
void showab(); };

void myclass::showab() {
cout << "a is " <<a << "\n"; obj1 befor assignment:
cout<< "b is " << b << "\n";} a is 10
b is 20
main() ob2 before assignment:
{myclass obj1, obj2; a is 0
obj1.setab(10, 20);
obj2.setab(0,0);
b is 0
cout<<"obj1 before assignment:\n"; ob1 after assignment:
obj1.showab(); a is 10
cout<< "obj2 before assignment: \n"; b is 20
obj2.showab(); Ob2 after assignment:
cout<< "\n";
a is 10
obj2=obj1;// assign ob1 to obj2
cout<<"obj1 after assignment: \n"; b is 20
obj1.showab();
cout<<"obj2 after assignment: \n";
obj2.showab();} 77

l Like simple variables, objects can be initialized dynamically


when they are created.
l This feature allows you to create exactly the type of object
you need, using information that is known only at run time.
l The point of overloading constructor functions is to help
programmers handle greater complexity by allowing objects
to be constructed in the most natural manner relative to be
used.
l Assigning Objects: you can assign object to another if both
objects of the same type (both are of the same class).
l It is not sufficient for the two classes to simply be physically
similar-their type names must be the same.
l When one object is assigned to another, the first object data
is copied to the second.

78
#include <iostream.h>
class C1{ int x,y;
public:
C1(){}
C1(int a,int b)
{x=a;y=b;}}; //End of Class C1
class C2{ int x,y;
public:
C2(){}
C2(int a,int b)
{x=a,y=b;} }; //End of Class C2
void main()
{ C1 L,M(10,20);
C2 N,P;
L=M;
N=P;
N=M; //Error no acceptable conversion } //End of main

79

Passing Objects to Functions


l An object can be passed to a function in the
same way as any other data type.
l Objects are passed to functions using the
normal c++ call-by-value parameter passing
convention. This means that a copy of the
object, not the actual object itself, is passed to
the function.
l Any change made to the object inside the
function does not affect the object used as
argument to the function.

80
#include <iostream.h>
class OBJ { int i; Executing function f
public: 10
void set_i(int x) { i = x;} 100
void out_i() {cout << i <<"\n";}};
Returning from function to main
void f(OBJ x) {
10
cout<<" Executing function f\n";
x.out_i(); // outputs 10
Press any key to continue
x.set_i(100); // this affects only local copy
x.out_i(); // outputs 100
cout<<" Returning from function to main\n";}
void main()
{ OBJ o;
o.set_i(10);
f(o);
o.out_i(); // still outputs 10, value of I unchanged
}

The Modification of x within f() has‫ﻗﺼﺎص‬


no‫واﺋﻞ‬effect on object o inside main()81

Constructors, Destructors, and Passing Objects


#include <iostream.h> Passing simple objects as arguments to functions is
a straightforward procedure.
class myclass {
int val;
public: Constructing
myclass(int i) 10
{ val=i; cout<<“Constructing\n”;} Destructing
~myclass() {cout <<“Destructing\n”;} Destructing
int getval() { return val;}};

void display(myclass ob) {


cout<<ob.getval()<<“\n”;}

void main()
{ myclass a(10);
display (a);}
l As you can see, there is one call to the constructor function
(as expected), but there are two calls to the destructor.
Why?

l When an object is passed to a function, a copy of that


object is made (and this copy becomes the parameter in
the function). This means that a new object comes into
existence. Also, when the function terminates, the copy of
the argument (i.e., the parameter) is destroyed.

l This raises two questions: first, is the object’s constructor


called when the copy is made? Second, is the object’s
destructor called when the copy is destroyed?

83

l When a copy of an argument is made during a function call, the


constructor function is not called. because the constructor function
is generally used to initialize some aspect of an object, it must not be
called to make a copy of an already existing object that is being
passed to a function. When passing an object to a function, you want
to use the current state of the object, not its initial state.

l When the function terminates and the copy is destroyed, the


destructor function is called. This is because the object might
perform some operations that must be undone when it goes out of
scope. For example the copy may allocate memory that must be
released.

l Finally when a copy of an object is created to be used as an


argument to a function, the constructor function is not called.

l When the copy is destroyed (usually by going out of scope when the
function returns), the destructor function is called.
A potential problem when passing objects

l If an object used as an argument allocates


dynamic memory and frees that memory
when it is destroyed, then its local copy
inside the function will free the same
memory when its destructor is called. This
will leave the original object damaged and
effectively useless so the abort screen will
appear.

85

#include <iostream.h>
#include <stdlib.h>
class myclass {
int *p; Abort screen
public: myclass(int i);
~myclass();
int getval(){return *p;}};

myclass::myclass(int i) {
cout<<"allocating p\n";
p= new int;
*p=i;}
myclass::~myclass() {
cout<<"Freeing p\n";
delete p;} //This will cause a problem .
void display(myclass ob) { // try to use &ob what will happen??
cout<<ob.getval()<<"\n"; }
void main()
{ myclass a(10);
display(a); }

86
this
l Each time a member function is invoked, it is automatically
passed a pointer, called this, to the object that has invoked. 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. For the following example:

class c1 {int i;
void load_i(int val){i=val;};

l i=val; statement can be used in a member function (load_i ) to


assign i the value of val. In actuality, the preceding statement is
shorthand for: this->i=10;

87

#include <iostream.h>
class c1 {
int i;
public:
void load_i(int val) {this->i=val;} // same as i=val 100
int get_i() {return this->i;} // same as return i

};
int main()
{ c1 o;
o.load_i(100);
cout<<o.get_i();
return 0;}

88
l As I see, one of the uses of this pointer is to
provide us a way to reach to the variables of
the object that has the same name with
variables in the function. We discussed such
a problem before.
l Next example clarifies the idea.

89

#include <iostream.h>
class Student {
int id;
public:
void set_id(int id)
{id=id;} // it should be { this->id=id;}
void print_id()
{cout<<"ID is "<<id<<endl;} }; //end of class

void main(){
Student St;
St.set_id(10);
St.print_id();
}
90
Operator Overloading
l C++ incorporates the option to use language standard
operators between classes and between fundamental
types. For example:

int a, b, c;
a = b + c;
l is valid, since the different variables of the addition are all
fundamental types. While, the following operation is
incorrect.

struct { char product [50]; float price; } a, b, c;


a = b + c;

91

l The assignation of a class (or struct) to another one of the same


type is allowed (default copy constructor). But addition operation
in principle is not valid between non-fundamental types.

l C++ has the ability to overload operators. Objects derived from


composed types such as the previous one can accept operators
which would not be accepted otherwise, and we can even modify
the effect of operators. Here is a list of all the operators that can
be overloaded:

+ - * / = < > += -= *= /= << >>


<<= >>= == != <= >= ++ -- % & ^ ! |
~ &= ^= |= && || %= [] () new delete
To overload an operator we only need to write a class
member function whose name is operator followed by
the operator sign that we want to overload, following this
prototype:

type operator sign (parameters);

Here you have an example that includes the operator +. We


are going to sum the bi-dimensional vectors a(3,1) and
b(1,2). The addition of two bi-dimensional vectors is an
operation as simple as adding the two x coordinates to
obtain the resulting x coordinate and adding the two y
coordinates to obtain the resulting y. In this case the result
will be (3+1,1+2) = (4,3).

#include <iostream.h>
class CVector { int x,y;
public:
CVector ( ) { };
CVector (int a, int b) { x = a; y = b;}
CVector operator + (CVector);
void print( ){cout<<x<<','<<y<<endl;}}; // End of class

CVector CVector::operator+ (CVector param) { 4,3


CVector temp;
temp.x = x + param.x;
temp.y = y + param.y;
return (temp);}

void main ( ) {
CVector a (3,1), b (1,2), c ;
c = a + b; // c = a.operator + (b);
c.print( );}
94
The function operator+ of class CVector is the one that is in charge
of overloading the arithmetic operator +. This one can be called
by any of these two ways:
c = a + b;
c = a.operator+ (b);

l Notice also that we have included the empty constructor (without


parameters) and defined it with a no-op block of instructions:
CVector () { };this is necessary, since there already exists another
constructor, CVector (int, int);so none of the default constructors
will exist in CVector if we do not explicitly declare one as we
have done. Otherwise the declaration CVector c; included in
main() would not be valid. Therefore, a more advisable
declaration would have been something similar to this: CVector
() { x=0; y=0; };

l As well as a class includes by default an empty and a


copy constructor, it also includes a default definition
for the assignation operator (=) between two classes
of the same type.

l This copies the whole content of the non-static data


members of the parameter object (the one at the right
side of the sign) to the one at the left side.

l Of course, you can redefine it to any other


functionality that you want for this operator, like for
example, copy only certain class members.
l Although the prototype of a function
operator+ can seem obvious since it takes the
right side of the operator as the parameter for
the function operator+ of the left side object,
other operators are not so clear. Here you have
a table with a summary on how the different
operator functions must be declared (replace
@ by the operator in each case):

97

where a is an object of class A, b is an object of class B and c


is an object of class C.
l You can see in this panel that there are two
ways to overload some class operators: as
member function and as global function.

l Its use is indistinct; considering that functions


that are not members of a class cannot access
the private or protected members of the class
unless the global function is friend of the class.

Operator Overloading Using Member Functions


class myClass {
int x, y, z; void myClass::show() {
public: cout<<x<<“, “;
myClass() {x=y=z=0;} cout<<y<<“, “;
myClass(int i, int j, int k){x=i; y=j; z=k;} cout<<z<<“\n “; }
myClass operator+(myClass t); void main() {
myClass operator=(myClass t); myClass a(1,2,3), b(10,10,10), c;
void show();}; a.show();
myClass myClass::operator+(myClass t) b.show();
{myClass temp; c=a+b;
temp.x=x+t.x; c.show();
temp.y=y+t.y; c=a+b+c;
temp.z=z+t.z; c.show();
return temp; } c=b=a; // b=a; c=b;
myClass myClass::operator=(myClass t) { c.show();
x=t.x; b.show(); } The Output is:
y=t.y; 1,2,3
z=t.z; 10,10,10
return *this;} 11,12,13
22,24,26
1,2,3
1,2,3
when a binary operator is overloaded using a member function, only
one argument is explicitly passed to it. The other argument is
implicitly passed using "this" pointer. Thus, in the line
temp.x=x+t.x;

The x refers to this->x, which is the x associated with the object that
invokes the operator function. In all cases it is the object on the
left side of an operation that causes the call to the operator
function. The object on the right side is passed to the function.

when you use a member function, no parameters are used when


overloading a unary operator and only one parameter is required
when overloading a binary operator.

Operator+() returns an object of type myClass. Although the


function could have returned any valid C++ type, the fact that it
returns a myClass object allows the + operator to be used in
compound expressions, such as a+b+c. Here, a+b generates a
result that is of type myClass. This value can then be added to c.

The operator=() function is called by the object that occurs on the


left side of the assignment, it is this object that is modified by the
assignment operation.
l ? Can we make the = overloading definition as
void operator=(Myclass t)

l ? Why the operator= has a type and why it returns *this


l The return value of an overloaded assignment operator is the object on
the left, after the assignment has been made.
l This is similar to :
strcpy(A,B); //copies B to A
cout<<strcpy(A,B); // copies B to A and also returns B to cout<<

103

Using Member Functions to Overload Unary


Operators

l When a unary operator (i.e. ++ , --) is overloaded by means of a


member function, no object is explicitly passed to the operator
function. Instead, the operation is performed on the object that
generates the call to the function through the implicitly passed
this pointer.
#include <iostream.h>
#include <stdlib.h>
class myClass { void main() {
int x, y, z; myClass a(1,2,3), b(10,10,10),
public: c;
myClass() {x=y=z=0;} a.show(); b.show();
myClass(int i, int j, int k){x=i; y=j; z=k;}
myClass operator+(myClass t); c=a+b;
myClass operator++(); c.show();
void show();}; c=a+b+c; c.show();
myClass myClass::operator++() c=b=a; c.show();
{ ++x;
++y; b.show();
++z; ++c;
return *this;} c.show();}
myClass myClass::operator+(myClass t) {
myClass temp; 1, 2, 3
temp.x=x+t.x;
10, 10, 10
temp.y=y+t.y;
temp.z=z+t.z; 11, 12, 13
return temp;} 22, 24, 26
void myClass::show() { 1, 2, 3
cout<<x<<", "; 1, 2, 3
cout<<y<<", "; 2, 3, 4
cout<<z<<"\n ";} 105

l operator++() increments each coordinate in the object and returns the


modified object.
l The ++ and -- have both a prefix and a postfix form. In the preceding
program, the operator++() function defines the prefix form of ++
relative to the three_d class.
l It is possible to overload the postfix form.
l The post operator will call the parameterized function, while the
prefix will always call the non-parameterized function.
l The prototype for the postfix form of the ++ operator relative to the
three_d class is shown here:
three_d three_d::operator++(int notused);
l The parameter not used is not used by the function, and should be
ignored. This parameter is simply a way for the compiler to distinguish
between the prefix and postfix forms of the increment and decrement
operators.
class C1 {
int x, y, z;
Example on Pre and Post fix ++
public:
C1(){} // empty
C1(int i, int j, int k){x=i; y=j; z=k;}
C1 operator++();
void show(); };
C1 C1::operator++()
{ ++x; ++y; ++z;
return *this;};

void C1::show() {
cout<<x<<", "<<y<<", "<<z<<"\n";}

void main()
{ C1 a(1,2,3),b;
++a;
a.show();
b=++a;
b.show();
b=a++; // warning : no postfix form of 'operator ++' found for type 'C1', using prefix
form
b.show(); 107
}

class three_d {
three_d three_d::operator++()
int x, y, z;
public: { ++x; ++y; ++z;
three_d() {x=y=z=0;} return *this;}
three_d(int i, int j, int k){x=i; y=j; z=k;} three_d three_d::operator++(int notused)
three_d operator+(three_d op2); { three_d temp=*this;
three_d operator=(three_d op2); x++; y++; z++;
three_d operator++();
three_d operator++(int notused); // postfix
return temp;}
void show(); }; void three_d::show() {
cout<<x<<", ";
three_d three_d::operator+(three_d op2) cout<<y<<", ";
{ three_d temp; cout<<z<<"\n ";}
temp.x=x+op2.x; void main()
temp.y=y+op2.y;
temp.z=z+op2.z;
{ three_d a(1,2,3), b(10,10,10), c;
return temp; } a.show(); b.show(); c=a+b; c.show();
c=a+b+c; c.show(); c=b=a; c.show();
three_d three_d::operator=(three_d op2) b.show();
{ x=op2.x; ++c;
y=op2.y;
c.show();
z=op2.z;
return *this;} c++;
c.show(); a=++c; a.show(); c.show();
a=c++; a.show(); c.show(); }
Output of prev. Example
1, 2, 3
10, 10, 10
11, 12, 13
22, 24, 26
1, 2, 3
1, 2, 3
2, 3, 4
3, 4, 5
4, 5, 6
4, 5, 6
4, 5, 6
5, 6, 7

109

class C {
int x, y, z;
public: Unary minus b= -a
C() {x=y=z=0;}
C(int i, int j, int k){x=i; y=j; z=k;}

C operator-()
{ C t;
t.x=-x;
t.y=-y;
t.z=-z;
return t;}
void print(){cout<<x<<','<<y<<','<<z<<endl;} };

void main(){
C a(10,10,10), b;
b=-a; //b = a.operator- ( ) ;
b.print();
a.print(); 110
}
Overloading a relational operator

l such as == or <, is a straightforward process.

l overloaded operator function usually returns an object of the


class for which it is overloaded. However, an overloaded
relational operator typically returns a true or false value.

l To show how an overloaded relational operator can be


implemented, the following function overloads the "==".
int three_d::operator==(three_d t) {
if ((x==t.x) && (y==t.y) && (z==t.z))
return 1;
else
return 0;}

class RelationalOver {
int x, y, z;
public:
RelationalOver() {x=y=z=0;}
RelationalOver(int i, int j, int k){x=i; y=j; z=k;}
int operator==(RelationalOver op2); };

int RelationalOver::operator==(RelationalOver t) {
if ((x==t.x) && (y==t.y) && (z==t.z))
return 1;
else
return 0;}

void main(){
RelationalOver a(10,10,10), b(10,10,10);
if (a==b) //if ( a.operator==(b) )
cout<<"The two objects are equal";
else
cout<<"The two objects are not equal";
}
Friend Operator Functions

l The only operators that cannot be overloaded using


friend functions are =,(),and ->.
l Friend functions do not have a this pointer.
l When a friend is used to overload an operator, both
operands are passed explicitly if a binary operator is
overloaded. If a unary operator is overloaded then a
single operand is passed.
l In The next program, a friend is used instead of a
member function to overload the + operation.
113

Friend Operator Functions

l There is one situation in which you must use a friend function.


l A pointer to the object that invokes a member operator function is passed
in this. In the case of a binary operator, the object on the left invokes
the function. This is fine, provided that the object on the left defines the
specified operation. Assuming some object called O, which has assignment
and addition defined for it, then this is perfectly valid statement:
O = O + 10; //will work
While;
O = 10 + O; //won’t work
l The problem with the above statement is that the object on the left side of
the + operator is an integer, a built-in type for which no operation involving
an integer and an object of O’s type is defined.

Solution:
l The problem of having a built-in type on the left side of an operation can be
eliminated if the + is overloaded using two friend functions. In this case, the
operator function is explicitly passed both arguments, and it is invoked like
any other overloaded function, based upon the types of its argument.
class C{
int x,y;
public:
C(){x=0;y=0;}
C(int i,int j)
{ x=i;y=j;}

C operator+(int i)
{C T;
T.x=x+i; T.y=y+i;
return T;}

void print(){ cout<<x<<","<<y<<endl;}


};
void main()
{C a(10,20),b,c;
b=a+10; // b= a.operator+ (10);
b.print();
c=10+b; // no global operator defined or there is no acceptable
conversion
c.print();
115
}

class C{ int x,y;


public:
C( ){x=0;y=0;}
C(int i,int j) { x=i;y=j;}
C operator+(int i) // works with b=a+10
{ C temp;
temp.x=x+i;temp.y=y+i;
return temp;}
friend C operator+ ( int ,C); // works with b=10+a
void print(){ cout<<x<<","<<y<<endl;}}; // End of class def.
C operator+ ( int i,C OP)
{ C temp;
temp.x=OP.x+i;
temp.y=OP.y+i;
return temp;}

void main()
{C a(10,20),b,c;
b=a+10; // b= a.operator+ (10);
b.print();
c=10+b; // this will call the friend function c= operator+(10,b) 116
c.print(); }
class CL {
//this handles int + ob.
public:
int count; CL operator+(int i, CL ob){
CL operator=(CL obj); CL temp;
friend CL operator+(CL ob, int i); temp.count=ob.count+i;
friend CL operator+(int i, CL ob); return temp;
}; }
CL CL::operator=(CL obj) { main() {
count = obj.count; CL o;
return *this; o.count=10;
} cout<<o.count<<“ “;//outputs 10
//this handles ob + int.
o=10+ o;// adds object to integer.
CL operator+(CL ob, int i){
cout<<o.count<<“ “;//outputs 20
CL temp;
o=o+12;// adds integer to object.
temp.count=ob.count+i;
return temp; cout<<o.count; // outputs 32
} return o;
}
117

Using a Friend to Overload a Unary Operator

l In overloaded unary operator using member function, Every member


function has an implicit argument, a pointer to the object that invokes
it, which is referenced inside the member function by the keyword
this.
l Unlike member functions, a friend function does not receive a this
pointer. And therefore cannot reference the object that invoked it.
Instead, a friend operator function is passed its operand explicitly. For
this reason, trying to create a friend operator++() function as shown
here will not work.

//This will not work This function will not work because only a
three_d operator++(three_d op1) copy of the object that activated the call to
{ operator++() is passed to the function in
parameter op1. Thus, the changes inside
op1.x++;
will not effect the calling object
op1.y++; 118

op1.z++;
return op1;
l Using a friend function when overloading a unary ++ or --
requires that the object be passed to the function as a
reference parameter.
parameter In this way the function can modify the
object.
l When a friend is used for overloading the increment or
decrement operators, the prefix form takes one parameter.
The postfix form takes two parameters. The second is an
integer, which is not used.

119

class C {
int x, y, z;
public:
C() {x=y=z=0;}
C(int i, int j, int k){x=i; y=j; z=k;}

void print(){cout<<x<<','<<y<<','<<z<<endl;}

friend C operator++(C);
};

C operator++(C OP) // should be C operator++(C & OP)


{ OP.x++;
OP.y++;
OP.z++; 11,11,11
return OP;}

void main(){ 10,10,10


C a(10,10,10), b;
b=++a; //b = operator++(a) ;
b.print();
a.print();
}

120
class three_d {
int x, y, z;
public:
three_d() {x=y=z=0;}
three_d(int i, int j, int k){x=i; y=j; z=k;}
friend three_d operator+(three_d op1, three_d op2);
three_d operator=(three_d op2);
friend three_d operator++(three_d &op1); // prefix
friend three_d operator++(three_d &op1 , int notused);
void show();
};

three_d operator+(three_d op1 , three_d op2)


{
three_d temp;
temp.x=op1.x+op2.x;
temp.y=op1.y+op2.y;
temp.z=op1.z+op2.z; 121

return temp; }

three_d three_d::operator=(three_d //overload the postfix version


op2) three_d operator++(three_d &op1,
{ int
x=op2.x; notused)
y=op2.y; {
three_d temp=op1;
z=op2.z;
op1.x++;
return *this;
op1.y++;
}
op1.z++;
return temp;
three_d operator++(three_d &op1) }
{
op1.x++; void three_d::show() {
op1.y++; cout<<x<<“, “;
op1.z++; cout<<y<<“, “;
return op1; cout<<z<<“\n “;
} }
main() a.show();
{ c.show();
three_d a(1,2,3), b(10,10,10), c;
a=c++;
a.show();
b.show(); a.show();
c=a+b; c.show();
c.show(); return 0;
c=a+b+c; }
c.show();
c=b=a;
c.show();
b.show();
++c; // prefix increment
c.show();
c++;//postfix increment
c.show();
a=++c;
123

Overloading [ ]
l The last operator we will overload is the [] array subscripting
operator. In C++, the [] is considered a binary operator when
it is overloaded.
l The [] can only be overloaded relative to a class, and only by
a member function. Therefore, the general form of a member
operator[ ]() function is:

type class-name::operator[](int index)


{
// ……
}

l Technically, the parameter does not have to be of type int,


but operator[]() functions are typically used to provide array
subscripting, so an integer value is generally used. 124
l Given an object called O, this expression

O[3]

Translates into this call to the operator[]() function: operator[](3)

l That is, the value of the expression within the subscripting


operator is passed to the operator[]() function explicitly.
l The this pointer will point to O, the object that generated the
call.
l In the following program , atype declares an array of three
integers. Its constructor function initialize each member of the
array. The overloaded operator[]() function returns the value of
the element specified by its parameter.

125

#include <iostream>
using namespace std;
const int SIZE=3;
class atype {
int a[SIZE];
public:
atype() {
register int i;
for(i=0; i<SIZE;i++) a[i] = i;
}
int operator[](int i) {return a[i];}
};
int main()
{
atype ob;
cout<<ob[2]; // ob.operator[](2)
return 0;
}

l In this slide, the initialization of the array a by the


constructor.
126
l It is possible to design the operator[]() function in such a way that the []
can be used on both the left and right sides of an assignment statement.
To do this, simply specify that the return value of operator[]() be a
reference.
const int SIZE=3;
class atype {
int a[SIZE];
public:
atype() {
register int i;
for(i=0; i<SIZE;i++) a[i] = i; }
int &operator[ ](int i) {return a[i];} //call by reference allows it o be L-
Value};
void main()
{ atype ob;
cout<<ob[2]; // display 2
cout<<“ “;
ob[2]=25; // [ ] on left of =
cout<<ob[2]; // now display 25 }

l (in the previous program) Because operator[]() now returns a


reference to the array element indexed by i, it can now be used on
the left side of an assignment statement to modify an element of
the array.

l One advantage of being able to overload the [ ] operator is that it


provides a means of implementing safe array indexing. As you
know, in C++, it is possible to overrun an array boundary at run
time without generating a run-time error message. However, if you
create a class that contains the array, and allow access to that
array only through the overloaded [ ] subscripting operator, then
you can intercept an out-of-range index. For example, the
program shown next adds a range check to the preceding
program, and proves that it works.

128
#include <iostream.h> main()
#include <stdlib.h> {
const int SIZE=3; atype ob;
class atype { cout<<ob[2]; // display 2
int a[SIZE];
cout<<“ “;
public:
ob[2]=25; // [ ] on left of =
atype() {
cout<<ob[2]; // display 25
register int i;
for(i=0; i<SIZE;i++) a[i] = i; ob[3] = 44; // generates
//runtime error, 3
}
//out=of-range
int &operator[ ](int i);
return 0;
};
// provide range checking for atype }
int &atype::operator[ ](int i) { In this program, when the
if(i<0 || i>SIZE-1) { statement ob[3]=44;
executes, the boundary
cout<<“\nindex value of “;
error is intercepted by
cout<<i<<“ is out-of-range.\n”;
operator[ ](), and the
exit(1); program is terminated befor
} any damage can be done.
return a[i]; 129

l Passing Objects to Functions


l Call by value,
l Call by reference
l How Constructors and Distructors work when passing objects to
functions
l A potential problem when passing objects, Freeing the memory
l This pointer , how and when its used
l Operator Overloading, different cases
l +
l =
l prefix ++ : ++OP
l postfix ++ : OP++
l b= -a
l when to use temp and return it.
l when to return *this.
l Overloading a relational operator

l Friend operator overloading


l The difference between Friend and Member operator overloading
l O=10+O
l Using a Friend to Overload a Unary Operator
. Overloading [ ]
130
We often need to use existing classes to define new classes. The two ways to do this are
called composition and the inheritance.
This chapter describes both methods and shows how to decide when to use them

Inheritance

l In order to derive a class from another, we must use the operator:


(colon) in the declaration of the derived class in the following
way:
class derived_class_name: public base_class_name;
l where derived_class_name is the name of the derived class and
base_class_name is the name of the class on which it is based.
public may be replaced by any of the other access specifiers
protected or private, and describes the access for the inherited
members
131

#include <iostream.h>
class CPolygon {
protected:
base class int width, height;
public:
void set_values (int a, int b){ width=a; height=b;} };

class CRectangle: public CPolygon { //inherit


public: 20
int area (void) { return (width * height); } }; 10

class CTriangle: public CPolygon {


public:
int area (void) { return (width * height / 2); } };

void main () {
CRectangle rect;
CTriangle trgl;
rect.set_values (4,5);
trgl.set_values (4,5);
cout << rect.area() << endl; 132
cout << trgl.area() << endl; }
l Objects of classes CRectangle and CTriangle each contain
members of CPolygon, that are: width, height and set_values().
The protected specifier is similar to private, its only difference
occurs when deriving classes. When we derive a class, protected
members of the base class can be used by other members of the
derived class. Nevertheless, private member cannot. Since we
wanted width and height to have the ability to be manipulated by
members of the derived classes CRectangle and CTriangle and
not only by members of CPolygon, we have used protected
access instead of private.

133

l We can summarize the different access types


according to whom can access them in the
following way:

Access Public protected private

members of the
yes yes yes
same class

members of
yes yes no
derived classes

not-members yes no no
134
l where "not-members" represent any reference from outside the
class, such as from main(), from another class or from any
function, either global or local. In above example, the members
inherited by CRectangle and CTriangle follow with the same
access permission as in the base class CPolygon:
CPolygon::width // protected access
CRectangle::width // protected access
CPolygon::set_values() // public access
CRectangle::set_values() // public access
This is because we have derived a class from the other as public,
remember:
l class CRectangle: public CPolygon;

l This public keyword represents the minimum level of protection


that the inherited members of the base class (CPolygon) must
acquire in the new class (CRectangle). The minimum access
level for the inherited members can be changed by specifying
protected or private instead of public. For example, daughter is
a class derived from mother that we defined thus:
class daughter: protected mother;
l This would establish protected as the minimum access level for
the members of daughter that it inherited from mother. That is,
all members that were public in mother would become
protected in daughter, that would be the minimum level at
which they can be inherited. Of course, this would not restrict
that daughter could have its own public members. The
minimum level would only be established for the inherited
members of mother.
l The most common use of an inheritance level
different from public is private that serves to
completely encapsulate the base class, since, in
that case, nobody except its own class will be
able to access the members of the base class
from which it is derived.
l Anyway, in most cases classes are derived as
public.
l If no access level is explicitly written, private
is assumed for classes created with the class
keyword

class A{
int i;
protected:
int j;
public :
int k;
};

class B :public A{
int x;
public:
int y;
int get_i(){
return i;} //cannot access private member declared in class 'A'
int get_j(){
return j;}
};

void main(){
B OP;
OP.i=5; // cannot access private member declared in class 'A'
OP.j=6; // cannot access protected member declared in class 'A'
OP.k=7;
138
OP.x=8; //cannot access private member declared in class 'B'
{ ;OP.y=9
class A{
int i;
protected:
int j;
public :
int k;

};
class B : A { // class B: private A {
int x;
public:
int y;

int get_j(){
return j;}
};

void main(){
B OP;
OP.k=7; // cannot access public member declared in class 'A'
OP.y=9; 139
}

Granting Access:
l When a base class is inherited as private, all members of
that class (public or protected) become private members of
the derived class.
l However, in certain circumstances, you may want to restore
one or more inherited members to their original access
specification. For example, you might want to grant certain
public members of the base class public status in the derived
class even though the base class is inherited as private.
l To do this, you must use an access declaration within the
derived class. An access declaration takes this general form:
base-class::member;

140
class base {
public:
int j; // public in base};

// Inherit base as private


class derived: private base {
public:
base::j; // make j public again
// …….
};

l You can use an access declaration to restore the access rights of


public and protected members.
l However, you cannot use an access declaration to raise or lower a
member’s access status. For example, a member declared as private
within a base class cannot be made public by a derived class. (allowing
this would destroy encapsulation!).

class base {
int i; // private to base
void main()
public: {
int j, k; derived ob;
void seti(int x) {i=x;} //ob.i=10; illegal because
int geti() {return i;}}; ob.j=20; /* legal j is made public*/
//ob.k=30;/*illegal k is private */
class derived: private base { ob.a=40;/* legal a is public*/
/* The next three statements override ob.seti(10);
base’s inheritance as private and cout<<ob.geti()<<“ “;
restore j, seti( ) and geti( ) to public cout<<ob.j<<“ “<<ob.a;
access. */ }
public:
base::j; // make j public again
base::seti; // make seti( ) public
base::geti; // make geti( ) public
// base::i; //illegal, you cannot elevate
access
int a; }; // public 142
What is inherited from the base class?
l In principle every member of a base class is inherited by a derived
class except:

Constructor and destructor


operator=() member
friends

l The default constructor (i.e. constructor with no parameters) and the


destructor of the base class are always called when a new object of a
derived class is created or destroyed.
l Other constructors and destructors of the base class are not inherited,
l If the base class has no default constructor or you want that an
overloaded constructor is called when a new derived object is created,
you can specify it in each constructor definition of the derived class:

derived_class_name (parameters): base_class_name (parameters) {}

// constructors and derivated classes


#include <iostream.h>
class mother {
public:
mother ( ) {
cout << "mother: no parameters\n"; } mother: no parameters
mother (int a) { daughter: int parameter
cout << "mother: int parameter\n"; } };
mother: int parameter
class daughter : public mother { son: int parameter
public:
daughter (int a) { cout << "daughter: int parameter\n\n"; } };

class son : public mother {


public:
son (int a) : mother (a) { cout << "son: int parameter\n\n"; } };

void main () {
daughter cynthia (1); son daniel(1);}
144
l Observe the difference between which mother's
constructor is called when a new daughter object
is created and which when it is a son object. The
difference is because the constructor declaration
of daughter and son:

daughter (int a) // nothing specified: call default


constructor
son (int a) : mother (a) // constructor specified: call this one
145

Multiple inheritance
l In C++ it is perfectly possible that a class inherits fields and
methods from more than one class simply by separating the
different base classes with commas in the declaration of the
derived class. For example, if we had a specific class to
print on screen (COutput) and we wanted that our classes
CRectangle and CTriangle also inherit its members in
addition to those of CPolygon we could write:

class CRectangle: public CPolygon, public COutput {}


class CTriangle: public CPolygon, public COutput { }
146
#include <iostream.h>

class CPolygon {
protected:
int width, height;
public:
void set_values (int a, int b) { width=a; height=b;}};
void main () {
CRectangle rect;
class COutput {
CTriangle trgl;
public:
rect.set_values (4,5);
void output (int i); };
trgl.set_values (4,5);
rect.output (rect.area());
void COutput::output (int i){ cout << i << endl; }
Trgl.output (trgl.area());}
class CRectangle: public CPolygon, public COutput {
public:
int area (void) { return (width * height); } };
20
class CTriangle: public CPolygon, public COutput { 10
public:
int area (void) { return (width * height / 2); }};

147

class name{ Another example


char n[20]; on multiple
protected:
inheritance
void set_name(char * N){
strcpy(n,N); }
void print_name(){cout<<n<<" ";}};

class ID{ int id;


protected:
void set_id(int i){ id=i;}
void print_id(){cout<<id<<endl;}};
148
class student :public name, public ID{
public:
student(char *Name,int Id)
{ set_name(Name);
set_id(Id); }

void print(){
print_name();
print_id();} };

void main()
{student ST("Ahmad",1000901123);
ST.print();
}
149

Exercise 1
l 1- Develop a class named person which can perform the following
functions, individually:
l can set name, nationality and date of birth
l can print name, nationality and date of birth
l Now inherit a class named student and teacher from the class person,
that can perform the above listed task and in addition, the new class
should also perform the following functions, individually.
l For student:
l Can set his semester and CGPA
l Can display his semester and GPA
l For teacher:
l Can set subject he is teaching
l Can display subject he is teaching
l Write a main program to test above classes. For example, create student
object and display his name, nationality, date of birth, semester and
CGPA. Similarly, create object for teacher.

150
Exercise 2
l 2- Create a class Shape and its subclasses having different
kinds/types of shape under it. Shape should have data members’
width and height and a constructor that accepts two parameters
width and height. Also create a display() method that will display
the width and height of the shape.
l Shape class should have a subclass Rectangle that should have
a constructor that accepts width and height of Rectangle and a
function that can calculate the area() of rectangle.
l Rectangle should have a subclass Square that should have a
constructor that accepts width and height of Square (use
overriding here) and function that can calculate area() (use the
concept of overriding here) of Square.
l Write a main program and create objects in it.

151

Polymorphism
l For a suitable understanding of this section you
should clearly know how to use pointers and
inheritance between classes.
l Polymorphism in the context of object-oriented
programming, is the ability of one type, A, to
appear as and be used like another type, B. Type
A somehow derives from type B, or type C
implements an interface that represents type B.

152
Pointers to base class

l One of the greater advantages of deriving


classes is that a pointer to a derived class
is type-compatible with a pointer to its
base class.

153

// pointers to base class int main () {


#include <iostream.h> CRectangle rect;
CTriangle trgl;
class CPolygon {
protected: CPolygon * p1,*p2;
int width, height; p1 = &rect;
public: p2 = &trgl;
void set_values (int a, int b) {
width=a; height=b; } }; p1->set_values (4,5);
p2->set_values (4,5);
class CRectangle: public CPolygon { //cout<<p1->area( )<<endl
public: cout << rect.area( ) << endl;
int area (void){ return (width * height); } }; cout << trgl.area( ) << endl;
return 0;}
class CTriangle: public CPolygon {
20
public:
10
int area (void){
return (width * height / 2); } }; Code 154
l The function main creates two pointers that point to objects of
class CPolygon, that are *p1 and *p2. These are assigned to the
addresses of rect and trgl, and because they are objects of classes
derived from CPolygon they are valid assignations.
l The only limitation of using *p1 and *p2 instead of rect and trgl
is that both *p1 and *p2 are of type CPolygon* and therefore we
can only refer to the members that CRectangle and CTriangle
inherit from CPolygon. For that reason when calling the area( )
members we have not been able to use the pointers *p1 and *p2.
l To make it possible for the pointers to class CPolygon to admit
area( ) as a valid member, this should also have been declared in
the base class and not only in its derived ones.

Virtual members

l In order to declare an element of a class


which we are going to redefine in derived
classes we must precede it with the keyword
virtual so that the use of pointers to objects
of that class can be suitable.

156
#include <iostream.h> void main () {
class CPolygon { CRectangle rect;
protected: CTriangle trgl;
int width, height; CPolygon poly;
public: CPolygon * p1, *p2, *p3;
void set_values (int a, int b) p1= &rect;
{ width=a; height=b; }
p2=&trgl;
virtual int area (void) {
p3 = &poly;
return (0); } };
p1->set_values(4,5);
class CRectangle: public CPolygon { p2->set_values(4,5);
public: p3->set_values (4,5);
int area (void){ cout << p1->area() << endl;
return (width * height); } }; cout << p2->area() << endl;
cout << p3->area() << endl;}
class CTriangle: public CPolygon { 20
public: 10
int area (void){ 0
return (width * height / 2); }
157
};

l The three classes (CPolygon, CRectangle and CTriangle) have the


same members: width, height, set_values() and area(). area() has
been defined as virtual because it is later redefined in derived
classes. You can verify if you want that if you remove this word
(virtual) from the code and then you execute the program the result
will be 0 for the three polygons instead of 20,10,0. That is because
instead of calling the corresponding area() function for each object
(CRectangle::area(), CTriangle::area() and CPolygon::area(),
respectively), CPolygon::area() will be called for all of them since
the calls are via a pointer to CPolygon. Therefore, what the word
virtual does is to allow a member of a derived class with the same
name as one in the base class be suitably called when a pointer to it
is used

Note that in spite of its virtuality we have also been able


to declare an object of type CPolygon and to call its
area() function, that always returns 0 as the result.
Abstract base classes
l Abstract classes are similar to the class CPolygon of our previous
example. The only difference is that in our previous example we have
defined a valid area() function for objects that were of class CPolygon
(like object poly), whereas in an abstract base class we could have
simply left without defining this function by appending = 0 to the
function declaration.
l The class CPolygon could have been thus:

// abstract class CPolygonclass


CPolygon {
protected:
int width, height;

public:
void set_values(int a, int b){width=a; height=b;}
virtual int area (void) =0; };

l This type of function is called a pure virtual function, and all


classes that contain a pure virtual function are considered
abstract base classes.
l The greatest difference of an abstract base class is that instances
(objects) of it cannot be created, but we can create pointers to
them. Therefore a declaration likes:

CPolygon poly; // incorrect


CPolygon * ppoly1; //correct

This is because the pure virtual function that it includes is not


defined and it is impossible to create an object if it does not have
all its members defined. A pointer that points to an object of a
derived class where this function has been defined is perfectly
valid.
#include <iostream.h>
class CPolygon {
protected: void main ( ) {
int width, height; CRectangle rect;
CTriangle trgl;
public:
CPolygon * ppoly1 = &rect;
void set_values (int a, int b) CPolygon * ppoly2 = &trgl;
{ width=a; height=b; } ppoly1->set_values (4,5);
virtual int area (void) =0; }; ppoly2->set_values (4,5);
cout << ppoly1->area() <<
class CRectangle: public CPolygon { endl;
public: cout << ppoly2->area() <<
endl;
int area (void){
}
return (width * height); } }; 20
10
class CTriangle: public CPolygon {
public:
int area (void){
return (width * height / 2); }
};

l If you review the program you will notice that we


can refer to objects of different classes using a
unique type of pointer (CPolygon*). This can be
tremendously useful. Imagine, now we can create
a function member of CPolygon that is able to
print on screen the result of the area() function
independently of what the derived classes are.

162
class Polygon {
protected: int width, height;
public:
void set_values (int a, int b){ width=a; height=b; }
virtual int area (void) =0;
void printarea (void){ cout << this->area( ) << endl; }
};

class Rectangle: public Polygon {


public:
int area (void){ return (width * height); } };

class Triangle: public Polygon {


public:
int area (void){ return (width * height / 2); } };
‫واﺋﻞ ﻗﺼﺎص‬
163

void main ( ) {
Rectangle rect;
Triangle trgl;
Polygon * p1 = &rect;
Polygon * p2 = &trgl;
p1->set_values (4,5);
p2->set_values (4,5);
p1->printarea( );
p2->printarea( );
}
164
l Remember that this represents a pointer to the object whose
code is being executed.

l Abstract classes and virtual members grant to C++ the


polymorphic characteristics that make object-oriented
programming such a useful instrument. Of course we have
seen the simplest way to use these features, but imagine these
features applied to arrays of objects or objects assigned
through dynamic memory.

165

Early Versus Late Binding

l Early binding and late binding. These terms refer to


events that occur at compile time and events that occur at
run time.

l Early binding means that a function call is resolved at


compile time. All information necessary for determining
which function will be called is known when the program
is compiled. (i.e. standard function calls, overloaded
function calls, and overloaded operator function calls).
The principle advantage to early binding is efficiency-it is
faster, and it often requires less memory. Its
disadvantage is lack of flexibility.
166
l Late binding means that a function call is resolved at run
time. Thus, precisely when function will be called is
determined “on the fly” as the program executes. Late
binding is a achieved in C++ through the use of virtual
functions and derived types. The advantage to late
binding is that it allows greater flexibility.
l It can be used to support a common interface, while
allowing various objects that utilize that interface to define
their implementations.
l Help in create class libraries, which can be reused and
extended. Its disadvantage, however, is a slight loss of
execution speed.

l Most large programs use a combination of both. Late


binding is one of the most powerful features of C++.
However, the price you pay for this power is that your
program will run slightly slower. Therefore, it is best to
use late binding only when it meaningfully adds to the
structure and manageability of your program. Keep in
mind, however, that the loss of performance caused by
late binding is very slight so when the situation calls for
late binding, you should most definitely use it.

168
Function templates
l Templates allow creating generic functions that accept any data
type as parameters and return a value without having to overload
the function with all the possible data types. Its prototype is any
of the two following ones:

l template <class identifier> function_declaration;


template <typename identifier> function_declaration;

l The difference between both prototypes is the use of keyword


class or typename, its use is indistinct, since both expressions
have exactly the same meaning and behave exactly the same
way.
l For example:
template <class GenericType>
GenericType GetMax(GenericType a, GenericType b){
return (a>b?a:b); }

l We have called GenericType. Therefore in the function that follows,


GenericType becomes a valid data type and it is used as the type for
its two parameters a and b and as the return type for the function
GetMax. GenericType still does not represent any concrete data
type; when the function GetMax will be called we will be able to call
it with any valid data type. This data type will serve as a pattern and
will replace GenericType in the function. The call to a template
class is as follows:

l function <pattern> (parameters);

int x=5,y=7,z;
z=GetMax <int> (x,y);

GetMax will be called as if each appearance of GenericType was


replaced by an int expression.
#include <iostream.h>
template <class T>
T GetMax (T a, T b) {
T result;
if (a>b) result= a;
else result= b; // result = (a>b)? a : b;
return (result); } 6
10
void main () {
int i=5, j=6, k;
long l=10, m=5, n;

k=GetMax<int>(i,j);
n=GetMax<long>(l,m);

cout << k << endl;


cout << n << endl; }
171

l We have written a function template and called it with two


different patterns. In concrete case where the generic T type is
used as a parameter for function GetMax, the compiler can find
out automatically which data type is passed to it without having
to specify it with patterns <int> or <long>. So we could have
written:

l int x=5, y=7, z;


z=GetMax (i,j);

l i and j are of type int the compiler would assume automatically


that the type is int. This implicit method is more usual and would
produce the same result:
// function template II
#include <iostream.h>
template <class T>
T GetMax (T a, T b) { return (a>b?a:b); }
6
int main () { 10
int i=5, j=6, k;
long l=10, m=5, n;
k=GetMax(i,j);
n=GetMax(l,m);
cout << k << endl;
cout << n << endl;
return 0;}

173

l Because our template function includes only one data type (class
T) and both arguments it admits are both of that same type, we
cannot call our template function with two objects of different
types as parameters:

int i;
long l;
k = GetMax (i,l);

l This would be incorrect, since our function waits for two


arguments of the same type (or class). We can also make
template-functions that admit more than one generic class or data
type. For example:

template <class T, class U>


T GetMin (T a, U b) {return (a<b?a:b); }
l In the previous case, our template function GetMin() admits two
parameters of different types and returns an object of the same type as
the first parameter (T) that is passed. For example, after that declaration
we could call the function by writing:

int i,j;
long l;
j=6;
l=7;
i = GetMin<int,long> (j,l);

or simply
i = GetMin (j,l);

#include <iostream.h>
class C{ int i,j;
public :
C (){}
C(int a,int b){ i=a; j=b;}

bool operator>(C W)
{ if (i>W.i && j>W.j)return true;
else return false;} };

template <class T,class U>


T GetMax (T a, U b) {
T result;
if (a>b) result= a;
else result= b; // result = (a>b)? a : b;
return (result); }

void main () {
C x(4,5), y(4,6) ,z;
int t=5, u= 7, v;
z=GetMax(x,y); 176
v=GetMax(t,u); }
Class templates
l In class templates, so that a class can have members based on
generic types that do not need to be defined at the moment of
creating the class or whose members use these generic types. For
example:

template <class T>


class pair {
T values [2];
public:
pair (T first, T second)
{ values[0]=first; values[1]=second; }};

Class to store two elements of any valid type.

pair<int> myobject (115, 36);


pair<float> myfloats (3.0, 2.18);

l The only member function has been defined


inline within the class declaration.
l If we define a function member outside the
declaration we must always precede the
definition with the prefix template <... >.

178
#include <iostream.h>
template <class T> class pair {
T value1, value2;
public:
pair (T first, T second)
{value1=first; value2=second;}
T getmax ( ){
T temp;
if (value1>value2) temp = value1 ;
else temp = value2;
return temp; }
}; Output
100

void main () {
pair <int> myobject (100, 75);
cout << myobject.getmax();
} 179

#include <iostream.h>
template <class T> class pair {
T value1, value2;
public:
pair (T first, T second)
{value1=first; value2=second;}
T getmax (); };

template <class T>


T pair<T>::getmax ( ) {
T temp;
temp = value1>value2 ? value1 : value2; 100
return temp; }

void main () {
pair <int> myobject (100, 75);
cout << myobject.getmax();} 180
l notice how the definition of member function
getmax begins:

template <class T>


T pair<T>::getmax ()

l All Ts that appear are necessary because whenever


you declare member functions you have to follow a
format similar to this (the second T makes reference
to the type returned by the function, so this may
vary).

181

Template specialization

l A template specialization allows a template to


make specific implementations when the pattern
is of a determined type.
l For example, suppose that our class template pair
included a function to return the result of the
module operation between the objects contained
in it, but we only want it to work when the
contained type is int.
l For the rest of the types we want this function to
return 0. This can be done the following way:
182
#include <iostream.h>
template <class T>
class pair { 25
T value1, value2; 0
public:
pair (T first, T second)
{value1=first; value2=second;} int main ( ) {
T module () {return 0;}}; pair <int> myints (100,75);
pair <float> myfloats (100.0,75.0);
cout << myints.module() << '\n';
template < > class pair <int> { cout << myfloats.module() << '\n';
int value1, value2; return 0;}
public:
pair (int first, int second)
{value1=first; value2=second;}

int module( )
{ return ( value1%value2); }
};
183

#include <iostream.h>
template <class T>
class pair { 25
T value1, value2; 0
public:
pair (T first, T second)
{value1=first; value2=second;}
int main () {
T module () {return 0;}};
pair <int> myints (100,75);
pair <float> myfloats (100.0,75.0);
template < > class pair <int> { cout << myints.module() << '\n';
int value1, value2; cout << myfloats.module() << '\n';
public: return 0;}
pair (int first, int second)
{value1=first; value2=second;}
int module ();};

template < >


int pair<int>::module() {
return value1%value2; } 184
l As you can see in the code the specialization is defined this way:
template < > class class_name <type>
l The specialization is part of a template, for that reason we must
begin the declaration with template < >. And indeed because it is
a specialization for a concrete type, the generic type cannot be
used in it and the first angle-brackets < > must appear empty.
l After the class name we must include the type that is being
specialized enclosed between angle-brackets < >.

l When we specialize a type of a template we must also


define all the members equating them to the
specialization (if one pays attention, in the example
above we have had to include its own constructor,
although it is identical to the one in the generic
template).
l The reason is that no member is "inherited" from the
generic template to the specialized one.

186
Parameter Values for Templates

l Besides the template arguments preceded by the


class or typename keywords that represent a
type, function templates and class templates can
include other parameters that are not types
whenever they are also constant values, like for
example values of fundamental types.

187

#include <iostream.h>
template <class T, int N> class array {
T memblock [N];
public:
void setmember (int x, T value);
T getmember (int x); };

template <class T, int N>


void array <T, N>:: setmember (int x, T value) {
100
memblock[x]=value;}
3.1416
template <class T, int N>
T array<T, N>:: getmember (int x) {
return memblock[x];}

void main () {
array <int,5> myints;
array <float,5> myfloats;
myints.setmember (0,100);
myfloats.setmember (3,3.1416);
cout << myints.getmember(0) << '\n';
‫واﺋﻞ ﻗﺼﺎص‬ 188

cout << myfloats.getmember(3) << '\n';}


l It is also possible to set default values for any template parameter
just as it is done with function parameters. Some possible
template examples seen before:

l template <class T> // The most usual: one class parameter.


l template <class T, class U> // Two class parameters.
l template <class T, int N> // A class and an integer.
l template <class T = char> // With a default value.

189

Exception handling

l During the development of a program, there may be some cases


where we do not have the certainty that a piece of the code is going
to work right, either because it accesses resources that do not exist or
because it gets out of an expected range, etc...
l These types of situations are included in what we consider
exceptions and C++ has recently incorporated three new operators to
help us handle these situations: try, throw and catch.
l try { // code to be tried
throw exception; }
catch (type exception) { // code to be executed in case of exception
}
And its operation:

- The code within the try block is executed normally. In case that
an exception takes place, this code must use the throw keyword
and a parameter to throw an exception. The type of the parameter
details the exception and can be of any valid type.

- If an exception has taken place, that is to say, if it has executed


a throw instruction within the try block, the catch block is
executed receiving as parameter the exception passed by throw.

Example 1
#include <iostream.h>
void main () {
try {
int answer;
cout<<"5+7 =";
cin >> answer;
if (answer!=12) throw "Error, Wrong answer\n";

cout<<"Good, Right answer\n";


}

catch (char * str) { cout << "Exception: " << str ; }


}

192
Example 2: with compile error
#include <iostream.h>
void main () {
try {
int answer;
cout<<"5+7 =";
cin >> answer;
if (answer!=12) throw "Error, Wrong answer\n";

cout<<"Good, Right answer\n";


}
cout<<"End of try\n"; // Error: " no catch handlers"
catch (char * str) // Error:" no try block associated with this catch handler "
{ cout << "Exception: " << str ; }
}
193

#include <iostream.h>
void main () {
char myarray[10];
try
Example 3
{ int L;
cout<<"Enter last value ";
cin>>L;
for (int n=0; n<=L; n++)
{
if (n>9)
throw "Out of range";

myarray[n]='z';
cout<<n<<" "; 0 1 2 3 4 5 6 7 8 9 Exception: Out of range
} } Finish

catch (char * str)


{ cout << "Exception: " << str << endl; }

cout<<"\nfinish\n";
} 194
l In this example, if within the n loop, n gets to be more than 9 an
exception is thrown, since myarray[n] would in that case point to
a non-trustworthy memory address.
l When throw is executed, the try block finalizes right away and
every object created within the try block is destroyed.
l After that, the control is passed to the corresponding catch block
(that is only executed in these cases).
l Finally the program continues right after the catch block, in this
case: cout<<"\nfinish"; return 0;
l The parameter that catch accepts can be of any valid type.
l Even more, catch can be overloaded so that it can accept
different types as parameters. In that case the catch block
executed is the one that matches the type of the exception sent
(the parameter of throw):

#include <iostream.h>
void main () {
try {
int A[10]={2,3,4,1,3,5,6,8,9};
int index;
cout<<"Enter Index: ";
cin >> index;
if (index<0) throw "Error, Negative index\n";
if (index>=10) throw index;

for (int n=0; n<=index; n++)


cout<<A[n]<<endl;
} // End of try block
catch (int i) { cout << "Exception: ";
cout << "index " << i << " is out of range" << endl; }

catch (char * str) { cout << "Exception: " << str ; }


196
} // End of main
#include <iostream.h>
int main () {
try {
char * mystring;
mystring = new char [10];
if (mystring == NULL) throw "Allocation failure";
for (int n=0; n<=100; n++) {
if (n>9) throw n;
mystring[n]='z'; } Exception: index 10 is out of range
}

catch (int i) {
cout << "Exception: "; cout << "index " << i << " is out of range"
<< endl; }

catch (char * str) {


cout << "Exception: " << str << endl; }
return 0; }
197

l In this case there is a possibility that at least


two different exceptions could happen:
l That the required block of 10 characters
cannot be assigned (something rare, but
possible): in this case an exception is thrown
that will be caught by catch (char * str).
l That the maximum index for mystring is
exceeded: in this case the exception thrown
will be caught by catch (int i), since the
parameter is an integer number.

198
Standard exceptions
l Some functions of the standard C++
language library send exceptions that can be
captured if we include them within a try block.
These exceptions are sent with a class
derived from std::exception as type. This
class (std::exception) is defined in the C++
standard header file <exception> and serves
as a pattern for the standard hierarchy of
exceptions:
199

File Input-Output (I/O) in C++


l C++ provides the following classes to perform output
and input of characters to/from files:
l These classes are derived directly or indirectly from
the classes istream, and ostream.
l 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.
l 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:
200
Write to file
// basic file operations
#include <iostream>
#include <fstream>
using namespace std;
void main () {
ofstream myfile;
myfile.open ("c:/example.txt", ios::binary);
myfile <<"Welcome to C++ files\n";
myfile <<" Written By Dr Wael \n";
myfile.close();
}
201

Another example on file write


// basic file operations
#include <iostream>
#include <fstream>
using namespace std;
void main () {
ofstream myfile;
char fname[20];
cout<<"Enter file name ";
cin.getline(fname,20);
myfile.open (fname, ios::binary);
int mark;
for (int i=1;i<=10;i++)
{ cout<<"Enter a mark ";
cin>>mark;
myfile <<mark<<"\r\n";}

myfile.close(); 202

}
Read files
// read file , letter by letter
#include <iostream>
#include <fstream>
using namespace std;
void main () {
char letter;
ifstream infile("c:/example.txt", ios::binary);
while(!infile.eof() )
{
letter=infile.get();
cout<< letter <<endl;
}
}

203

Read files
// read file , line by line
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void main () {
string line;
ifstream infile("c:/example.txt", ios::binary);
while(!infile.eof() )
{
getline(infile,line);
cout<< line <<endl;
} 204

}
// read file , word by word
#include <iostream>
#include <fstream>

using namespace std;


void main () {
char line[80];
ifstream infile("c:/example.txt", ios::binary);
while(!infile.eof() )
{
infile.getline(line,20,' ');
cout<< line <<endl;
} 205

// basic file operations


#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void main () {
ofstream myfile;
myfile.open ("example.txt", ios::binary);
myfile << "Writing this to a file.\n";
myfile.close();
string line;
ifstream infile("example.txt", ios::binary);

while(!infile.eof() )
{ getline(infile, line);
cout<< line <<endl;}
}
206
Read numbers from file
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void main () {
ifstream myfile;
char fname[20];
cout<<"Enter file name ";
cin.getline(fname,20);
myfile.open (fname, ios::binary);
char number[10];
int Sum=0;
for (int i=1;i<=10;i++)
{ myfile.getline(number,10);
Sum=Sum+atoi(number);}

cout<<Sum; 207

Built in functions
l 1- write( (char *) (&ObjName), sizeof(class_name) );
l Takes the first argument a char pointer for the data to be
stored and the second argument as size of the memory
to be stored in file.
l 2- read( (char *) (&ObjName), sizeof(data type) );
l Takes the first argument a char pointer for the variable /
object in which retrieved data (from file) is to be stored
and the second argument as size of the memory to be
read.

208
#include <iostream.h>
#include <string>
class C{
char Fname[10],Lname[10];
public:
C(char * F, char * L)
{strcpy(Fname,F);
strcpy(Lname,L);
}};

void main()
{
C OB1("Ahmad","Ali");
cout.write((char *)(&OB1),20 );
}
209

Writing Object to file


#include <iostream.h>
#include <fstream.h>
#include <string>
class C{
int A,B;
char name[20];
public:
C ( char *N,int X,int Y)
{strcpy(name,N);
A=X,B=Y;}
};
void main()
{ C OB("Wael",5,7);
ofstream F;
F.open("C:/example.txt",ios::binary);
F.write((const char *) (&OB),sizeof(OB));
F.close();
}
210
Reading Object from file
#include <iostream.h>
#include <fstream.h>
#include <string>
class C{
int A,B;
char name[20];
public:
void print(){cout<<name<<" "<<A<<" "<<B<<endl;}
};
void main()
{ C OB;
ifstream F;
F.open("C:/example.txt",ios::binary);
F.read((char *) (&OB),sizeof(OB));
OB.print();
}
211

Opening a file
l 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.
l In order to open a file with a stream object we use its member
function open():
l open (filename, mode);
l 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:
212
l Mode Meaning
l ios::in Open for input operations.
l ios::out Open for output operations.
l ios::binary Open in binary mode.
l ios::ate Set the initial position at the end of the file.
If this flag is not set to any value, the initial position is the
beginning of the file.
l ios::app All output operations are performed at the
end of the file, appending the content to the current
content of the file. This flag can only be used in streams
open for output-only operations.

213

l ios::truncIf the file opened for output operations


already existed before, its previous content is deleted
and replaced by the new one.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():
l 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:
214

Das könnte Ihnen auch gefallen