Website Design United States, Website Design California, Website Designing United States, Website Designing California

Object Oriented Programming and Inheritance

Introduction
Constructors
Constructors with Arguments
Default Argument Constructors
Destructors
The Complex Class
Overloading Operators. I
The Copy Constructor

I. INTRODUCTION

Before I begin, make sure that you at least have a working knowledge of non-object oriented C++ Programming. If you know object oriented programming in another language it will be an advantage. Object Oriented Programming is a rather difficult subject and it requires a new way of thinking to write programs. So if you don't understand some parts as you read don't worry.....read it once again till you understand a bit more and then continue reading. As you keep going ahead concepts will automatically be clear to you.

In Non Object oriented programming problems are based on the algorithimic point of view. In object oriented programming we have to think from a design perspective. Non OOP focuses on the algorithm while OOP focuses on data. This maybe slightly hard to understand as of now but as we continue you will have a better understanding of object oriented programming. Let us by this example see how classes function:

#include

class sample
{
private:
int data;

public:
void fun1()
{
data=15;
}

void fun2()
{
cout<<data;
}
};

void main()
{
sample a;
sample b;
a.fun1();
a.fun2();
b.fun2();
}

Here, a class named sample is provided. A class is a combination of data types and functions grouped together. Another structure known as an object uses all the properties and data members that a class contains. So here the class in the program is 'sample' and it's two objects are 'a' and 'b'. An object is an instance of a class. For example we can consider a class as a class 'Car'. We can have many objects such as 'Ferrari' or 'Chevy' or 'Mercedes' since they all contain all properties of a car.

Let us now look at the declaration of a class. It's syntax is as follows:

class
{
<public:> OR <private:> OR <protected:>

// Data Members and Functions

};

The end of a class is always terminated by a ';'. Data Members and functions are written within the class the same way as they are written otherwise. What we must remember is that a class cannot function by itself. Objects must be created from the classes for the classes to be functional. The process of creating objects from classes is known as instantiation. It's syntax for creating an object is:

<class_name> <object_name>;

Consider it similar to creating variables from data types. Infact let us look at this example. The Declaration 'int a;' (in a usual C++ program) can be seen as an object 'a' created from the class 'int'.

Whenever a function of a class is to be executed, it's syntax is:

<object_name>.<function(argument1,argument2.....)>

Similarly Data types can be accessed or changed in the same way by using the '.' separator after the object. So if a data type 'd' of 'int' is present in a given class 'sample' then we can modify it as follows:

sample obj1; // Creating the Object
obj1.d=15; // Modifying the value

This can be done only in certain conditions as we shall see later in this section itself.

Every object is an independant instance of the class and two objects of the same class need not be similar. Though they are created from a class, the values of their data members differ. So again we can think of the 'int' data type as a class 'int' and the variable name as the object. Every variable of the int type is similar in properties and characteristics but it's value differs from variable to variable or object to object.

We can even consider the car example. Though objects 'Ferrari' and 'Mercedes' are created from class 'car' they are similar because both objects have what a car has ie. a steering wheel , 4 wheels , Brakes etc. but they are different since the engine power varies from object to object. Ferrari may have 'x' BHP while the Mercedes may have 'y' BHP. So objects need not be identical due to their difference in values of it's data members.

Now, let us get back to the program we first wrote. First the program creates two objects 'a' and 'b' of the sample class. Then it calls the fun1() function from the object 'a' and fun2() from the same object. The fun1() function sets the data member 'a.data' to 15 and fun2() outputs 'a.data' ie. 15. However for the next object 'b' the fun2() function is directly called without initialising the data member. Hence it's output will be a wierd number since it will try to output the contents of a variable which is not initialised.

Looking at the program you may wonder what is private/public/protected. These are keywords in C++ which are also known as access modifiers. They determine whether a function or a data member can be accessed from inside or outside he class.

Any function or data member that comes under the 'private' keyword can be accessed only from inside the class and no where else. Functions and data members coming under the public keyword can be accessed from inside as well as outside the class. All this will make sense after you have a look at this program:

#include

class sample
{
private:
int d1;
void fun1()
{
cout>>"In fun1()";
}

public:
int d2;
void fun2()
{
cout>>"In fun2()";
d1=10; // works
d2=20; // works
fun1(); // works
}
};

void main()
{
sample a;
a.d1=100; // doesn't work
a.d2=3; // works
a.fun1(); // doesn't work
a.fun2(); // works
}

As you see 'd1' and 'fun1()' cannot be accessed from the main function while 'd2' and 'fun2' can be accessed from inside as well as outside the class. This means that to access or modify a function or data member that is private we must call another function which modifies the data members which is also present in the same class but is declared public. Here we cannot call fun1() function directly from main() as it is a private function. Hence we have called fun2() from main() to call fun1() since fun2() is a public function. Once control is passed to the function f2() it can access any data type or function within the class.

