ZeePedia

Advantages of Objects as Class Members, Structures as Class Members

<< Pointers, References, Call by Value, Call by Reference, Dynamic Memory Allocation
Overloading Template Functions, Template Functions and Objects >>
img
CS201 ­ Introduction to Programming
Lecture Handout
Introduction to Programming
Lecture No. 40
Reading Material
Deitel & Deitel - C++ How to Program
Chapter 7
7.3, 7.4
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 511
img
CS201 ­ Introduction to Programming
/* 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 512
img
CS201 ­ Introduction to Programming
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 513
img
CS201 ­ Introduction to Programming
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 514
img
CS201 ­ Introduction to Programming
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 515
img
CS201 ­ Introduction to Programming
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 516
img
CS201 ­ Introduction to Programming
{
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 517
img
CS201 ­ Introduction to Programming
}
~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 518
img
CS201 ­ Introduction to Programming
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 library 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 519
img
CS201 ­ Introduction to Programming
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 520
img
CS201 ­ Introduction to Programming
}
~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 521
img
CS201 ­ Introduction to Programming
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 522
img
CS201 ­ Introduction to Programming
};
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 523
img
CS201 ­ Introduction to Programming
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 524
img
CS201 ­ Introduction to Programming
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 525
img
CS201 ­ Introduction to Programming
};
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.
Page 526
Table of Contents:
  1. What is programming
  2. System Software, Application Software, C language
  3. C language: Variables, Data Types, Arithmetic Operators, Precedence of Operators
  4. C++: Examples of Expressions, Use of Operators
  5. Flow Charting, if/else structure, Logical Operators
  6. Repetition Structure (Loop), Overflow Condition, Infinite Loop, Properties of While loop, Flow Chart
  7. Do-While Statement, for Statement, Increment/decrement Operators
  8. Switch Statement, Break Statement, Continue Statement, Rules for structured Programming/Flow Charting
  9. Functions in C: Structure of a Function, Declaration and Definition of a Function
  10. Header Files, Scope of Identifiers, Functions, Call by Value, Call by Reference
  11. Arrays: Initialization of Arrays, Copying Arrays, Linear Search
  12. Character Arrays: Arrays Comparisonm, Sorting Arrays Searching arrays, Functions arrays, Multidimensional Arrays
  13. Array Manipulation, Real World Problem and Design Recipe
  14. Pointers: Declaration of Pointers, Bubble Sort Example, Pointers and Call By Reference
  15. Introduction, Relationship between Pointers and Arrays, Pointer Expressions and Arithmetic, Pointers Comparison, Pointer, String and Arrays
  16. Multi-dimensional Arrays, Pointers to Pointers, Command-line Arguments
  17. String Handling, String Manipulation Functions, Character Handling Functions, String Conversion Functions
  18. Files: Text File Handling, Output File Handling
  19. Sequential Access Files, Random Access Files, Setting the Position in a File, seekg() and tellg() Functions
  20. Structures, Declaration of a Structure, Initializing Structures, Functions and structures, Arrays of structures, sizeof operator
  21. Bit Manipulation Operators, AND Operator, OR Operator, Exclusive OR Operator, NOT Operator Bit Flags Masking Unsigned Integers
  22. Bitwise Manipulation and Assignment Operator, Programming Constructs
  23. Pre-processor, include directive, define directive, Other Preprocessor Directives, Macros
  24. Dynamic Memory Allocation, calloc, malloc, realloc Function, Dangling Pointers
  25. History of C/C++, Structured Programming, Default Function Arguments
  26. Classes and Objects, Structure of a class, Constructor
  27. Classes And Objects, Types of Constructors, Utility Functions, Destructors
  28. Memory Allocation in C++, Operator and Classes, Structures, Function in C++,
  29. Declaration of Friend Functions, Friend Classes
  30. Difference Between References and Pointers, Dangling References
  31. Operator Overloading, Non-member Operator Functions
  32. Overloading Minus Operator, Operators with Date Class, Unary Operators
  33. Assignment Operator, Self Assignmentm, Pointer, Conversions
  34. Dynamic Arrays of Objects, Overloading new and delete Operators
  35. Source and Destination of streams, Formatted Input and Output, Buffered Input/Output
  36. Stream Manipulations, Manipulators, Non Parameterized Manipulators, Formatting Manipulation
  37. Overloading Insertion and Extraction Operators
  38. User Defined Manipulator, Static keyword, Static Objects
  39. Pointers, References, Call by Value, Call by Reference, Dynamic Memory Allocation
  40. Advantages of Objects as Class Members, Structures as Class Members
  41. Overloading Template Functions, Template Functions and Objects
  42. Class Templates and Nontype Parameters, Templates and Static Members
  43. Matrices, Design Recipe, Problem Analysis, Design Issues and Class Interface
  44. Matrix Constructor, Matrix Class, Utility Functions of Matrix, Input, Transpose Function
  45. Operator Functions: Assignment, Addition, Plus-equal, Overloaded Plus, Minus, Multiplication, Insertion and Extraction