Member Objects

You can write a class that contains objects as members. This is known as “composition,” the act of making a new class by using other classes as components. Suppose that you want a PersonInfo class that stores a person's name, address, and birthday. You can give that class a Date as a member, as follows:

class PersonInfo

{

public:

// public member functions...

private:

char name[30];

char address[60];

Date birthday; // member object

};

This declaration specifies a private member named birthday, which is a Date object. Note that no arguments are specified in the declaration of birthday. However, this does not mean that the default constructor is called. The birthday object is not constructed until a PersonInfo object is constructed.

To call the constructor for a member object, you must specify a “member initializer.” Place a colon after the parameter list of the containing class's constructor, and follow it with the name of the member and a list of arguments. For example, the constructor for PersonInfo is written as follows:

class PersonInfo

{

public:

PersonInfo( char *nm, char *addr, int mn, int dy, int yr );

// ...

private:

// ...

};

PersonInfo::PersonInfo( char *nm, char *addr,

int mn, int dy, int yr )

: birthday( mn, dy, yr ) // Member initializer

{

strncpy( name, nm, 30 );

strncpy( address, addr, 60 );

}

This syntax causes the Date class constructor to be invoked for the birthday member object, using the three arguments specified. The Date constructor is called first, so the birthday member is initialized before the PersonInfo constructor begins executing. If your class has more than one member object, you can specify a list of member initializers, separating each with a comma.

If you don't use the member initializer syntax, the compiler implicitly calls the default constructor for the member object before constructing the containing object. You can then assign values to the member object using its access functions. For example, since Date has a default constructor, you could write the PersonInfo constructor as follows:

PersonInfo::PersonInfo( char *nm, char *addr, int mn, int dy, int yr )

// Default constructor sets birthday to January 1, 1

{

strncpy( name, nm, 30 );

strncpy( address, addr, 60 );

birthday.setMonth( mn );

birthday.setDay( dy );

birthday.setYear( yr );

}

If the member object's class doesn't define a default constructor, the compiler generates an error.

However, this is an inefficient technique because the value of birthday is set twice. First it is initialized to January 1, 1, by the default constructor, and then it is assigned the value specified by the member functions. In general, you should use member initializers to initialize your member objects, unless the default constructor performs all the initialization you need.

A member initializer is required when you have a constant member object. Since a person's birthday never changes, you can declare birthday with the const keyword. In this case, omitting the member initializer syntax is fatal. For example:

class PersonInfo

{

public:

// ...

private:

char name[30];

char address[60];

const Date birthday; // Constant member object

};

PersonInfo::PersonInfo( char *nm, char *addr, int mn, int dy, int yr )

// Default constructor sets birthday to January 1, 1

{

strncpy( name, nm, 30 );

strncpy( address, addr, 60 );

birthday.setMonth( mn ); // Error

birthday.setDay( dy ); // Error

birthday.setYear( yr ); // Error

}

Since birthday is a const object, you can't call any of its set member functions, because those are non-const functions. Thus, you have no way to change the value of birthday from the value that the default constructor initialized it to.

The same is true of any member declared const, even if it's a variable of a built-in type, like an integer. A const integer member cannot be assigned a value in the constructor; you must use a member initializer. For example:

class Count

{

public:

Count( int i ); // Constructor

private:

const int cnt; // Constant integer member

};

Count( int i )

: cnt( i ) // Member initializer for integer

{

}

Use a member initializer to initialize any const member, whether or not it's an object.