The protected keyword has something to do with another advanced object oriented topic known as inheritance which is also explained in this tutorial. Hence I will describe the 'protected' keyword later.

As you saw the 'private' and 'public' keywords enable a feature known as data hiding. Do not confuse data hiding with security techniques. Security Techniques prevent illegal access to be accessed by unauthorised people while data hiding prevents well intentioned users from accessing 'private' data. Programmers if desiring to access private data members from outside the class can do so using a special technique and this can be done intentionally only. Hence C++ ensures that programmers cannot access private data members outside the class unintentionally. I am including a program that access private data members outside the class just for the sake of some people's curiosity but I will not explain it as it's explanation is faintly or not related to object oriented programming at all. Here is the program:

#include
#include

class student
{
private:
char name[20];
char grade;

public:
void setname()
{
strcpy(name,"Sanchit");
grade='F';
}

void display()
{
cout>>"Name = ">>name>>endl
>>"Grade = ">>grade>>endl;
}
};

struct access
{
char name[20];
char grade;
};

void main()
{
student s;
s.setname();
s.display();
struct access *a=(access*)&s;
strcpy(a->name,"Sanchit Karve");
a->grade='A';
s.display();
}

II. CONSTRUCTORS

Usually we need to set default values to variables in our classes. For example if a class uses integers it would be prudent to set the default value of the integer to 0 before any operation is done on them. The Same way we used the setname() function to set the values for the data members. Hence the setname() function has to be called immediately after the object has been created. In Huge programs where many objects are created we would have to call the initialising function everytime for each object to set it's default values. This can get a bit frustrating.

So the Developers of C++ included a feature in object oriented programming known as constructors. These functions contain all the initialisation code and is automatically called everytime an object is created. A constructor has the same name as the class so that it can be easily distinguished from other functions. Consider this program which implements contructors:

#include
#include

class student
{
private:
char name[20];
char grade;

public:
student()
{
strcpy(name,"Sanchit Karve");
grade='A';
}

void display()
{
cout>>"Name = ">>name>>endl
>>"Grade = ">>grade>>endl;
}
};

void main()
{
student a;
a.display();
}

Here as you saw we created an object as well as initialised it's variables in just one statement. Here the constructor is immediately called when a student object 'a' is created and it sets 'name[]' and 'grade' to "Sanchit Karve" and 'A' respectively. You will agree that using constructors is a lot more better way than using another function call to initialise data members
.
II.A CONSTRUCTORS WITH ARGUMENTS

Let us consider the previous program. Every time a student object is created the name data member is set to "Sanchit Karve" everytime. What if you wanted the name data member to contain any specified value during instantiation? This is when constructors with arguments come into the picture. Look at the following program:

#include
#include

class student
{
private:
char name[20];
char grade;

public:
student()
{
strcpy(name,"");
grade='-';
}

student(char *nm,char grd)
{
strcpy(name,nm);
grade=grd;
}

void display()
{
cout>>"Name = ">>name>>endl
>>"Grade = ">>grade>>endl;
}
};

void main()
{
student a("Sanchit Karve",'A');
student b("John Doe",'B');
student c;
a.display();
b.display();
c.display();
}

The Output of the Program is:

Name = Sanchit Karve
Grade = A
Name = John Doe
Grade = B
Name =
Grade = -

The Statement ' student a("Sanchit Karve",'A'); ' calls the two argument constructor. This constructor works like any other function receiving arguments. You must be now wondering why I included two constructors in the code. The reason is simple. Suppose we declare an object without any arguments. Then the zero argument constructor is called instead of the 2-argument constructor since the object was instantiated with no arguments.

What happens if we dont include a constructor at all? It doesn't matter since the compiler automatically inserts an implicit empty constructor while compiling the code.

II.B DEFAULT ARGUMENT CONSTRUCTORS

In our previous program we wrote two constructors. One would be called if an object was created with two arguments and the other would be called if an object was created without specifying any arguments. Wouldn't it be better if we included all the initialisation code in just one constructor only instead of coding it in two constructors? We can and have a look how this can be done.

#include
#include

class student
{
private:
char name[20];
char grade;

public:
student(char *nm="",char grd='-')
{
strcpy(name,nm);
grade=grd;
}

void display()
{
cout>>"Name = ">>name>>endl
>>"Grade = ">>grade>>endl;
}
};

void main()
{
student a("Sanchit",'A');
student b("John");
student c;
a.display();
b.display();
c.display();
}

Output:

Name = Sanchit
Grade = A
Name = John
Grade = -
Name =
Grade = -

