You are on page 1of 20

Linear Programming:

 The evolution of programming languages has been increasingly oriented towards the needs of the human
programmer.

 The first programming languages were designed to develop programs that performed relatively simple tasks and
were inadequate for complex ones.

 Shortcomings of Linear Programming Languages:

1. Facilities needed to reuse existing code are not available.


-A piece of code was duplicated every time it was used in different programs.

2. No capacity to control the visibility of data items.


-All data items were global and could be modified by any part of a program.

Structure Programming

 Structured programs are organised according to the operations they perform.

 The program is broken up into individual modules (procedures or functions) that perform discrete tasks in a larger,
more complex process.

 The goal was to - make software development easier


- reuse code already written and tested
- improve program reliability and maintainability

 By coding modules that can stand on their own, a structured program minimises the chance that one procedure
will affect another.

 Easier to isolate problems.

 A powerful concept was introduced with structured programming: abstraction.

 Abstraction is the ability to look at something without being concerned with its internal details.

 Function abstraction is the cornerstone of structured programming.

 It is sufficient to know that a given procedure performs a specific task.

 How that task is performed is unimportant.

 So long as the procedure is reliable, it can be used without having to know how it correctly completes its function.

 Today, nearly every programming language has the facilities required to support structured programming
(including traditionally unstructured languages like BASIC).

 The structured programs have proven themselves to be easier to write and maintain than non-structured
programs.

Weaknesses of Structured Programming

 The data structures in a program are just as important as the operations performed on them, especially for large
applications.

 Data items are processed in many procedures within a structured program, and when changes occur in the data
types, modifications must be made at every location that acts on those data types within the program.

 This is a frustrating, time-consuming and error-prone task in very large programs.

 A further weakness of Structured Programming appears when multiple programmers work on an application as a
team.

 While structured programs were easier to work with in a group situation, errors in communication between team
members could lead to time spent rewriting.

OOP in C++ Page 1 of 20


Data Abstraction

 Data Abstraction does for data what functional abstraction does for operations.
 With data abstraction, data structures and items can be used without having to be concerned with the exact
details of implementation.
 For instance, floating-point numbers are abstracted in all programming languages.
 You do not have to be concerned with the exact binary representation of a floating-point when assigning it a
value.
 Data abstraction frees you from worrying about non-essential details.
 Fortunately, data abstraction exists in all programming languages for complicated elements such as floating point
numbers.
 It has only been recently, however, that languages have been developed that allow you to define your own
abstract data types.

OBJECT-ORIENTED PROGRAMMING
 The object-oriented paradigm is built on the foundation laid by structured programming concepts and data
abstraction.
 The purpose of a program is to manipulate data.
 An object-oriented program is built around the data being operated upon, rather than upon the operations
themselves.
 Object-oriented programming associates data structures with operations, which is how we all think about the
world.
 We associate a specific set of actions with a given type of object, and base assumptions on those associations.
 For example - A car has wheels, moves, and has its direction changed by a turning a steering wheel.
- A tree is a plant with a woody stem and leaves.
 We assume that what can be done with a car cannot be done with a tree.
- It is futile to try to steer a tree.
- A car does not grow when watered.
 A personnel record can be read, changed and saved, or a complex number can be used in calculations. Yet a
complex number cannot be written to a file as a personnel record, and two personnel records cannot be added
together.
 An object-oriented program specifies the exact characteristics and behaviour of its data types, which allows us to
know exactly what to expect from various data types.
 It is also possible to create relationships between similar, yet distinct data types in an object-oriented program.
 We often conceptualise the world as a tree-like structure, with successive levels of details building on earlier
generalisations. This is an efficient method of organising the world around us.
 Object-Oriented Programs work in the same natural manner – they allow new framework to be build on existing
one, incorporating the features of the base framework while adding new features.
 For example, when you hear the word “plant”, you can immediately visualise a generic item of that type the exact
species of the plant is unimportant.
 Plants, however, can be very different from each other.
- Blue-green algae are tiny single-called plants that live in water VS Durian trees
 Although algae and Durian trees are members of the same family, they belong to different sub-groups of that
family.

OBJECT-ORIENTED TERMINOLOGY
 Object-oriented programming let you organises the data in your programs the same way a biologist organises different
kinds of plants.

 From the point of view of Object-oriented programming, cars, trees, inventory records, complex numbers and books
