We begin by reviewing some details about whole-part relationships:
It is common practice for the whole object to send messages to its parts (i.e., for the whole's functions to call the part's functions). In this section, we focus on the syntax needed to chain the whole class's inserter, operator<<, and the extractor, operator>> functions to the corresponding functions in the part class. The I/O functions are always implemented as non-member friend
functions, without a this pointer. Therefore, when a whole object sends a message to one of its parts, it must include a reference to the part receiving the message. All access to the members of the part must be through the parameter reference. A program may use many classes that define overloaded inserters and extractors, and the compiler chooses between them based on the second parameter.
Part | Whole | UML |
---|---|---|
class part { private: string name; double cost; public: . . . }; |
class whole { private: part my_part; int simple; public: . . . }; |
|
friend ostream& operator<<(ostream& out, part& me) { out << me.name << endl; out << me.cost << endl; return out; } |
friend ostream& operator<<(ostream& out, whole& me) { out << me.my_part << endl; out << me.simple << endl; return out; } |
|
friend istream& operator>>(istream& in, part& me) { in >> me.name; in >> me.cost; return in; } |
friend istream& operator>>(istream& in, whole& me) { in >> me.my_part; in >> me.simple; return in; } |
out << me.my_part
and in >> me.my_part
are function calls, calling the corresponding functions in the part class.
Unlike composition relationships, programs can establish and change the aggregation connecting whole and part objects at any time. Consequently, the constructor or a setter function may set an aggregating pointer to nullptr at any point in program execution. It is a runtime error for a program to attempt to access data through a nullptr. Safe programming practices require the I/O functions to test for and avoid this error. In the following examples, if my_part doesn't point to a valid part object, the program skips the output statement.
Part | Whole | UML |
---|---|---|
class part { private: string name; double cost; public: . . . }; |
class whole
{
private:
part* my_part;
int simple;
public:
. . .
}; |
|
friend ostream& operator<<(ostream& out, part& me) { out << me.name << endl; out << me.cost << endl; return out; } |
friend ostream& operator<<(ostream& out, whole& me)
{
if (me.my_part != nullptr)
out << *me.my_part << endl;
out << me.simple << endl;
return out;
}
|
|
friend istream& operator>>(istream& in, part& me) { in >> me.name; in >> me.cost; return in; } |
friend istream& operator>>(istream& in, whole& me)
{
if (me.my_part != nullptr)
in >> *me.my_part;
in >> me.simple;
return in;
}
|