<Previous Lesson

Introduction to Programming

Next Lesson>

Lesson#40

Lesson 40

Summary


Objects as Class Members
Example 1
Example 2
Advantages of Objects as Class Members
Structures as Class Members
Classes inside Classes
Tips

Objects as Class Members

A class is a user defined data type and it can be used inside other classes in the same way
as native data types are used. Thus we can create classes that contain objects of other
classes as data members.
When one class contains objects of other classes, it becomes mandatory to understand
how and in what sequence the contained and containing objects are constructed. An
important point in construction of an object is that the contained data members of the
object (regardless whether they are native or user defined data types) are constructed
before the object itself. The order of destruction of an object is reverse to this
construction order, where the containing object is destroyed first before the contained
objects.
To elaborate the construction and destruction orders of objects, we take a class A and
contain its instance (object) in another class B.
Page 512
/* This program illustrates the construction and destruction orders of objects. */
#include <iostream.h>
class A
{
public:
A()
{
cout << "\n A Constructor ...";
}
~A()
{
cout << "\n A Destructor ...";
}
};
class B
{
public:
B()
{
cout << "\n B Constructor ...";
}
~B()
{
cout << "\n B Destructor ...";
}
private:
A a;
};
void main(void)
{
B b;
}
The output of this code is as follows:
A Constructor ...
B Constructor ...
B Destructor ...
A Destructor ...
Page 513
In the code above, we have contained an instance of the class A inside class B. In the
main
function, we have only created an object of the class B.
From the output, we can see the first line that the contained object a’s default constructor
is called before the default constructor of the class B. At destruction time, the destructor
of the class B is called first before A’s. Note that the contained object ‘a’ of class A is
constructed by calling the default constructor. Hence, we have found one way of
constructing contained objects by means of default constructors and then setting the
values of data members by calling setter methods of the object. But this is cumbersome
and wasteful, we have a better way provided by the language to initialize contained
objects at construction time using the initializer list. Initializer list is used to initialize the
contained objects at the construction time.

Example 1


Let’s take a class of PersonInfo that stores name, address and birthday of a person. This
PersonInfo
class contains an instance of our veteran Date class to store birthday of a
person.
class PersonInfo
{
public:
// public member functions...
private:
char name[30];
char address[60];
Date birthday; // member object
};

This declaration specifies a Date object birthday as a private data member. Note that no
arguments are specified in the declaration of birthday. However, this does not mean that
the default constructor is called when the PersonInfo object is constructed but we can
always specify a member initializer to call a parameterized constructor.
A colon is placed after the parameter list of the containing class's constructor, followed
by the name of the member and a list of arguments as shown below:
class PersonInfo
{
public:
PersonInfo( char * nm, char * addr, int month, int day, int year );
// ...
private:
// ...
};

Page 514
PersonInfo::PersonInfo( char * nm, char * addr, int month, int day, int year )
: birthday( month, day, year ) // Member initializer
{
strncpy( name, nm, 30 );
strncpy( address, addr, 60 );
}

Note that there are five parameters inside PersonInfo constructor including the three
parameters month, day and year parameters to be passed to Date class’s parameterized
constructor as birthday( month, day, year ). We are using the initializer list, therefore,
there is no need to call setter methods of the Date class to initialize the birthday object.
Similarly, multiple contained objects can be initialized by using comma separated
initializers. The order of the execution of initializers is the same as the order of
declarations of objects inside the outer class. To confirm about the order of execution, let
us have another Date object drvLicenseDate declared after birthday object in the
PersonInfo
class:
/* This program illustrates the initializer list, order of execution of constructor’s inside
the list. */
#include <iostream.h>
#include <string.h>
class Date
{
public:
Date( );
Date(int month, int day, int year);
~Date ( );
private:
int month, day, year;
};
Date::Date( )
{
cout << "\n Date -- Default constructor called ...";
month = day = year = 0;
}
Date::Date(int month, int day, int year)
{
cout << "\n Date -- Constructor with month=" << month
<< ", day= " << day << ", year= " << year << " called ...";
this->month = month;
Page 515
this->day = day;
this->year = year;
}
Date::~Date ( )
{
cout << "\n Date -- Destructor called ...";
}
class PersonInfo
{
public:
// public member functions...
PersonInfo( char * nm, char * addr, int month, int day, int year,
int licMonth, int licDay, int licYear );
PersonInfo::~PersonInfo();
private:
char name[30];
char address[60];
// member objects
Date birthday;
Date drvLicenseDate;
};
PersonInfo::PersonInfo( char * nm, char * addr, int month, int day, int year,
int licMonth, int licDay, int licYear )
: drvLicenseDate( licMonth, licDay, licYear), birthday( month, day, year )
// Above line is initializer list
{
cout << "\n PersonInfo -- Constructor called ...";
strncpy( name, nm, 30 );
strncpy( address, addr, 60 );
}
PersonInfo::~PersonInfo()
{
cout << "\n PersonInfo -- Destructor called ...";
}
main(void)
{
PersonInfo pi("Abbas", "12-Y, DHS, Lahore, Pakistan", 12, 12, 1972, 12, 10, 1992);
}
Page 516
The output of this program is:
Date -- Constructor with month=12, day= 12, year= 1972 called ...
Date -- Constructor with month=12, day= 10, year= 1992 called ...
PersonInfo -- Constructor called ...
PersonInfo -- Destructor called ...
Date -- Destructor called ...
Date -- Destructor called ...
Because birthday is declared before drvLicenseDate, it is clear from the output that the
constructor for birthday is called first and then for the drvLicenseDate object, although
drvLicenseDate
is present before birthday in the initializer list.

