Aggregation (Composition) of Classes
Making new classes from previous ones
- We've seen examples of aggregation:
class Student
{
private:
int credits;
int ID;
string name;
public:
// member function prototypes
}- Only need the string interface (prototypes and descriptions) in order to use string members
- Can make use of this previously written (and optimized) classes
- Aggregated classes can become members of other classes, etc.
Implicit functionality of aggregated classes
Constructors
- When an aggregated class constructor is called, parameterless constructors are called for its class-type members
- These are called before the constructor for the aggregated class
- Can override this behavior using initialization lists
- Ensures correct initialization of class-type members
Destructors
- When an aggregated class destructor is called, destructors are called for its class-type members
- These are called after the destructor for the aggregated class
- Ensures correct clean-up and return of dynamically-allocated memory
Copy Constructors
- Default copy constructor calls copy constructors for all class-type members
- Programmer-defined copy constructors by default call parameterless constructors (not copy constructors!) for class-type members
- Can call copy constructors in initialization list instead
Assignment Operators
- Default assignment operator uses assignment operator of each class-type member to assign
- Programmer-defined assignment operators do not do any implicit assignment; this must be done by the operator implementation
Example: Movie class
Movie.h
class Movie
{
public:
Movie ( ); // Constructors
Movie ( string title_parameter );
Movie ( string title_parameter, int n_stars );
Movie ( const Movie& rhs); // Copy constructor
~Movie ( ); // Destructor
Movie& operator = ( const Movie& rhs ); // Overload =
friend ostream& operator << ( ostream&, const Movie& ); //
Overload <<
// Can't be a member function, since lhs is ostream, not Movie
string
GetTitle ( ) const;
string GetStar ( int index = 0 ) const;
int GetID ( ) const;
void
SetTitle ( string title_parameter );
void SetStar ( string name, int index );
// No SetID ( ); handled by static field below
private:
string title;
string* stars;
int num_stars;
int ID;
static int nextID;
}
Movie.cpp
int Movie :: nextID = 1; // No assignments possible within class definition
Two possible parameterless constructors:
Movie :: Movie ( )
{
ID = nextID ++;
stars = 0; // converts to NULL pointer
num_stars = 0;
// Default string constructor called: title is
empty string
}
OR
Movie :: Movie ( ) : title ( "No title" )
{
ID = nextID ++;
stars = 0;
num_stars = 0;
// Could also have said: title = string("No
title"); in constructor body
}
Movie :: Movie ( string title_parameter ) : title ( title_parameter )
{
ID = nextID ++;
stars = 0;
num_stars = 0;
}
Movie :: Movie ( string title_parameter, int n_stars ) : title (
title_parameter )
{
ID = nextID ++;
num_stars = n_stars;
stars = new string [ n_stars ];
if ( stars == 0 ) // Not enough memory
exit ( 1 ); // Could also throw
an exception
}
Movie :: Movie ( const Movie& rhs ) : title ( rhs.title )
{
ID = rhs.ID;
num_stars = rhs.num_stars;
stars = new string [ num_stars ]; // Default constructor used
here for each string
if ( stars == 0 )
exit ( 1 );
// Could also throw an exception
for ( int i=0; i < num_stars; i++)
stars [ i ] = rhs.stars [ i ]; //
Overloaded = used here for each string
}
Movie :: ~Movie ( )
{
delete [ ] stars; // Has no effect on NULL pointers
// string destructor will be called on title
and each element of stars array
}
string Movie :: GetTitle ( ) const
{
return title;
}
string GetStar ( int index ) const
{
if ( 0 <= index && index < num_stars )
return stars [ index ];
return string("Error: index out of range."); //
Explicit constructor call
// Could also throw an exception
}
int Movie :: GetID ( ) const
{
return ID;
}
void Movie :: SetTitle ( string title_parameter )
{
title = title_parameter; // overloaded string
assignment used
}
void Movie :: SetStar ( string title_parameter, int index )
{
if ( 0 <= index && index < num_stars )
stars [ index ] = title_parameter;
// Could throw an exception if index out of range
}
Movie& Movie :: operator = ( const Movie& rhs )
{
if ( this != &rhs ) // Don't clobber on
self-assignment
{
ID = rhs.ID;
num_stars = rhs.num_stars;
title = rhs.title;
// Assignment operator for string used here
delete [ ] stars;
stars = new string [ num_stars ] ; //
Default constructor for each string used here
if ( stars == 0 )
num_stars =
0; // Or could throw an exception
}
return *this;
}
ostream& operator << ( ostream& out, const Movie&
rhs )
{
out << "Title: " << rhs.title <<
endl;
out << "Stars: " << endl;
for ( int i = 0; i < rhs.num_stars; i++ )
out << rhs.stars[ i ] <<
" "; // String class has also overloaded <<
return out;
}