2. Course Outline
Introduction to Object Oriented Programming
(OOP):
Overview of programming languages. * What is object
Oriented? * Benefits of Object Oriented. * What is an
object? * What is a class? * Black boxes and interfaces.
* Attributes and methods. * What is abstract class
(abstraction)?
classes
Class fundamentals. * A closer look at class member
access. * Constructors and destructors. * Parameterized
constructors. * Classes and structures are related. *
Unions and classes are related. * Inline functions. *
Creating inline functions inside a class. * Arrays of
object. * Pointers to object. * Object references.
وائل قصاص 2
3. Friend functions
Friend functions. * Overloading constructor functions. *
Dynamic initialization. * Assigning objects. * Passing
objects to functions. * Returning objects. * Creating and
using copy constructor. * The this keyword.
Operator Overloading
Operator Overloading using member functions. * Friend
operator functions. * A closer look at the assignment
operator. * Overloading new and delete. * Overloading []. *
Overloading other operators.
Inheritance
Introducing inheritance. * Base class access control. * Using
protected members. * Inheriting Multiple Base Classes. *
Constructors, destructors, and inheritance. * Granting
access. * Virtual base class.
Virtual functions and polymorphism
Pointers to derived class. * Virtual functions. * Virual
functions are inherited. * Pure virual functions and Abstract
classes. * Early versus late binding.
Templates and Exception handling
Generic functions. * A function with two generic types. *
Exception handling. * Options for exception handling. *
Generic classes. * Eception Handling. * Using Multiple catch
statements. * Catching all exceptions.
4. Text book and references
Title : C++ How to Program
Author(s) Paul J. Deitel, Harvey M. Deitel
Edition : 4th
Publisher : Prentice Hall
*************************************************************
Title : C++ From the GROUND UP
Author(s) R. Schildt
Edition: 3rd
Pubisher: McGraw-Hill
وائل قصاص 4
5. • First Exam 15
• Second Exam 15
• Lab 20
• Final Exam 50
Grading Policy
وائل قصاص 5
6. 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.
7. 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:
private members of a class are accessible only from
other members of their same class or from their
"friend" classes.
protected members are accessible from members of
their same class and friend classes, and also from
members of their derived classes.
public members are accessible from anywhere the
class is visible.
وائل قصاص 7
8. 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;
وائل قصاص 8
9. 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.
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
rect.set_value(3,4);
myarea = rect.area();
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.
11. 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 ::
The scope operator (::) specifies the class to
which the member being declared belongs.
وائل قصاص 11
12. 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.
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.
وائل قصاص 12
13. #include< iostream.h>
#include <cstring> Example
class student {
char name[20];
int First,second,final,total,ID;
int calculate_total( );
public:
student()
{ strcpy(name,"AHMAD");
ID=900901123;
First=second=final=0; }
int get_total(){return total;}
void print(void)
{cout<<name<<"t"<<ID<<"t"<<calculate_total( )<<endl;}
}; // end of class definition
int student::calculate_total( ){total=First+second+final;
return total;}
وائل قصاص 13
15. 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)
وائل قصاص 15
is CRectangle, of which there are two instances, or
16. Quiz
Design a class named Student that has a
name, ID, first,second, final and Total
marks.
This class has a calculate_total method
which is has a private permission
The class has set_Name_ID( char[ ],int)
method that stores the name of the student
The class has set_marks(int,int,int) method
The class has print( ) method that calls the
calculate_total and then prints the student
name , student ID and student total mark.
The main function looks like this:
void main( ){
Student St;
St.set_Name_ID(“Ahmad”,1000901123);
St.set_marks(19,21,41);
St.print( ); }
وائل قصاص 16
17. Example:
// 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
وائل قصاص 17
19. Constructors and Destructors
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.
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.
In order to avoid that, a class can include
وائل قصاص 19
20. Example:
“A
construct
or never
returns a
value nor
does the
void have
to be
specified”
21. For the student example add a constructor
method that prints a message indicating
that an object has been constructed.
وائل قصاص 21
22. class Student {
char name[20]; Example
int first,second,final,total,ID;
on
Constructors
void calculate_total( );
public:
Student( )
{ cout<<"An object has been constructedn";}
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
وائل قصاص 22
24. 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;}
وائل قصاص 24
25. Overload the constructor so it can take the
name and the ID at the creation of the
instance.
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();
وائل قصاص 25
26. Example:
Empty Constructor
If we didn’t define a constructor then there is
an empty constructor as default
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();
}
وائل قصاص 26
27. With no constructors, the compiler automatically
assumes that it has empty constructor:
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.
CExample::CExample () { };
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.
◦ Perform simple variable by variable
assignment: copy of all the components of one
object to another.
◦ Passing an object to a function. It is a
constructor with one parameter of same type that
28. CExample::CExample (const CExample&
rv)
create a new object of
class CExample (rv) {a=rv.a; b=rv.b;
c=rv.c;}
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
وائل قصاص 28
29. Overloading Constructors
A constructor can also be overloaded with
several functions that have the same name
but different types or numbers of
parameters.
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:
29
class CExample { public:
31. 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. Notice that if we declare
a new object and we do not want to pass
parameters to it we do not include
parentheses ( ):
CRectangle rectb; // right
وائل قصاص 31
32. Destructor
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.
The destructor must have the
same name as the class with a
33. Build a Destructor that prints a messege
denoting when an object is being distructed
~Student()
{
cout<<name<<"nAn object has been
destructedn";
}
وائل قصاص 33
34. 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 functionn";
}
وائل قصاص 34
35. The new keyword allocate memory
dynamically at run time , so that the
records are allocated while execution
There is a direct relationship between new
and pointer.
new , delete keywords
وائل قصاص 35
36. Example:
#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;
}
وائل قصاص 36
40. // 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;}
int main () {
CRectangle rect (3,4), rectb (5,6); Example:
cout << "rect area: " << rect.area() << endl; 40
cout << "rectb area: " << rectb.area() << endl; وائل قصاص
41. 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()
{
//……..
Inline functions
}
وائل قصاص 41
42. 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.
وائل قصاص 42
43. class c1 {
int i; If you compile this
public: version of the program,
int get_i(); save its object code, and
void put_i(int j);};
then compile it again with
inline int c1::get_i() the inline specifier
{ return i;} removed, you will see the
inline void c1::put_i(int
j) inline version is several
{ i=j;} bytes smaller.
Inline is a request not a
void main()
{ c1 s; command, that the
s.put_i(10); compiler generate inline
cout<<s.get_i();}
code.
output
example Some compilers will not
10
generate inline code if a
function contains a loop, a
44. Second :Creating Inline Functions
inside a Class
Any function that is defined inside a class
definition is automatically made into an
class c1 { function. It is not necessary to
inline
precede int i; declaration with the keyword
its
public:
//automatic inline functions. inline.
int get_i() {return i;}
void put_i(int j) {i=j;} };
int main()
{ c1 s;
s.put_i(10); Output
10
cout<<s.get_i();
return 0;}
وائل قصاص 44
46. The keyword enum creates a user-defined
type
Examples:
enum semesters {first, second, summer};
enum months{jan=1,feb,mar,apr,may,jun,jul,aug,sept,
oct,nov,dec};
If you print enum variable, the output will be an integer
that represents the order of the value in the definition
starting from 0
For the second example the counting will start from 1
You cant read an enum by cin
You cant make arithmatic operations on enum
You can only assign a value for the enum by using = ,
and make relational operations on enum
Enumeration in C++
وائل قصاص 46
47. Example:
#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 examn";
else
cout<<"you failed in the examn";
}
وائل قصاص 47
48. Array of objects can be created in the same
way that you create arrays of any other
data types.
3.3 Array of object and Pointers to
Objects
وائل قصاص 48
50. Example:
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.
وائل قصاص 50
51. (*p).show_num(); and
p->show_num(); are the same
*p can be read: pointed by p
&x can be read: address of x
x.y can be read: member y of object x
(*p).y can be read: member y of object
pointed by p
p->y can be read: member y of object
pointed by p (equivalent to the previous one)
x[0] can be read: first object pointed by x
x[1] can be read: second object pointed by
x
x[n] can be read: (n+1)th object pointed
by x
وائل قصاص 51
52. 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();
} Example: 52
وائل قصاص
53. class Student {
char name[20];
int first,second,final,total,ID;
public: Another
Student( ) //constructor example
{strcpy(name,"No name") ;
ID=0; on 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
وائل قصاص 53
55. Example:
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();
}
Rewrite the main in the prev.
example with pointers
وائل قصاص 55
56. Example:
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();
}
Rewrite the main in the prev.
example with pointers
وائل قصاص 56
57. Static Class Member
Each object has its own copy of
all data members of the class.
If a data 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.
وائل قصاص 57
58. Example:
// Example on class 29-feb-2012
#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 objectsn";
Student S1,S2;
cout<<"nConstruct 3 objectsn";
Student St[3]; } // end of main
وائل قصاص 58
59. 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
The Output will be:
وائل قصاص 59
61. This type of function can be accessed in two ways:
3. Through any object of that class (ob1.get_count()).
4. Or through the class name using the binary scope of
resolution (::) directly
(cout<<st_example ::get_count();).
Static Member Function
وائل قصاص 61
62. 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 objectsn";
Student S1,S2;
cout<<"nConstruct 3 objectsn";
Student St[3];}
وائل قصاص 62
63. 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
Outbot
وائل قصاص 63
65. 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.
وائل قصاص 65
66. #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
:
}
Keyword const is used to specify non modifiable objects,
and any attempt to modify the const object cause syntax
error.
const time noon(12,0,0);// indicates constant object noon
of class time which is initialized to 12 noon.
وائل قصاص 66
67. Constant Member Function
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
وائل قصاص 67
68. 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
}
وائل قصاص 68
69. Friends (friend keyword)
There are three levels of internal protection
for the different members of a class: public,
protected and private.
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.
وائل قصاص 69
70. Friend Functions
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 declaration that shares its
members.
وائل قصاص 70
71. 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;}
وائل قصاص 71
72. #include <iostream.h>
class CRectangle {
int width, height;
public:
void set_values (int, int);
int area (void) {return (width * height);}
friend CRectangle duplicate (CRectangle); }; // End of Class24
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(); }
وائل قصاص 72
73. 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.
As in the previous example, it would have
been shorter to integrate duplicate()
within the class CRectangle.
A function may be a friend of more
than one class.
وائل قصاص 73
74. #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);
friend int idle(c1 a, c2 b); };
class c2 { int status; Screen can be used
public: Pop up in use
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
usedn”;
else cout << “Pop-up In usen”;
x.set_status(INUSE);
if ( idle(x,y) ) cout <<“Screen can be
usedn”;
else cout <<“Pop-up in usen”;}
وائل قصاص 74
75. 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.
Friend Classes (friend)
وائل قصاص 75
76. #include <iostream.h>
class CSquare;
class CRectangle {
int width, height;
public:
int area (void) {return (width * height);}
void convert (CSquare a); };
class CSquare { 16
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(); }
وائل قصاص 76
77. 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()).
وائل قصاص 77
78. Function Overloading
Struct:
Classes:
Public, private,
scope operator (::)
Empty constructor , Copy constructor
Constructor Destructor
New delete keywords and its relation with Constructor and
destructor
Inline function
Enum
Arrays of objects and relation with pointers
Static object
Static function
Const Object
Const member function.
Friend function
Friend Class
Topic covered up to now
وائل قصاص 78
79. Dynamic initialization
In c++, both local and global variables can
initialized at run time. This process is sometimes
referred to as Dynamic initialization.
Most initializations have used constants.
Under Dynamic initialization, a variable can be
initialized at run time using any c++ expression
valid at the time the variable is declared.
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;
وائل قصاص 79
80. Applying Dynamic Initialization to
Constructors
#include <iostream.h>
class myclass {
int a, b;
public:
void setab(int i, int j) {a=i; b=j;}
void showab(); };
void myclass::showab() { obj1 befor assignment:
cout << "a is " <<a << "n"; a is 10
cout<< "b is " << b << "n";}
main() b is 20
{myclass obj1, obj2; ob2 before assignment:
obj1.setab(10, 20);
obj2.setab(0,0); a is 0
cout<<"obj1 before assignment:n"; b is 0
obj1.showab();
cout<< "obj2 before assignment: n"; ob1 after assignment:
obj2.showab(); a is 10
cout<< "n"; b is 20
obj2=obj1;// assign ob1 to obj2
cout<<"obj1 after assignment: n"; Ob2 after assignment:
obj1.showab(); a is 10
cout<<"obj2 after assignment: n";
obj2.showab();} b is 20
وائل قصاص 80
81. Like simple variables, objects can be initialized
dynamically when they are created.
This feature allows you to create exactly the type of
object you need, using information that is known only
at run time.
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.
Assigning Objects: you can assign object to another if
both objects of the same type (both are of the same
class).
It is not sufficient for the two classes to simply be
physically similar-their type names must be the same.
When one object is assigned to another, the first
object data is copied to the second.
وائل قصاص 81
82. #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
وائل قصاص 82
83. Passing Objects to Functions
An object can be passed to a function in
the same way as any other data type.
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.
Any change made to the object inside the
function does not affect the object used as
argument to the function.
وائل قصاص 83
84. #include <iostream.h>
class OBJ {
int i;
public:
void set_i(int x) { i = x;}
void out_i() {cout << i << " ";}};
10 100 10
void f(OBJ x) {
x.out_i(); // outputs 10;
x.set_i(100); // this affects only local copy
x.out_i();} // outputs 100;
void main()
{ OBJ o;
o.set_i(10);
f(o);
o.out_i(); // still outputs 10, value of I
unchanged}
وائل قصاص 84
The Modification of x within f() has no effect on object o inside main()
85. Constructors, Destructors, and Passing Objects
Passing simple objects as arguments to functions is
a straightforward procedure.
#include <iostream.h>
class myclass {
int val; Constructing
public: 10
myclass(int i) Destructing
{ val=i; cout<<“Constructingn”;} Destructing
~myclass() {cout <<“Destructingn”;}
int getval() { return val;}};
void display(myclass ob) {
cout<<ob.getval()<<“n”;}
void main()
{ myclass a(10);
display (a);}
86. As you can see, there is one call to the constructor
function (as expected), but there are two calls to the
destructor. Why?
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.
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?
وائل قصاص 86
87. 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.
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.
Finally when a copy of an object is created to be used as an
argument to a function, the constructor function is not called.
When the copy is destroyed (usually by going out of scope when
the function returns), the destructor function is called.
88. A potential problem when passing
objects
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.
وائل قصاص 88
89. #include <iostream.h>
#include <stdlib.h>
class myclass {
int *p;
public:
myclass(int i);
~myclass();
int getval(){return *p;}};
myclass::myclass(int i) {
cout<<"allocating pn";
p=new int;
if (p==NULL) {
cout<<"allocating failure";
exit(1);} //exit program if out of memory
*p=i;} Abort screen
myclass::~myclass() {
cout<<"Freeing pn";
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);}
وائل قصاص 89
90. 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;)};
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;
This
وائل قصاص 90
91. #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;}
وائل قصاص 91
92. Operator Overloading
C++ incorporates the option to use language
standard operators between classes and between
fundamental types. For example:
int a, b, c;
a = b + c;
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;
وائل قصاص 92
93. 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.
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
94. 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).
95. #include <iostream.h>
class CVector {
public: int x,y;
CVector () { };
CVector (int,int);
CVector operator + (CVector); };
CVector::CVector (int a, int b) { x = a; y = b;}
CVector CVector::operator+ (CVector param) {
CVector temp; 4,3
temp.x = x + param.x;
temp.y = y + param.y;
return (temp);}
void main () {
CVector a (3,1);
CVector b (1,2);
CVector c;
c = a + b;
cout << c.x << "," << c.y;}
وائل قصاص 95
96. 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);
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; };
97. 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. 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. Of course, you can redefine it to any other functionality that
you want for this operator, like for example, copy only certain class
members.
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):
98. where a is an object of class A, b is an object of class B and c
is an object of class C.
99. You can see in this panel that there are two ways to overload some
class operators: as member function and as global function. 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.
100. Operator Overloading Using Member Functions
class myClass { void myClass::show() {
int x, y, z; cout<<x<<“, “;
public: cout<<y<<“, “;
myClass() {x=y=z=0;}
myClass(int i, int j, int k){x=i; cout<<z<<“n “; }
y=j; z=k;} void main() {
myClass operator+(myClass t); myClass a(1,2,3), b(10,10,10), c;
myClass operator=(myClass t);
void show();}; a.show();
myClass myClass::operator+ b.show();
(myClass t) {myClass temp; c=a+b;
temp.x=x+t.x; c.show();
temp.y=y+t.y;
temp.z=z+t.z; c=a+b+c;
return temp; } c.show();
myClass c=b=a;
myClass::operator=(myClass t)
{ c.show(); The Output is:
x=t.x; b.show(); } 1,2,3
y=t.y; 10,10,10
z=t.z; 11,12,13
return *this;}
22,24,26
1,2,3
1,2,3
101. 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.
102. 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.
The return value of an overloaded assignment operator is the
object on the left, after the assignment has been made.
103. Using Member Functions to Overload Unary Operators
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.
105. operator++() increments each coordinate in the object and returns
the modified object.
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.
It is possible to overload the postfix form.
The post operator will call the parameterized function, while the
prefix will always call the non-parameterized function.
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);
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.
107. Overloading a relational operator
such as == or <, is a straightforward process.
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.
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;}
108. 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)
cout<<"The two objects are equal";
else
cout<<"The two objects are not equal";
}
109. Friend Operator Functions
The only operators that cannot be overloaded using
friend functions are =,(),and ->.
Friend functions do not have a this pointer.
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.
In The next program, a friend is used instead of a
member function to overload the + operation.
وائل قصاص 109
110. Friend Operator
Functions
There is one situation in which you must use a friend
function.
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
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:
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.
111. class CL { //this handles int + ob.
public:
CL operator+(int i, CL ob){
int count;
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
//this handles ob + int. 10
CL operator+(CL ob, int i){
o=10+ o;// adds object to
CL temp;
integer.
temp.count=ob.count+i;
return temp; cout<<o.count<<“ “;//outputs
} 20
o=o+12;// adds integer to
object.
cout<<o.count; // outputs 32
return o;
}
وائل قصاص 111
112. Using a Friend to Overload a Unary
Operator
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.
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
three_d operator++(three_d op1)
{
op1.x++; This function will not work because only a
op1.y++;
op1.z++; copy of the object that activated the call to
return op1; operator++() is passed to the function in
} parameter op1. Thus, the changes inside
will not effect the calling object
وائل قصاص 112
113. Using a friend function when overloading a unary ++ or --
requires that the object be passed to the function as a
reference parameter. In this way the function can modify
parameter
the object.
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.
وائل قصاص 113
114. 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;
return temp; }
وائل قصاص 114
117. Overloading [ ]
The last operator we will overload is the [] array
subscripting operator. In C++, the [] is considered a binary
operator when it is overloaded.
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)
{
// ……
}
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.
وائل قصاص 117
118. Given an object called O, this expression
O[3]
Translates into this call to the operator[]() function: operator[](3)
That is, the value of the expression within the subscripting operator
is passed to the operator[]() function explicitly.
The this pointer will point to O, the object that generated the call.
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.
وائل قصاص 118
119. #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 i;}
};
int main()
{
atype ob;
cout<<ob[2]; // display 2
return 0;
}
وائل قصاص 119
120. In the previous slide, the initialization of the array a by the constructor.
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];}
};
void main()
{
atype ob;
cout<<ob[2]; // display 2
cout<<“ “; ob[2]=25; // [] on left of =
cout<<ob[2]; // now display 25
}
121. (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.
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.
وائل قصاص 121
122. #include <iostream.h> main()
#include <stdlib.h>
{
const int SIZE=3;
class atype { atype ob;
int a[SIZE]; cout<<ob[2]; // display 2
public: cout<<“ “;
atype() { ob[2]=25; // [] on left of =
register int i;
for(i=0; i<SIZE;i++) a[i] = i;
cout<<ob[2]; // display 25
} ob[3] = 44; // generates
int &operator[](int i); //runtime
}; error, 3
// provide range checking for atype //out=of-range
int &atype::operator[](int i) { return 0;
if(i<0 || i>SIZE-1) { }
cout<<“nindex value of “; In this program, when the
cout<<i<<“ is out-of- statement ob[3]=44;
range.n”;
executes, the boundary
exit(1);
error is intercepted by
}
operator[](), and the
return a[i]; program is terminated
} befor any damage can be
done.
وائل قصاص 122
123. 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;
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
Inheritance
وائل قصاص 123
124. #include <iostream.h>
class CPolygon {
protected:
int width, height;
public:
void set_values (int a, int b){ width=a; height=b;} };
class CRectangle: public CPolygon {
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;
cout << trgl.area() << endl; }
وائل قصاص 124
125. 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.
وائل قصاص 125
126. 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
126
وائل قصاص
127. 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:
class CRectangle: public CPolygon;
128. 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;
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.
129. 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. Anyway, in most cases classes are derived as
public. If no access level is explicitly written, private is
assumed for classes created with the class keyword and public
for those created with struct.
130. Granting Access: When a base class is inherited as private, all
members of that class (public or protected) become private
members of the derived class. 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.
To do this, you must use an access declaration within the derived
class. An access declaration takes this general form:
base-class::member;
وائل قصاص 130
131. class base {
public:
int j; // public in base};
// Inherit base as private
class derived: private base {
public:
base::j; // make j public again
// …….
};
You can use an access declaration to restore the access rights of
public and protected members. 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!).
132. class base {
int i; // private to base
public:
int j, k;
void seti(int x) {i=x;} void main()
int geti() {return i;}}; {
derived ob;
class derived: private base { //ob.i=10; illegal because
/* The next three statements override ob.j=20; /* legal j is made public*/
base’s inheritance as private and //ob.k=30;/*illegal k is private */
restore j, seti() and geti() to public ob.a=40;/* legal a is public*/
access. */ ob.seti(10);
public: cout<<ob.geti()<<“ “;
base::j; // make j public again cout<<ob.j<<“ “<<ob.a;
base::seti; // make seti() public }
base::geti; // make geti() public
// base::i; //illegal, you cannot elevate
access
int a; }; // public
وائل قصاص 132
133. What is inherited from the base class?
In principle every member of a base class is inherited by a derived class
except:
Constructor and destructor
operator=() member
friends
Although the constructor and destructor of the base class are not inherited,
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.
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) {}
134. // constructors and derivated classes
#include <iostream.h>
class mother {
public:
mother () { mother: no
cout << "mother: no parametersn"; } parameters
mother (int a) { daughter: int
cout << "mother: int parametern"; } }; parameter
mother: int
class daughter : public mother { parameter
public: son: int parameter
daughter (int a) { cout << "daughter: int parameter
nn"; } };
class son : public mother {
public:
son (int a) : mother (a) { cout << "son: int
parameternn"; } };
void main () {
daughter cynthia (1); son daniel(1);} وائل قصاص 134
135. 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
وائل قصاص 135
136. 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 { }
Multiple inheritance
وائل قصاص 136
137. #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);
void COutput::output (int i){ cout << i << endl; } rect.output (rect.area());
Trgl.output (trgl.area());}
class CRectangle: public CPolygon, public COutput {
public:
int area (void) { return (width * height); } };
class CTriangle: public CPolygon, public COutput { 20
public: 10
int area (void) { return (width * height / 2); }};
وائل قصاص 137
138. For a suitable understanding of this section you
should clearly know how to use pointers and
inheritance between classes.
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.
Polymorphism
وائل قصاص 138
139. Pointers to base class
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.
وائل قصاص 139
140. // pointers to base class
#include <iostream.h>
int main () {
class CPolygon { CRectangle rect;
protected: CTriangle trgl;
int width, height;
public: CPolygon * p1 = ▭
void set_values (int a, int b) { CPolygon * p2 = &trgl;
width=a; height=b; } };
p1->set_values (4,5);
class CRectangle: public CPolygon { p2->set_values (4,5);
public:
int area (void){ return (width * height); } }; cout << rect.area() << endl;
cout << trgl.area() << endl;
class CTriangle: public CPolygon { return 0;}
public:
int area (void){
return (width * height / 2); } }; 20
10
وائل قصاص Code 140
141. 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.
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.
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.
142. 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.
Virtual members
وائل قصاص 142
143. #include <iostream.h> int main () {
class CPolygon { CRectangle rect;
protected: CTriangle trgl;
int width, height; CPolygon poly;
public: CPolygon * p1 = ▭
void set_values (int a, int b)
{ width=a; height=b; } CPolygon *p2=&trgl;
virtual int area (void) { CPolygon * p3 = &poly;
return (0); } }; p1->set_values(4,5);
p2->set_values(4,5);
class CRectangle: public CPolygon { p3->set_values (4,5);
public: cout << p1->area() << endl;
int area (void){ cout << p2->area() << endl;
return (width * height); } }; cout << p3->area() << endl;}
class CTriangle: public CPolygon
{ public:
int area (void){ 20
return (width * height /
2); } }; 10
0
وائل قصاص 143
144. 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.
145. Abstract base classes
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.
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; };
146. This type of function is called a pure virtual function, and all classes
that contain a pure virtual function are considered abstract base
classes.
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.
147. #include <iostream.h>
class CPolygon { int main () {
protected: CRectangle rect;
int width, height; CTriangle trgl;
public: CPolygon * ppoly1 = ▭
void set_values (int a, int b) { width=a; CPolygon * ppoly2 = &trgl;
height=b; } virtual int area (void) =0; ppoly1->set_values (4,5);
};
ppoly2->set_values (4,5);
cout << ppoly1->area() << endl;
class CRectangle: public CPolygon {
cout << ppoly2->area() << endl;
public:
int area (void){ return 0;}
return (width * height); } };
class CTriangle: public CPolygon {
public: 20
int area (void){ 10
return (width * height / 2); } };
148. 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.
وائل قصاص 148
149. #include <iostream.h>
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); } };
int main () {
Rectangle rect; Triangle trgl; Polygon * p1 = ▭ Polygon * p2 = &trgl;
p1->set_values (4,5); p2->set_values (4,5);
p1->printarea(); p2->printarea();
return 0;}
وائل قصاص 149
150. Remember that this represents a pointer to the object whose
code is being executed.
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.
وائل قصاص 150
151. Early Versus Late Binding
Early binding and late binding. These terms refer to
events that occur at compile time and events that occur at
run time.
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.
وائل قصاص 151
152. 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.
It can be used to support a common interface, while allowing
various objects that utilize that interface to define their
implementations.
Help in create class libraries, which can be reused and
extended. Its disadvantage, however, is a slight loss of
execution speed.
153. 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.
وائل قصاص 153
154. Function templates
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:
template <class identifier> function_declaration;
template <typename identifier> function_declaration;
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.
For example:
template <class GenericType>
GenericType GetMax(GenericType a, GenericType b){
return (a>b?a:b); }