Example 2


Let’s take another example to work with the size of a matrix. We declare a Column class
first then a Row class. Row class contains an instance of Column class to store the number
of columns (number of elements) inside one Row instance. Further, the Matrix class
contains an instance of Row class. See the code below.
/* Program to illustrate the initialization lists, construction and destruction sequences of
contained and containing objects. */
#include <iostream.h>
#include <stdlib.h>
class Column
{
private :
int size ;
public :
Column ( int size )
{
cout << "Column created" << endl << endl ;
this->size = size ;
}
~Column ( )
{
cout << "Column destroyed " << endl << endl ;
}
void showSize ( ) ;
void setSize ( int ) ;
};
void Column :: showSize ( )
Page 517
{
cout << "Column size is : " << size << endl << endl ;
}
void Column :: setSize ( int sz )
{
size = sz ;
}
class Row
{
private :
int size ;
Column col ;
public :
Row ( int rowSize, int colSize ) : col( colSize )
{
cout << "Row created" << endl << endl ;
this->size = rowSize ;
}
~Row ( )
{
cout << "Row destroyed " << endl << endl ;
}
void showSize ( ) ;
void setSize ( int ) ;
};
void Row :: showSize ( )
{
col.showSize ( ) ;
cout << "Row size is : " << size << endl << endl ;
}
void Row :: setSize ( int sz )
{
size = sz ;
}
class Matrix
{
private :
Row row ;
public :
Matrix ( int rowSize, int colSize ) : row( rowSize, colSize )
{
cout << "Matrix created" << endl << endl ;
Page 518
}
~Matrix ( )
{
cout << "Matrix destroyed" << endl << endl ;
}
void displayMatrixSize ( ) ;
} ;
void Matrix :: displayMatrixSize ( )
{
row.showSize ( ) ;
}
void f( )
{
Matrix matrix(3, 4) ;
matrix.displayMatrixSize ( ) ;
}
int main()
{
f( );
system("PAUSE");
return 0;
}
The output of the program is as follows:
Column created
Row created
Matrix created
Column size is : 4
Row size is : 3
Matrix destroyed
Row destroyed
Column destroyed
Press any key to continue . . .
Page 519
Notice the construction sequence of objects. In order to create a Matrix object, a Row
object is created first and to create a Row object, a Column object is created. So the
contained object Column is constructed first of all, then comes the Row object and finally
the Matrix object. At destruction time, the very first object to destroy is the last object
constructed, which is the Matrix object. The second object destroyed is Row object and
then the Column object at the end. See also the use of initializer list in the code, how the
colSize
and rowSize arguments are passed to the constructors.
The public data members of a contained object can also be accessed from outside of the
containing class. For example, if row object inside Matrix class is declared as public and
has a public variable named size then it can be accessed using the dot operator (“.”) as:
int main ( void )
{
Matrix matrix ( 4, 5 ) ;
Matrix.row.size = 8 ;
}

Advantages of Objects as Class Members