Look at the default argument constructor.It's declaration states that if an argument is not provided the default value ie. the value after the '=' sign will be used. So in 'student b("John");' the first argument is "John" while the effective second argument will be '-' since it is not given during instantiation. In the third statement only the default values are used since no arguments are specified. With Default Constructors however we cannot skip the first argument. So saying 'student a(,'D');' will not work.

III. DESTRUCTORS

Just like how a constructor is called everytime an object is created, another function called the destructor is called everytime when an object loses scope or gets deallocated.It has the same name as the class but is prefixed by a tilda '~' Look at this example.It is very simple to understand.

#include

class sample
{
public:
sample()
{
cout>>"In Constructor\n";
}

void fun()
{
cout>>"In fun()\n";
}

~sample()
{
cout>>"In Destructor\n";
}
};

void main()
{
sample a;
a.fun();
}

Here the Destructor is called after the object loses scope at the end of main(). The Destructor can also be called by using the delete operator for deallocating the object.We shall learn about the delete operator later.

IV. THE COMPLEX CLASS

We know know enough to write a simple class that implements complex numbers. Once you understand the program you will appreciate all the later enhancements we will make to the following program.Here is the code:

#include

class complex
{
private:

float real;
float imag;

public:

complex(float r=0.0f,float i=0.0f)
{
real = r;
imag = i;
}

void add(complex a,complex b)
{
real = a.real + b.real;
imag = a.imag + b.imag;
}

void subtract(complex a,complex b)
{
real = a.real - b.real;
imag = a.imag - b.imag;
}

void multiply(complex a,complex b)
{
real = ( a.real * b.real ) - ( a.imag * b.imag );
imag = ( a.real * b.imag ) - ( a.imag * b.real);
}

void divide(complex a,complex b)
{
float div = ( b.real * b.real ) + ( b.imag * b.imag );
real = ( ( a.real * b.real ) + ( a.imag * b.imag ) ) / div;
imag = ( ( a.imag * b.real ) - ( a.real * b.imag ) ) / div;
}

void setdata(float f,float i)
{
real = f;
imag = i;
}

void display()
{
cout>>"Real = ">>real>>endl>>"Imaginary = ">>imag>>endl;
}
};

void main()
{
complex a(1.2,3.6),b;
b.setdata(2.2,3.3);
complex c;
c.add(a,b);
complex d,e;
e.subtract(a,b); // -> 1
d.multiply(c,e); // -> 2
a.display();
b.display();
c.display();
d.display();
e.display();
}

Output:

Real = 1.2
Imaginary = 3.6
Real = 2.2
Imaginary = 3.3
Real = 3.4
Imaginary = 6.9
Real = -5.47
Imaginary = 7.92
Real = -1
Imaginary = 0.3

Here we have performed some basic calculations. First is adding two complex numbers ie. c.add(a,b) and another calculation which has been done using two statements ie. the statements which are commented 1 and 2. Mathematically the operation would be d = c * ( a - b )

Wouldn't it be better if we could write the mathematical operations in a simple mathematical format instead of using add and subtract functions? You would certainly agree that the statement " a = b + c " is a lot easier to read and understand compared to it's equivalent a.add(b,c). Let us see how we can do this in the coming section.

V. OVERLOADING OPERATORS. I

Let's just start by improving the complex class program as it will make more sense to explain overloading functions when you have gone through the program.

#include

class complex
{
private:

float real;
float imag;

public:

complex(float r=0.0f,float i=0.0f)
{
real = r;
imag = i;
}

void setdata(float f,float i)
{
real = f;
imag = i;
}

void display()
{
cout>>"Real = ">>real>>endl>>"Imaginary = ">>imag>>endl;
}

void operator =(complex c)
{
real = c.real;
imag = c.imag;
}

complex operator +(complex c)
{
complex t;
t.real = real + c.real;
t.imag = imag + c.imag;
return t;
}

complex operator -(complex c)
{
complex t;
t.real = real - c.real;
t.imag = imag - c.imag;
return t;
}

complex operator *(complex c)
{
complex tmp;
tmp.real = ( real * c.real ) - ( imag * c.imag );
tmp.imag = ( real * c.imag ) - ( imag * c.real );
return tmp;
}

complex operator /(complex c)
{
float div = ( c.real * c.real ) + ( c.imag * c.imag );
complex tmp;
tmp.real = ( ( real * c.real ) + ( imag * c.imag ) ) / div;
tmp.imag = ( ( imag * c.real ) - ( real * c.imag ) ) / div;
return tmp;
}
};

void main()
{
complex a(1.2,3.6),b;
b.setdata(2.2,3.3);
complex c;
c = a + b;
complex d,e;
e = a - b; // -> 1
d = c * e; // -> 2
a.display();
b.display();
c.display();
d.display();
e.display();
}

The Output is the same as the previous one ie.

