The simultaneous lifetimes of the whole and its parts in a composition relationship imply that the whole constructor instantiates and initializes the parts. Consequently, chaining the constructor calls is the most challenging aspect of managing an embedded, tightly bound whole-part relationship. It's relatively easy to use composition once established. However, embedding the part within the whole doesn't circumvent the protection the private
keyword provides. So, we must use the part class's public interface to access or use the data it manages.
We use part objects by sending them messages. As outlined previously, we send a message to an object using its name and the now-familiar dot operator syntax that binds an object with a member function. We'll introduce a simple display function to help illustrate the syntax for calling part member functions and how to chain member function calls together. Recall that an object's member functions can directly access its private
member variables without using a selection operator.
class Part { private: int member; public: void display() { cout << member << endl; } }; |
class Whole { private: Part my_part; public: void display() { my_part.display(); } }; |
class Address { private: string city; string state; public: Address(string c, string s) : city(c), state(s) {} void display() { cout << city << ", " << endl; } }; class Person { private: string name; Address addr; // (a) public: Person(string n, string c, string s) : addr(c, s), name(n) {} // (b) void display() { cout << name << end; addr.display(); // (c) } }; class Student : public Person // (d) { private: double gpa; public: Student(string n, double g, string c, string s) : Person(n, c, s), gpa(g) {} // (e) void display() { Person::display(); // (f) cout << gpa << endl; } } |
|
|
private
and only accessible outside the defining class through its public interface. The Student constructor "pushes" data into the connected object through the chained constructor calls, and the display function call "pulls" it from the objects by chaining calls to the display functions in the program:
Student valedictorian("Alice", 4.0, "Ogden", "Utah"); valedictorian.display();
class Pet { private: string name; string vaccinations; public: Pet(string n, string v) : name(n), vaccinations(v) {} void display() { cout << name << " " << vaccinations << endl; } }; class Person { private: string name; string phone; public: Person(string n, string p) : name(n), phone(p) {} void display() { cout << name << endl; cout << phone << endl; } }; class Owner : public Person // (a) { private: int account; Pet my_pet; // (b) public: Owner(string n, string p, int a, string pn, string v) : Person(n, p), // (c) my_pet(pn, v), account(a) {} // (d) void display() { Person::display(); // (e) cout << account << endl; my_pet.display(); // (f) } }; |
|
|
Owner pet_owner("Dilbert", "801-555-1234", 123456, "Fido", "2020/06/01"); pet_owner.display();