Professional Documents
Culture Documents
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.
Structure Programming
The program is broken up into individual modules (procedures or functions) that perform discrete tasks in a larger,
more complex process.
By coding modules that can stand on their own, a structured program minimises the chance that one procedure
will affect another.
Abstraction is the ability to look at something without being concerned with its internal details.
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.
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.
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.
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.
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.
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.
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
};
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
inline Functions
A class can have inline functions.
#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; }
main()
{
//create an object of the class cube
cube thiscube(4,5,6);
cube defaultcube; // no initialisers; default used
If no arguments are specified at the time of creating an object of the class, the default values are used.
#include <iostream.h>
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 :
main ( )
{
// create an object of the class cube
cube thiscube(4,5,6);
cube othercube; //-- no initialisers
othercube = thiscube;
return 0;
}
#include <iostream.h>
class cube
{
int height, width, depth;
public:
cube(int, int, int);
int volume();
};
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;
}
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.
int volume ()
{ return height * width * depth; }
thiscube.new_ht() = 10;
cout << “This height is : “ << thiscube.getheight() << ‘\n’;
cout << “Volume is “ << thiscube.volume () << ‘\n’;
return 0;
}
#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;
}
main()
{
date dates[2]
date temp(10, 7, 61);
dates[0] = temp;
dates[0].display();
dates[1].display();
return 0;
}
// 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);
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 ..
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
class date
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.
#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’; }
};
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:
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)
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;
}
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.
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.
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()
{
int person::number=0; // static data member comes into existence here with value 0
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
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&);
};
Sometimes the function that is to be a friend is not a member of another class at all.
A function that is friend to more than one class can have access to the private members of both.
#include <iostream.h>
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)
Constructor Initialisation
class X
{
int age;
float salary;
public:
X (int a, float sal)
{
age = a;
salary = sal;
}
}
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
class X
{
const int age;
float salary;
public:
X (int a, float sal) : age(a)
{
salary = sal;
}
};
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;
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) { … }
If a programmer does not provide a copy constructor, the compiler automatically provides one which would do a member
by member assignment.