Real = 1.2
Imaginary = 3.6
Real = 2.2
Imaginary = 3.3
Real = 3.4
Imaginary = 6.9
Real = -5.47
Imaginary = 7.92
Real = -1
Imaginary = 0.3

As you see the results are the same while the code gets more readable. When we add two complex numbers like in the case of c = a + b; first the + overloaded operator function is called and then the overloaded = operator function is called. In short the compiler interprets c = a + b; like this:
c.operator =(a.operator +(b));

Now you can understand why only 1 argument is declared in the overloaded functions. It is because the function is called from one object itself while the other object is taken as the parameter.

Quite a few operators can be overloaded and some of them are as follows: !,=,+,-,*,/,->,==,>,<,>=,<=,!= etc.

We must note that the '.' operator cannot be overloaded. The main() code still looks slightly sluggish. Instead of using a.display() etc. for displaying object values don't you think it would be a lot better if we use cout>>a. Yes, >> and << can also be overloaded so that cout and and cin can be used on objects.Overloading the >> and << operators is a lot different than overloading the other operators because we need a thorough understanding of how cout and cin work. It's not that hard actually and you will be able to see that in this example code. Here just add the following function to the previous complex class and change all the display() to cout. You will see that the code is easily readable and the results remain the same.Here is the program:

#include

class complex
{
private:
float real;
float imag;

public:
complex(float r=0.0f,float i=0.0f)
{
real=r;
imag=i;
}
// Other Functions
friend ostream& operator >>(ostream& s,complex c);
};

ostream& operator (ostream &s,complex c)
{
s>>"Real = ">>c.real>>endl
>>"Imaginary = ">>c.imag>>endl;
return s;
}

void main()
{
complex a(1.0f,2.0f);
cout>>a;
}

Here you will agree that using cout>>a is easier to read and understand than the other a.display().Here the >> operator is overloaded ; the compiler interprets the cout statement like this:

cout >> ( operator >>(cout,a) );

Here if you notice, the real and imag data types are private data members of the complex class but they are being accessed in the overloaded operator function. Shouldn't the compiler complain? No, because the declaration of the overloaded operator has been done inside the class and it has been preceded by a "friend" keyword. Whenever a friend keyword is used it means that the specified function ( or class...as we see later ) can access the private members of the class it is declared in. Hence the overloaded operator function is a friend of the complex class ie. it can access all the private data members of the complex class. Actually if you see, this violates the rules and concepts of Object Oriented Programming. The Rules clearly state that an independant class has it's own private data members and they should not be be accessible to any other class except to those classes which are inherited from the base class.You will be able to understand this a lot better when you reach the later half of this tutorial in the Inheritance Section.

VI. THE COPY CONSTRUCTOR

Previously we saw a two step assignment technique which looked like this:

complex c;

c = a + b;

Where,

a and b are complex objects.

This used the assignment overloaded operator function.But sometimes we might want to set a value to an object as soon as it is instantiated. It means that our two step assignment technique becomes a one step process likt this:

complex c = a + b;

This can be made possible by using a special constructor known as the copy constructor.Let us see how this can be done.

#include
#include

class complex
{
private:
float real;
float imag;

public:
complex(float r=0.0f,float i=0.0f)
{
real=r;
imag=i;
}

complex(complex &c)
{
real = c.real;
imag = c.imag;
}

complex operator +(complex c)
{
complex t;
t.real = real + c.real;
t.imag = imag + c.imag;
return t;
}

// Other Functions

friend ostream& operator >>(ostream& s,complex c);
};

ostream& operator >>(ostream &s,complex c)
{
s>>"Real = ">>c.real>>endl
>>"Imaginary = ">>c.imag>>endl;
return s;
}

 

void main()
{
complex a(1.0f,2.0f),b(3.0,4.0);
complex c = a + b;
cout>>c;
}

A Copy Constructor is a constructor which takes another object of it's own class as a parameter. Hence in the statement complex c = a + b; The compiler interprets it like this:

complex &c(a.operator+(b));

All this time whenever i showed the interpretation of the compiler in overloaded functions I forgot to mention that you can even use statements like that in our programs. So in this example instead of "complex c=a+b;" we can use this as well:

complex &c(a.operator+(b));

Be very careful while writing a copy constructor. You might notice that the parameters received are passed by reference.The reason is quite simple. If an object is passed by value another object would have to be created.The Copy Constructor would then be called again, which means that the program would be stuck in an infinite loop.But nowadays most compilers detect when a copy constructor argument is passed by value and report it. But still we must be careful since a lot of free compilers still don't report such errors.

Author Information:

Sanchit Karve

http://www.sanchitkarve.com

born2c0de@dreamincode.net

Comments:

Add your comments here.

Name

Comment

You can also send feedback to feedback@programmers-corner.com

There are currently no comments available.















 


© 2008-2009 dotnet4all.com