would all be known as Classes.

 A Class is a template that describes both the data structure and the valid actions for data items.

 When a data item is declared to be a member of a class, it is called an object.

 Those functions that are defined as valid for a class are known as methods, and they are the only functions that
manipulate the data of that class’s objects.

 Each object has its own copies of the class data elements, which are called instance variables.

 The methods defined for a class can be invoked by objects of that class. This is called sending a message to the
object.

 Messages are object-specific; only the object receiving the message acts upon the message.
OOP in C++ Page 2 of 20
 Objects are independent of each other
- Changes to the instance variables of one object have no effect on the instance variables of other objects.
- Sending a message to one object has no effect on other objects.

 The standard data types build into a programming language can be thought of a class, and object-oriented
programming allows you to create your own tightly controlled data types, which are just like the types built into the
language.

 However, unlike the built-in data types, classes can use other classes as building blocks. New classes can be built
from old ones through inheritance.

THE CLASS CONSTRUCT

 In C, you organise the basic data types into logical structures and write functions to manipulate the structure.

 In C++ , you do the same but also bind the data description and its algorithms (for manipulation) together.

 This combination is set up as a new data type by defining a class.

 A class consists of
-a user-defined data structure
-member functions
-custom operators
 The syntax of a class definition is similar to a struct.
 The class has the ability to hide some of its members from the rest of the program (a structure cannot)
 The class definition doesn't set aside any memory to hold any instance of the class.
 Memory is set aside when an object of the class is defined.
#include <iostream>
using namespace std
//----------------a cube class declaration
class cube {
private :
int height, width, depth; //-- private data members
public :
cube (int, int, int); //-- Constructor function
~cube(); //-- destructor function
int volume(); //-- member function
};

// the constructor function


cube::cube(int ht, int wd, int dp) {
height = ht;
width = wd;
depth = dp;
}

cube::~cube() //the destructor function


{}

// member function to compute volume


int cube::volume()
{
return(height * width * depth);
}

int main() // an application to use the cube

OOP in C++ Page 3 of 20


{
cube thiscube(4,5,6);//create an object of class cube
cout « thiscube.volume(); //compute & display volume
return 0;
}

 The declaration of a class object can accept a list of initialisers in parenthesis.

Homework: Create 3 files

Create a class named sphere (header file and implementation file).


Create a program (driver file) for testing your implementation.
Data Members
 The data members are the ones that are the data types.
 called as attributes or properties of the class.
 A data member may be any valid C++ data type, including another class.

Constructor Function
 The constructor function is executed when an instance (object) of the class comes into existence.
 The constructor function has the same name as the class
 When a class object comes into scope, enough memory is set aside to hold the data members.
 The data members are not initialised. The constructor function does it.
 The memory for the data variables is released when the class object goes out of scope.
 If the constructor function has a void or empty parameter list, the declaration of the object does not require the
parentheses.
 A constructor function returns nothing. It is void by default.
 You may define multiple, overloaded constructor functions.

Destructor Function
 The destructor function is called when a class object goes out of scope.
 The destructor function name is always that of the class with a tilde (~) character as a prefix.
 There is only one destructor function for a class.
 A destructor function takes nothing and returns nothing.
 It may be omitted altogether but examiner does expect it to be implemented.

Member Function

 These are the functions defined within the class definition.


 Also called the methods of the class.
 The cube class has one member function called volume.
 The complete name of the function is cube::volume.
 The function calling convention is the same as that for calling a structure’s function i.e. using a dot operator
int vol = thiscube.volume();

Class Member Visibility


 The private and public labels in the class definition specify the visibility of the members that follows the labels.
 The mode invoked by a label continues until another label occurs or the class definition ends.
 Private members can only be assessed by member functions.
 All class definitions begin with the private label as the default mode, so it can be omitted.
 The protected label works the same as private except during class inheritance.

inline Functions
 A class can have inline functions.

OOP in C++ Page 4 of 20


 You can code the body of the function directly into the class definition, rather than coding a prototype.
 inline functions should be small.

#include <iostream.h>
class cube // a cube class declaration
{
int height, width, depth; // private data members
public:

cube (int ht, int wd, int dp) // inline constructor function
{ height=ht; width=wd; depth=dp; }

int volume() // inline member function


{ return height*width*depth; }
};

