Chapter 5 OOP Using C++
Chapter 5 OOP Using C++
Chapter 5 OOP Using C++
CHAPTER FIVE
5. INTRODUCTION TO OBJECT ORIENTED PROGRAMMING USING C++
1|Page
Debremarkos University Department of Information Technology
A class is used to specify the form of an object and it combines data representation and methods for
manipulating that data into one neat package. The data and functions within a class are called members
of the class.
2|Page
Debremarkos University Department of Information Technology
// box 1 specification
Box1.height = 5.0;
Box1.length = 6.0;
Box1.breadth = 7.0;
// box 2 specification
Box2.height = 10.0;
Box2.length = 12.0;
Box2.breadth = 13.0;
// volume of box 1
volume = Box1.height * Box1.length * Box1.breadth;
cout << "Volume of Box1 : " << volume <<endl;
// volume of box 2
volume = Box2.height * Box2.length * Box2.breadth;
cout << "Volume of Box2 : " << volume <<endl;
return 0;
}
When the above code is compiled and executed, it produces the following result:
Volume of Box1 : 210
Volume of Box2 : 1560
It is important to note that private and protected members cannot be accessed directly using direct
member access operator (.). We will learn how private and protected members can be accessed.
3|Page
Debremarkos University Department of Information Technology
rect.set_values (3,4);
cout << "area: " << rect.area( );
}
4|Page
Debremarkos University Department of Information Technology
In order to avoid that, a class can include a special function: a constructor, which can be declared by
naming a member function with the same name as the class. This constructor function will be called
automatically when a new instance of the class is created (when declaring a new object or allocating an
object of that class) and only then. We are going to implement CRectangle including a constructor:
Example: - // classes example
#include <iostream.h>
class CRectangle
{
int width, height;
public:
CRectangle (int a, int b) //constructor is created rect area: 12
{ rectb area: 30
width = a;
height = b;
}
int area (void)
{
return (width*height);
}
};
int main ( )
{
CRectangle rect (3,4);// object rect with initial value
CRectangle rectb (5,6);
cout << "rect area: " << rect.area() << endl;
cout << "rectb area: " << rectb.area() << endl;
}
The Destructor fulfills the opposite functionality of constructor. It is automatically called when an
object is released from the memory, either because its scope of existence has finished (for example, if it
was defined as a local object within a function and the function ends) or because it is an object
dynamically assigned and it is released using operator delete.
The destructor must have the same name as the class with a tilde ( ~) as prefix and it must return no
value.
The use of destructors is especially suitable when an object assigns dynamic memory during its life and
at the moment of being destroyed we want to release the memory that it has used.
Example on constructors and destructors
#include <iostream.h>
class CRectangle
{
int *width, *height;
public:
CRectangle (int a, int b)
{
width = new int;
height = new int;
*width = a;
*height = b;
}
5|Page
Debremarkos University Department of Information Technology
~CRectangle ( )
{
delete width;
delete height;
}
int area (void)
{
return (*width * *height);
}
};
int main ( )
{
CRectangle rect (3, 4), rectb (5, 6);
cout << "rect area: " << rect.area( ) << endl;
cout << "rectb area: " << rectb.area( ) << endl;
return 0;
}
Overloading Constructors
Like any other function, a constructor can also be overloaded with several functions that have the same
name but different types or numbers of parameters. Remember that the compiler will execute the one
that matches at the moment at which a function with that name is called. In this case, at the moment at
which a class object is declared.
In fact, in the cases where we declare a class and we do not specify any constructor the compiler
automatically assumes two overloaded constructors ("empty constructor" and "copy constructor").
For example, for the class:
class CExample {
public:
int a,b,c;
void multiply (int n, int m)
{
a=n; b=m; c=a*b;
}
};
with no constructors, the compiler automatically assumes that it has the following constructor member
functions, empty constructor:
Empty constructor
It is a constructor with no parameters defined as nop(empty block of instructions). It does nothing.
CExample::CExample ( ) { };
Copy constructor
It is a constructor with only one parameter of its same type that assigns to every non-static class
member variable of the object a copy of the passed object.
CExample::CExample (const CExample & rv) {
a=rv.a; b=rv.b; c=rv.c;
}
It is important to realize that both default constructors: the empty constructor and the copy constructor
exist only if no other constructor is explicitly declared. In case that any constructor with any number of
6|Page
Debremarkos University Department of Information Technology
parameters is declared, none of these two default constructors will exist. So if you want them to be
there, you must define your own ones.
Of course, you can also overload the class constructor providing different constructors when you pass
parameters between parenthesis and when you do not (empty):
// overloading class constructors
#include <iostream.h>
class CRectangle {
int width, height;
public:
CRectangle ( );
CRectangle (int, int);
int area (void) {return (width*height);}};
CRectangle::CRectangle ( )
{
width = 5;
height = 5;
}
CRectangle::CRectangle (int a, int b)
{
width = a;
height = b;
}
int main ( )
{
CRectangle rect (3,4);
CRectangle rectb;
cout << "rect area: " << rect.area( ) << endl;
cout << "rectb area: " << rectb.area( ) << endl;
}
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.
CRectangle rectb; // right
CRectangle rectb( ); // wrong!
7|Page
Debremarkos University Department of Information Technology
Thus, we can say a television clearly separates its internal implementation from its external interface
and you can play with its interfaces like the power button, channel changer, and volume control without
having zero knowledge of its internals.
Now, if we talk in terms of C++ programming, C++ classes provides great level of data abstraction.
They provide sufficient public methods to the outside world to play with the functionality of the object
and to manipulate object data, i.e., state without actually knowing how class has been implemented
internally.
For example, your program can make a call to the sort( ) function without knowing what algorithm the
function actually uses to sort the given values. In fact, the underlying implementation of the sorting
functionality could change between releases of the library, and as long as the interface stays the same,
your function call will still work.
In C++, we use classes to define our own abstract data types (ADT). You can use the cout object of
class ostream to stream data to standard output like this:
#include <iostream>
int main( ){
cout << "Hello C++" <<endl;
return 0; }
Here, you don't need to understand how cout displays the text on the user's screen. You need to only
know the public interface and the underlying implementation of cout is free to change.
Access Labels Enforce Abstraction:
In C++, we use access labels to define the abstract interface to the class. A class may contain zero or
more access labels:
Members defined with a public label are accessible to all parts of the program. The data-
abstraction view of a type is defined by its public members.
Members defined with a private label are not accessible to code that uses the class. The private
sections hide the implementation from code that uses the type.
There are no restrictions on how often an access label may appear. Each access label specifies the
access level of the succeeding member definitions. The specified access level remains in effect until the
next access label is encountered or the closing right brace of the class body is seen.
Benefits of Data Abstraction:
Data abstraction provides two important advantages:
Class internals are protected from unintentional user-level errors, which might corrupt the state
of the object.
The class implementation may evolve over time in response to changing requirements or bug
reports without requiring change in user-level code.
By defining data members only in the private section of the class, the class author is free to make
changes in the data. If the implementation changes, only the class code needs to be examined to see
what affect the change may have. If data are public, then any function that directly accesses the data
members of the old representation might be broken.
8|Page
Debremarkos University Department of Information Technology
When creating a class, instead of writing completely new data members and member functions, the
programmer can designate that the new class should inherit the members of an existing class. This
existing class is called the base class, and the new class is referred to as the derived class.
Base & Derived Classes:
A class can be derived from more than one classes, which means it can inherit data and functions from
multiple base classes. To define a derived class, we use a class derivation list to specify the base
class/es. A class derivation list names one or more base classes and has the form:
class derived-class: access-specifier base-class
Where access-specifier is one of public, protected, or private, and base-class is the name of a previously
defined class. If the access-specifier is not used, then it is private by default.
Consider a base class Shape and its derived class Rectangle as follows
#include <iostream.h>
// Base class
class Shape
{
public:
void setWidth(int w){
width = w;}
void setHeight(int h){
height = h; }
protected:
int width;
int height;
};
class Rectangle: public Shape // Derived class
{
public:
int getArea( ){
return (width * height); }
};
int main(void){
Rectangle Rect;
Rect.setWidth(5);
Rect.setHeight(7);
// Print the area of the object.
cout << "Total area: " << Rect.getArea() << endl;
return 0;}
When the above code is compiled and executed, it produces the following result:
Total area: 35
Access Control and Inheritance:
A derived class can access all the non-private members of its base class. Thus base-class members that
should not be accessible to the member functions of derived classes should be declared private in the
base class. We can summarize the different access types according to who can access them in the
following way:
10 | P a g e
Debremarkos University Department of Information Technology
Type of Inheritance:
When deriving a class from a base class, the base class may be inherited through public, protected or
private inheritance. The type of inheritance is specified by the access-specifier as explained above. We
hardly use protected or private inheritance, but public inheritance is commonly used. While using
different type of inheritance, following rules are applied:
Public Inheritance: When deriving a class from a public base class, public members of the base
class become public members of the derived class and protected members of the base class
become protected members of the derived class. A base class's private members are never
accessible directly from a derived class, but can be accessed through calls to the public and
protected members of the base class.
Protected Inheritance: When deriving from a protected base class, public and protected members
of the base class become protected members of the derived class.
Private Inheritance: When deriving from a private base class, public and protected members of the
base class become private members of the derived class.
Multiple Inheritances:
A C++ class can inherit members from more than one class and here is the extended syntax:
class derived-class: access baseA, access baseB....
where access is one of public, protected, or private and would be given for every base class and they
will be separated by comma as shown above. Let us try the following example:
#include <iostream.h>
// Base class Shape
class Shape {
public:
void setWidth(int w){
width = w; }
void setHeight(int h){
height = h;}
protected:
int width;
int height;};
// Base class PaintCost
class PaintCost {
public:
int getCost(int area){
return area * 70; }};
// Derived class
class Rectangle: public Shape, public PaintCost{
public:
int getArea() {
return (width * height); }};
int main(void){
Rectangle Rect;
int area;
Rect.setWidth(5);
11 | P a g e
Debremarkos University Department of Information Technology
Rect.setHeight(7);
area = Rect.getArea( );
// Print the area of the object.
cout << "Total area: " << Rect.getArea() << endl;
// Print the total cost of painting
cout << "Total paint cost: $" << Rect.getCost(area) << endl;
return 0;}
5.5. Polymorphism
Polymorphism is another important OOP concept. Polymorphism, a Greek term, means the ability to
take more than one form. An operation may exhibit different behavior in different instances. The
behavior depends upon the types of data used in the operation. For example, consider the operation of
addition. For two numbers, the operation will generate a sum. If the operands are strings, then the
operation would produce a third string by concatenation. The process of making an operator to exhibit
different behaviors in different instances is known as operator overloading.
The following figure illustrates that a single function name can be used to handle different number and
different types of argument. This is something similar to a particular word having several different
meanings depending upon the context. Using a single function name to perform different type of task is
known as function overloading.
Polymorphism plays an important role in allowing objects having different internal structures to share
the same external interface. This means that a general class of operations may be accessed in the same
manner even though specific action associated with each operation may differ. Polymorphism is
extensively used in implementing inheritance.
C++ polymorphism means that a call to a member function will cause a different function to be
executed depending on the type of object that invokes the function.
12 | P a g e
Debremarkos University Department of Information Technology
When you call an overloaded function or operator, the compiler determines the most appropriate
definition to use by comparing the argument types you used to call the function or operator with the
parameter types specified in the definitions. The process of selecting the most appropriate overloaded
function or operator is called overload resolution.
13 | P a g e
Debremarkos University Department of Information Technology
In order to allow an external function to have access to the private and protected members of a class we
have to declare the prototype of the external function that will gain access preceded by the keyword
friend within the class declaration that shares its members. In the following example we declare the
friend function duplicate:
The friend functions can serve, for example, to conduct operations between two different classes.
Generally the use of friend functions is out of an object-oriented programming methodology, so
whenever possible it is better to use members of the same class to make the process. Such as in the
previous example, it would have been shorter to integrate duplicate ( ) within the class CRectangle.
14 | P a g e
Debremarkos University Department of Information Technology
15 | P a g e