Object Oriented Programming Using C++

Lecture Notes

3. 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 structbut with difference that will be illustrated in this course. 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. 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.
  • Finally, public members are accessible from anywhere the class is visible.

Whendeclaring 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;

Declares class CRectangle and an object called rect of this class (type). This class contains 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. Notice the difference between class name and object name: In the previous example, CRectangle was the class name (i.e., the user-defined type), whereas rect was an object of type CRectangle. On successive instructions in the body of the program, any of the public members of the object rectcould be referred to just by putting the object's name followed by a point and then the class member (like we did with C structs). For example:

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.

// classes example
#include <iostream.h>
class CRectangle {
int x, y;
public:
void set_values (int,int);
int area (void) {return (x*y);}};
void CRectangle::set_values (int a, int b) {
x = a;
y = b;}
int main () {
CRectangle rect;
rect.set_values (3,4);
Cout < "area: " < rect.area();} / area: 12
An object are created in memory with its own data members
rect
after rect.set_value
rect become

The new thing in this code is 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 classgiven 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, granting exactly the same scope properties as if it was directly defined within the class.

The reason why x and yare made private members is because sometimes it is important that values cannot be modified in an unexpected wayfrom 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 example above, 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. Perhaps in a so simple example as this you do not see a great utility protecting those two variables, but in greater projects it may be very important that values cannot be modified in an unexpected way (unexpected from the point of view of the object). Several different objects can be defined from one class. For example, following with the previous example of class CRectangle, we could have declared the object rectb in addition to the object rect:

#include <iostream.h>
class CRectangle {
int x, y;
public:
void set_values (int,int);
int area (void) {return (x*y);}};
void CRectangle::set_values (int a, int b) { x = a; y = b;}

void main () {
CRectangle rect, rectb;
rect.set_values (3,4);
rectb.set_values (5,6);
cout < "rect area: " < rect.area() < endl;
cout < "rectb area: " < rectb.area() < endl;} / rect area: 12
rectb area: 30
Two object are created in memory each with its own data members
rect

rectb

Notice that the call to rect.area() does not give the same result as the call to rectb.area(). This is because each object of class CRectangle has its own variables x and y, and its own functions set_value() and area(). On that is based the concept of object and object-oriented programming. In that data and 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) is CRectangle, of which there are two instances, or objects: rect and rectb, each one with its own member variables and member functions.

3.1 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. For example, 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 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 automaticallywhen a new instance of the class is created (when declaring a new object or allocating an object of that class) and only then. To implement CRectangle including a constructor:

// classes example
#include <iostream.h>
class CRectangle {
int width, height;
public:
CRectangle (int,int);
int area (void) {return (width*height);}};
CRectangle::CRectangle (int a, int b) {
width = a;
height = b;}
void main () {
CRectangle rect (3,4);
CRectangle rectb (5,6);
cout < "rect area: " < rect.area() < endl;
cout < "rectb area: " < rectb.area() < endl;} / rect area: 12
rectb area: 30
Two object are created in memory each with its own data members
rect

rectb

The result of this example is identical to the previous one. In this case, only the function set_values,that no longer exists, by a class constructor. Notice the way in which the parameters are passed to the constructor at the moment at which the instances of the class are created:

CRectangle rect (3,4); CRectangle rectb (5,6);

You can also see how neither the prototype nor the later constructor declaration includes a return value, not even void type. This must always be i.e.“A constructor never returns a value nor does the void have to be specified”, as shown in the previous example.

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 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) 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 (rv) 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 members.

3.1.1 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 objects is declared.

In fact, 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:

class CExample {

public:

int a,b,c;

void multiply (int n, int m) { a=n; b=m; c=a*b; } };

Of course, you can also overload the class constructor providing different constructors for 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 (CRectangle &cp)
{ width =cp.width; height = cp.height;}
CRectangle::CRectangle (int a, int b) {
width = a; height = b;}
void main () {
CRectangle rect (3,4);
CRectangle rectb,rectc(rect);
cout < "rect area: " < rect.area() < endl;
cout < "rectb area: " < rectb.area() < endl;
rectb=rect;
cout < "rectb area: " < rectb.area() < endl;} / rect area: 12
rectb area: 25
Rectb area: 12
Two object are created in memory each with its own data members
rect

rectb
rectc

rectb

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

CRectangle rectb(); // wrong

3.1.2Destructor

The Destructor fulfills the opposite functionality. 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.

Destructor receives no parameter and returns no value.

 A class may have only one destructor.