main()
{
//create an object of the class cube
cube thiscube(4,5,6);
cube defaultcube; // no initialisers; default used

//compute & display volume


cout << thiscube.volume();
cout << ‘\n’ << defaultcube.volume();
return 0;
}

Constructors with Default Arguments

 Constructor functions can have default values.

 If no arguments are specified at the time of creating an object of the class, the default values are used.
#include <iostream.h>

class cube // a cube class declaration


{
public:
int height, width, depth; //-- private data members
public :
cube(int ht = I, int wd = 2, int dp = 3) //-- inline constructors with default initialisers
{ height = ht; width = wd; depth = dp; }

int volume() //-- inline member function


{ return height * width * depth; }
};
main ( )
{
// create an object of the class cube
cube thiscube(4,5,6);
cube defaultcube; //-- no initialisers; default used

// compute & display volume


cout « thiscube.volume();
cout « '\n' « defaultcube.volume();
OOP in C++ Page 5 of 20
return 0;
}

Overloaded Constructors
 A class can have more than one constructor function.
 The several constructor functions must have different parameter lists.

//#include <iostream.h>
// a cube class declaration
class cube
{
public:
int height, width, depth; //-- private data members
public :

cube() { /* does nothing */ }


cube(int ht, int wd, int dp)
{ height = ht; width = wd; depth = dp; }

//-- inline member function


int volume()
{ return height * width * depth; }
};

main ( )
{
// create an object of the class cube
cube thiscube(4,5,6);
cube othercube; //-- no initialisers
othercube = thiscube;

// compute & display volume


cout « othercube.volume();

return 0;
}

Pointers to Class Objects


 It is possible to take the address of a class object by the & operator.
 &objectname wiil fetch the address of the named object
 A pointer to a class object can also be declared
classname *pointername;
 A pointer on creation does not point to anything valid. It has to be explicitly initialised.

#include <iostream.h>
class cube
{
int height, width, depth;
public:
cube(int, int, int);
int volume();
};

cube::cube (int ht, int wd, int dp)

OOP in C++ Page 6 of 20


{ height = ht; width = wd; depth= dp; }

int cube::volume()
{ return height*width*depth; }

main()
{
cube *pcube; // pcube doesn’t point to a valid object
cube thiscube (4, 5, 6);
pcube = &thiscube; // -- pcube now points to thiscube
cout << pcube->volume() << ‘\n’; //-- display the volume
return 0;
}

Creating an Unnamed Object


 An object can be created without a name, but with a pointer pointing to it.

cube *ppcube= new cube(4, 5, 6);


cout << ppcube->volume() << ‘\n’; // display volume
delete ppcube;

 The first statement creates an object of the class cube with initial values of 4, 5 and 6.
 The pointer ppcube points to this unnamed object.
 The members of the unnamed object can be accessed by the pointer.
 A pointer initialised by the new operator must be deleted.

Manipulating Private Data Members


 Making data members public allows other functions to access them.
 This makes the data members public to all the other functions.
 Convention: - Keep data members private and member functions public.
- Provide member functions to read and write data members.
 Changes to the data of a class are managed by the member functions that are bound to the class.

/* Manipulating Private Data Members */


#include <iostream.h>
class cube
{
int height, width, depth; // private data members
public:
cube (int ht, int wd, int dp)
{ height = ht; width = wd; depth = dp; }

int volume ()
{ return height * width * depth; }

// member function to return the height


int getheight()
{ return height; }

// member function to allow height to be changed


int& new_ht()
{ return height; }

OOP in C++ Page 7 of 20


int set_height(int ht)
{
height = ht;
return 0;
}
};
main()
{
cube thiscube (4, 5, 6)
cout << “This height is : “ << thiscube.getheight() << ‘\n’;
cout << “Volume is “ << thiscube.volume () << “\n\n”;

thiscube.new_ht() = 10;
cout << “This height is : “ << thiscube.getheight() << ‘\n’;
cout << “Volume is “ << thiscube.volume () << ‘\n’;
return 0;
}

Array of Class Objects


 You can make an array of class objects.

#include <iostream.h>
class date // date class
{
int da, mo, yr;
public:
date() { da = 0; mo= 0; yr =0}
date( int d, int m, int y) { da = d; mo = m; yr = y }
void display()
{ cout << ‘\n’ << da << ‘/’ << mo << ‘/’ << yr; }
};