It is a way of reusing the code when we contain objects of our already written classes into
a new class. For example, Date class can be used as data member of Student, Employee
or PersonInfo class. In this approach, we don’t have to test our previously written classes
again and again. We write a class, test it once and add it into our components to
use it later.
It gives clarity and better management to the source code of our programs when we break
up problems into smaller components. The smaller components can be managed
independently from their contained objects forming their own classes. For example, in the
previous example program, Matrix was subdivided into Row and Column classes.
When we declare an object as a constant data member inside a class then that constant
object is initialized using the initializer list. Therefore, a class, whose object is contained
as const object, must have a parameterized constructor.
Structures as Class Members

We have already studied that structures and classes are very similar in C++ except the
default scope of members. The default scope for members of structures is public whereas
the default visibility for class members is private.
Page 520
Likewise, objects of different classes can act as data members, structures and unions can
also act as data members of a class. In fact, all the discussion above for Class Objects as
Class Members
applies to this topic of Structure Objects as Class Members.
#include <iostream.h>
#include <stdlib.h>
struct VehicleParts
{
int wheels;
int seats;
VehicleParts()
{
cout << "\n VehicleParts - default constructor";
}
VehicleParts(int wheels, int seats)
{
this->wheels = wheels;
this->seats = seats;
cout << "\n VehicleParts - parameterized constructor";
}
~VehicleParts()
{
cout << "\n VehicleParts - destructor" << endl;
}
} ;
class Vehicle
{
private :
VehicleParts vehicleParts ;
public :
Vehicle( )
{
cout << "\n Vehicle - default constructor" << endl;
}
Vehicle( int a, int b ) : vehicleParts( a, b )
{
cout << "\n Vehicle - parameterized constructor";
Page 521
}
~Vehicle( )
{
cout << "\n Vehicle - destructor";
}
void setPartsNum ( int a, int b )
{
vehicleParts.wheels = a ;
vehicleParts.seats = b ;
}
void displayNumVehicleParts ( )
{ /* The data members of the structure are public,
therefore, directly accessible from outside. */
cout << "\n Number of wheels for this vehicle are "
<< vehicleParts.wheels;
cout << "\n Number of seats for this vehicle are "
<< vehicleParts.seats << endl;
}
} ;
void f()
{
Vehicle car( 4, 2 ) ;
car.displayNumVehicleParts( ) ;
}
void main ( )
{
f();
system ( "PAUSE" ) ;
}
The output of the program is:
VehicleParts - parameterized constructor
Vehicle - parameterized constructor
Number of wheels for this vehicle are 4
Number of seats for this vehicle are 2
Vehicle - destructor
VehicleParts - destructor
Press any key to continue . . .
Page 522

Classes inside Classes