The use of destructors is specially suitable when an object assigns dynamic memory during its life and at the moment of being destroyed, release the memory that it has used.

// 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);
cout < "rect area: " < rect.area() < endl;
cout < "rectb area: " < rectb.area() < endl;
return 0;} / rect area: 12
rectb area: 30

3.2 Inline functions

  • 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.

General form

inline int f()

{

//……..

}

•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.

•Where the inline function is large, the overall size of your program will also increase. For this reason, the best inline functions are those that are very small.

Example

•If you compile this version of the program, save its object code, and then compile it again with the inline specifier removed, you will see the inline version is several bytes smaller.

•Inline is a request not a command, that the compiler generate inline code.

•Some compilers will not generate inline code if a function contains a loop, a switch, or a goto.

•no inline recursive functions.

•Inline functions that contain static variables are frequently disallowed.

Second :Creating Inline Functions inside a Class

•Any function that is defined inside a class definition is automatically made into an inline function. It is not necessary to precede its declaration with the keyword inline.

3.3 Array of object and Pointers to Objects

Array of objects can be created in the same way that you create arrays of any other data types.

#include <iostream.h>
enum disp_type {mono, cga, ega, vga};
class display {
int colors; //number of colors
enum disp_type dt; // display type
Public:
void set_colors(int num) {colors=num;}
int get_colors() {return colors;}
void set_type(enum disp_type t) {dt=t;}
enum disp_type get_type() {return dt;}};
char names [4][5] = {“mono”, “cga”, “ega”, “vga”};
void main()
{display monitors[3];
int i;
monitors[0].set_type(mono);
monitors[0].set_colors(1);
monitors[1].set_type(cga);
monitors[1].set_colors(4);
monitors[2].set_type(vga);
monitors[2].set_colors(16);
for(i=0;i<3;i++) {
cout<names[monitors[i].get_type()]<““;
cout<“has “<monitors[i].get_colors();
cout<“ colors”“\n”;
}
} / mono has 1 colors
cga has 4 colors
vga has 16 colors

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. For example:

#include <iostream.h>
class p_example {
int num;
public:
void set_num(int val) {num=val;}
void show_num();};
void p_example::show_num()
{cout<num<“\n”;}
voidmain()
{p_example ob[2], *p;
ob[0].set_num(10); // access objects directly
ob[1].set_num(20);
p=&ob[0]; // obtain pointer to first element
p->show_num(); // show value of ob[0] using pointer
p++; // advance to next object;
p->show_num(); // show value of ob[1] using pointer
p--; //retreat to previous object
(*p).show_num(); } / 10
20
10

(*p).show_num(); and p->show_num(); are the same

*x can be read: pointed by x

&x can be read: address of x

x.y can be read: member y of object x

(*x).ycan be read: member y of object pointed by x

x->y can be read: member y of object pointed by x (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

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>
class p_example {
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();
} / 10
20

3.4 Static Class Member

As mentioned above, each object has its own copy of all data members of the class. In certain cases, only one copy of the variable should be shared by all objects of a class. To do so, a static class variable is used. 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.

#include <iostream.h>
class st_example {
static int count;
public:
st_example (){count++;}
int get_count(){return count;}};
int st_example::count=0;
void main()
{st_example ob1;
cout”\n count is”<ob1.get_count();
st_example ob2;
cout<”\n count is”<ob1.get_count();
cout<”\n count is”<ob2.get_count();
st_example ob3;
cout<”\n count is”<ob3.get_count(); } / count is1
count is2
count is2
count is3
ob1

ob2

ob3

As there is static data member, there is a static member function. This type of function can be accessed through any object of that class(ob1.get-count()), or they can be accessed through the class name using the binary scope of resolution (:: ) directly(cout<st_example ::get_count();). As shown in the example below:

#include <iostream.h>
class st_example {
static int count;
public:
st_example (){count++;}
static int get_count(){return count;}};
int st_example::count=0;
void main()
{ st_example ob1;
cout<”\n count is”<ob1.get_count();
st_example ob2;
cout<”\n count is”< st_example ::get_count();
cout”\n count is”<ob2.get_count();
st_example ob3;
cout<”\n count is”<ob3.get_count(); } / count is1
count is2
count is2
count is3

3.5 Constant Object and 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.

#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 timenoon(12,0,0); //constant object
:
}

3.5.1 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.

3.5.2 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

#include <iostream.h>
class time {
private:
int h,m,s;
public:
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 timenoon(12,0,0);
noon.print();
// cout<noon.get_h(); error since get-h not const
} / 12:0:0

3.6Friends (friend keyword)