main()
{
date dates[2]; // an array of objects
date temp(10, 7, 61);
dates[0] = temp;
dates[0].display();
dates[1].display();
return 0;
}

Class Array Constructors


 When you declare an array of objects of a class, the compiler calls the constructor function for that class once for
each element in the array.

class date // date class


{
int da, mo, yr;
public:
date() { da = 0; mo= 0; yr =0}

OOP in C++ Page 8 of 20


date( int d, int m, int y) { da = d; mo = m; yr = y }
void display()
{ cout << ‘\n’ << da << ‘/’ << mo << ‘/’ << yr; }
};

date:: date() // constructor that is called for each array element


{
cout << “\nDate constructor running ..”;
da = 0; mo = 0; yr = 0;
}

main()
{
date dates[2]
date temp(10, 7, 61);

dates[0] = temp;
dates[0].display();
dates[1].display();
return 0;
}

 The program displays the following message:

Date constructor running ..


Date constructor running ..
10/7/61
0/0/0

Class Array Destructor


 When an array of objects of a class goes out of scope, the compiler calls the destructor function once for each
element of the array.

// date class
class date
{
int da, mo, yr;
public:
date() { da = 0; mo= 0; yr =0}
date( int d, int m, int y) { da = d; mo = m; yr = y }
~date();
void display()
{ cout << ‘\n’ << da << ‘/’ << mo << ‘/’ << yr; }
};
date::~date() // destructor called for each element of a date array
{
cout << “\nDate destructor running .. “;
}
main()
{
date dates[2]
date temp(10, 7, 61);

OOP in C++ Page 9 of 20


dates[0] = temp;
dates[0].display();
dates[1].display();
return 0;
}

The following display shows that the destructor runs three times – twice for the two elements in the dates array and once
for the temp object:

10/7/61
0/0/0
Date destructor running ..
Date destructor running ..
Date destructor running ..

The Free Store and Class Arrays


 In the following example, a pointer to an array of class objects has been created.

class date
{
int da, mo, yr;
public
date() { cout << “\nDate constructor .. “; }
~date() { cout << “\nDate destructor .. “; }
};

main()
{
date *dt = new date[5];
cout << “\n Process the dates .. “;
delete dt; // not enough delets !!
return 0;
}

 date constructor function executes five times from the new operator.
 There is one call to the destructor function because the delete operator doesn’t know that the pointer points to more
than one date object.
 The program displays the following:
Date constructor ..
Date constructor ..
Date constructor ..
Date constructor ..
Date constructor ..
Process the dates ..
Date destructor ..

 The delete operator must be told that the pointer being deleted points to an array by placing a subscript in the delete
operator

delete [number_of_elements] pointername

class date

OOP in C++ Page 10 of 20


{
int da, mo, yr;
public
date() { cout << “\nDate constructor .. “; }
~date() { cout << “\nDate destructor .. “; }
};
main()
{
date *dt = new date[5];
cout << “\n Process the dates .. “;
delete [5] dt; // deleting 5 times or ‘delete [] dt’ will also work
return 0;
}

 The program displays the following:


Date constructor ..
Date constructor ..
Date constructor ..
Date constructor ..
Date constructor ..
Process the dates ..
Date destructor ..
Date destructor ..
Date destructor ..
Date destructor ..
Date destructor ..

 If you pass a value to delete that is not the same as the number of elements in the array, you will get unpredictable
results.
 If the value is less than the number of elements, the remaining elements will not be destroyed by your destructor.
 If the value is greater, you will call the destructor with the addresses of nonexistent objects and probably cause the
program to crash, depending on what the destructor does.

Destroying Objects: a closer look


 In the following example, the person class has a pointer to a string that contains the name of a person.

#include <iostream.h>
#include <string.h>

class person
{
private:
char *name;
float age;
public:
person() {name = NULL; age =0;}
person (char *, float);
~person (); // destructor function
void display() {cout << name << ‘\n’ << age << ‘\n’; }
};

person::person(char *nam, float ag) // constructor

OOP in C++ Page 11 of 20