In C language, structures can be defined inside structures, Similarly in C++, we can have
structures or classes defined inside classes. Classes defined within other classes are called
nested classes.
A nested class is written exactly in the same way as a normal class. We write its data
members, member functions, constructors and destructors but no memory is allocated for
a nested class unless an instance of it is created. C++ allows multiple levels of nesting.
Importantly, we should be clear about the visibility of the nested class. If a class is nested
inside the public section of a class, it is visible outside the outer (enclosed) class. If it is
nested in the private section, it is only visible to the members of the outer class. The outer
class has no special privileges with respect to the inner class. So, the inner class still has
full control over the accessibility of its members by the outer class. Interestingly, the
friend
operator can be used to declare enclosed class as a friend of inner class to provide
access to inner class’s private members. This operator is used in the same way as we use
it for other classes that are not nested. We can also make the inner class to access the
private
members of enclosed class by declaring the inner class as a friend of outer class.
The reason of nesting classes within other classes is simply to keep associated classes
together for easier manipulation of the objects.
/* This program illustrates the nested classes */
#include <iostream.h>
#include <stdlib.h>
class Surround
{
public :
class FirstWithin
{
public:
FirstWithin ()
{
cout << "\n FirstWithin - default constructor";
}
~FirstWithin()
{
cout << "\n FirstWithin - destructor";
}
int getVar() const
{
return (variable);
}
private:
int variable;
Page 523
};
FirstWithin myFirstWithin;
private:
class SecondWithin
{
public:
SecondWithin()
{
cout << "\n SecondWithin - default constructor";
}
~SecondWithin()
{
cout << "\n SecondWithin - destructor ";
}
int getVar() const
{
return (variable);
}
private:
int variable;
};
// other private members of Surround
};
void f(void)
{
Surround::SecondWithin a;
Surround::FirstWithin b;
Surround c;
c.myFirstWithin.getVar();
}
int main()
{
f();
cout << endl << " ";
system("PAUSE");
return 0;
}
The output of the program is as follows:
Page 524
SecondWithin - default constructor
FirstWithin - default constructor
FirstWithin - default constructor
FirstWithin - destructor
FirstWithin - destructor
SecondWithin - destructor
Press any key to continue . . .
Notice the access specifier ( :: ) usage in function f() to access the members of inner class.
The class FirstWithin is visible both outside and inside Surround. The class FirstWithin
has therefore global scope. The constructor FirstWithin() and the member function
getVar()
of the class FirstWithin are also globally visible. The int variable data member
is only visible for the members of the class FirstWithin as it is declared private. Neither
the members of Surround nor the members of SecondWithin can access the variable of
the class FirstWithin directly. The class SecondWithin is visible only inside Surround.
The public members of the class SecondWithin canalso be used by the members of the
class FirstWithin, as nested classes can be considered members of their surrounding class.
The constructor SecondWithin() and the member function getVar() of the class
SecondWithin
can also only be reached by the members of Surround (and by the
members of its nested classes). The int variable data member of the class SecondWithin
is only visible to the members of the class SecondWithin. Neither the members of
Surround
nor the members of FirstWithin can access the variable of the class
SecondWithin
directly.
The nested classes can be considered members of the surrounding class, but the members
of nested classes are not members of the surrounding class. So, a member of the class
Surround
may not access FirstWithin::getVar() directly. The nested classes are only
available as type names. They do not imply as objects containment by the surrounding
class. If a member of the surrounding class uses a (non-static) member of a nested class
then a pointer to a nested class object or a nested class data member is defined in the
surrounding class. The pointer is further used by the members of the surrounding class to
access members of the nested class.
It is important to know how do we define Member functions of nested classes. They may
be defined as inline functions or they can also be defined outside of their surrounding
class. Consider the constructor of the class FirstWithin in the previous example.
The constructor FirstWithin() is defined in the class FirstWithin, which is, in turn,
defined within the class Surround.
Consequently, the class scopes of the two classes must be used to define a constructor as
the following:
Surround :: FirstWithin :: FirstWithin ( )
{
variable = 0 ;
}

The classes FirstWithin and SecondWithin are both nested within Surround, and can be
considered members of the surrounding class. Since members of a class may directly
Page 525
refer to each other, members of the class SecondWithin can refer to public members of
the class FirstWithin but they cannot access private members of the FirstWithin unless
SecondWithin
is declared as a friend of FirstWithin.
See the code snippet below, we have used friend operator here extensively so that all the
three classes Surround, FirstWithin and SecondWithin can access private members of
each other.
class Surround
{
class SecondWithin ;
public :
class FirstWithin
{
friend class Surround ;
friend class SecondWithin ;
public :
int getValue()
{
Surround :: variable = SecondWithin :: variable ;
return (variable);
}
private :
static int variable ;
} ;
friend class FirstWithin ;
int getValue ( )
{
FirstWithin :: variable = SecondWithin :: variable ;
return (variable) ;
}
private :
class SecondWithin
{
friend class Surround ;
friend class FirstWithin ;
public :
int getValue ( )
{
Surround::variable = FirstWithin::variable;
return (variable) ;
}
private:
static int variable;
};
friend class SecondWithin ;
static int variable;

Page 526
};

We can also define structures inside classes in the same manner as we defined classes
within classes. Again, all the above discussion is valid for structures inside classes except
the default scope of members in structures is public unless explicitly declared otherwise.
Tips

A class can contain instances of other classes as its data members.
It is a way of reusing the code when we contain objects of our already written
classes into a new class.
The inner data members of the object are constructed and then the object itself.
The order of destruction of an object is reverse to this construction order, where
the outer object is destroyed first before the inner data members.
Initializer list is used to initialize the inner objects at the construction time.
In C++, we can have structures or classes defined inside classes. Classes defined
within other classes are called nested classes.

<Previous Lesson

Introduction to Programming

Next Lesson>

Home

Lesson Plan

Topics

Go to Top

Copyright © 2008-2013 zainbooks All Rights Reserved
Next Lesson
Previous Lesson
Lesson Plan
Topics
Home
Go to Top