{
name = new char[strlen(nam)+1];
if (name != NULL)
{
strcpy(name, nam);
age = ag;
{
else
{
cout << “Insufficient memory. Program aborted.” << endl;
exit(1);
}
}

person::~person() // destructor function


{ if (name != NULL)
delete name;
}

int main()
{
person lecturer (“Tan Bee Bee”, 30);
lecture.display();
person sect_head;
section_head = lecturer; // the problem!
section_head.display();
return 0;
}
 A class assignment is either a binary copy or a member-by-member copy.

 This results into section_head.name pointing to the string “Tan Bee Bee”

 When the object lecturer is destroyed, the name pointer is deleted and the memory occupied by the name string is
returned to the heap.

 When the object section_head goes out of scope, the destructor will attempt to delete the same pointer again, giving
unpredictable results.

 The problem can get compounded further if you introduce the following code:

person lecturer (“Tan Bee Bee”, 30);


person section_head (“Wong Tong Tong”, 40);
section_head = lecturer;

 Not only does the name value in lecturer gets deletd twice, the one originally in section_head never gets deleted.

 The solution lies in writing a special operator function to deal with the problem.

(The solution to this problem will be deal under the Operators Overloading)

The this pointer

OOP in C++ Page 12 of 20


 Exists for a class while a non-static member function is executing.

 Points to the object for which the member function is currently executing

 When you call a member function of an object, the compiler assigns the address of the object to the this pointer
before calling the function.
 Every reference to a data member from within a member function implicitly uses the this pointer.

void person::display()
{
cout << name;
cout << this->name;
}

 Both the statements do the same thing.

 The second statement explicitly uses the pointer notation while the first one uses it implicitly.

 The this pointer finds wide application in Operators Overloading, which will be discussed later.

Static Members

 For a class member declared as static, only one instance exists, no matter how many objects of the class are
created.

 It is accessible to all the member functions.

 Unless it is a public member, it cannot be seen by the rest of the program.

 No instance of the class needs to be declared for the static member to come into existence.

 The declaration of a static member in a class does not automatically defines the variable.

 You must define it outside of the class definition for it to exist.

Static Data Members

 A static data member may be used to maintain a global value that applies to all instances of the class.

 Member functions can modify this value, and all other objects of the class will then see the modified value.

class person
{
static int number;
char * name;
float age;
public:
person() { number +=1; name= NULL; age =0;}
person (char *, float);
~person(); // destructor function
void display()
{

OOP in C++ Page 13 of 20


cout << “Total objects : “ << number;
cout << ‘\n’ << name << ‘ ‘ << age << ‘\n’;
}
};

int person::number=0; // static data member comes into existence here with value 0

person::person(char *nam, float ag)


{
number += 1;
name = new char [strlen(nam) + 1];
if (name != NULL)
{
strcpy(name, nam);
age = ag;
}
else
{
cout << “Insufficient memory. Program aborted.” << endl;
exit(1);
}
}
person::~person() // destructor
{
if (name != NULL) delete name;
number - = 1;
}

main()
{
person lecturer( “ Tan Bee Bee”, 30);
lecturer.display();
{
person section_head(“ Wong Tong Tong”, 40);
person deputy_head(“Lim William”, 45);
section_head.display();
deputy_head.display();
}
lecturer.display();
return 0;
}

Friends

 There are times when a class definition must allow out side functions to directly read and write the class’s data
members.
 Such functions can be declared as friend to the class where the data item is a member.

 A class can specify that all the member functions of another class can read and write it’s private data members by
identifying the other class as a friend.

class date

OOP in C++ Page 14 of 20


{
int da, mo, yr;
public:
date (int d, int m, int y) { da=d; mo=m; yr=y; }
void display() { cout << “\nToday is : “ << da << ‘-‘ << mo << ‘-‘ << yr; }
friend class time; // forward reference to class
};
class time
{
int hr, min, sec;
public:
time (int h, int m, int s) { hr = h; min = m; sec = s; }
void display(date&);
};
void time::display(date& dt)
{
cout << “\nTime is : “;
cout << dt.da << ‘/’ << dt.mo << ‘/’ << dt.yr;
cout << “<-->” << hr << ‘:’ << min << ‘:’ << sec;
}
main()
{ date today(15, 11, 91);
today.display();
time now(12, 30, 0);
now.display(today);
return 0;
}

Friend Functions
 Usually you don’t want an entire class to be a friend of another class.
 You need a way to specify that only selected member functions of another class may read and write the data
members of the current class.

#include <iostream.h>
#include <conio.h>
class data; // tells compiler date class is coming
class time
{
int hr, min, sec;
public:
time (int h, int m, int s) { hr = h; min = m; sec=s; }
void display (date&);
};

class date
{
int da, mo, yr;
public:
date (int d, int m, int y) { da =d; mo=m; yr=y;}
void display() { cout << “\nToday is : “ << da << ‘-‘ << mo << ‘-‘ << yr; }
friend void time::display (date&);
};

OOP in C++ Page 15 of 20


void time::display(date& dt)
{
cout << “\nTime is : “;
cout << dt.da << ‘/’ << dt.mo << ‘/’ << dt.yr;
cout << “<-->” << hr << ‘:’ << mini << ‘:’ << sec;
}
main()
{
clrscr();
date today(15,11,91);
today.display();

time now(12, 30, 0);


now.display(today);
}

Non-member Friend Functions

 Sometimes the function that is to be a friend is not a member of another class at all.

 You may specify that a non-class member function is a friend to a class.

 This feature is particularly useful in overloading is to bridge classes.

 A frequent use of non-member friend functions is to bridge classes.

 A function that is friend to more than one class can have access to the private members of both.

#include <iostream.h>

class time; // tells compiler a date class is coming


class date
{
int da, mo, yr;
public:
date (int d, int m, int y)
{ da = d; mo = m; yr = y}
friend void display (date&, time&); // bridge function
};

class time
{
int hr, min, sec;
public:
time (int h, int m, int s)
{ hr = h; min = m; sec = s;}
friend void display(date&, time&); // bridge function
};

// a bridge function
void display(date& dt, time& tm)

OOP in C++ Page 16 of 20


{
cout << ‘\n’ << dt.da << ‘/’ << dt.mo << ‘/’ << dt.yr;
cout << “ “;
cout << tm.hr << ‘:’ << tm.min << ‘:’ << tm.sec;
}

OOP in C++ Page 17 of 20


main()
{
date today(12,11,91);
time now(10,55,0);
display (today, now);
return 0;
}

Constructor Initialisation

 A constructor can use different styles to initialise data members.

 Initialisation in constructor body:

class X
{
int age;
float salary;
public:
X (int a, float sal)
{
age = a;
salary = sal;
}
}

 Initialisation in constructor header:

class X
{
int age;
float salary;
public:
X (int a, float sal) : age(a), salary(sal)
{}
};

 Mixed Initialisation:

class X
{
int age;
float salary;
public:
X (int a, float sal) : age(a)
{ salary(sal); }
};

Header Initialisation

 Header initialization is mandatory when a class has const data members


 Initialisation of const data members:

class X
{
const int age;
float salary;
public:
X (int a, float sal) : age(a)
{
salary = sal;
}
};

OOP in C++ Page 18 of 20


 It would be illegal to initialise the constant variable age in the body of the constructor.
Copy Constructor
 A copy constructor is invoked when, an object, during its creation is initialized by another object of the same class.
 The copy constructor initialises each data member of the object being created with the value of the corresponding
data member of the object used for the initialisation.
 An object can be initialised in two ways:

Colour c1(10, 30, 25);


Colour c2=c1;
Colour c2(c1);

E.g.
class test
{
int x;
public:
test() {
cout << “Default constructor ..” << endl;
x = 0;
}
test(int I) {
cout << Parameterised constructor ..” << endl;
x = I;
}
test(test& t) {
cout << “Copy constructor ..” << endl;
x = t.x;
}
int getx(void) { return x;}
};

main()
{
test sam1; // default constructor
cout << sam1.getx() << endl;

test sam2(10); // parameterized constructor


cout << sam2.getx() << endl;

test sam3(sam2); // copy constructor


cout << sam3.getx() << endl;
test sam4 = sam1; // copy constructor
cout << sam4.getx() << endl;
}

The output of the above program is :

Default constructor ..
0
Parameterised constructor ..
10
Copy constructor..
10
Copy constructor..
0

The copy constructor receives a class reference rather than a class object.

test (test& t) { … }

It is illegal to pass a class object to the copy constructor.

test (test t) { … } // Error

If a programmer does not provide a copy constructor, the compiler automatically provides one which would do a member
by member assignment.

OOP in C++ Page 19 of 20


THE END

OOP in C++ Page 20 of 20

You might also like