Effective C++, 2E | Item 40: Model "has-a" or "is-implemented-in-terms-of" through layering Back to Item 39: Avoid casts down the inheritance hierarchy. Continue to Item 41: Differentiate between inheritance and templates. Item 40: Model "has-a" or "is-implemented-in-terms-of" through layering. Layering is the process of building one class on top of another class by having the layering class contain an object of the layered class as a data member. For example: class Address { ... }; // where someone lives class PhoneNumber { ... }; class Person { public: ... private: string name; // layered object Address address; // ditto PhoneNumber voiceNumber; // ditto PhoneNumber faxNumber; // ditto }; In this example, the Person class is said to be layered on top of the string, Address, and PhoneNumber classes, because it contains data members of those types. The term layering has lots of synonyms. It's also known as composition, containment, and embedding. Item 35 explains that public inheritance means "isa." In contrast, layering means either "has-a" or "is-implemented-in-terms-of." The Person class above demonstrates the has-a relationship. A Person object has a name, an address, and telephone numbers for voice and FAX communication. You wouldn't say that a person is a name or that a person is an address. You would say that a person has a name and has an address, etc. Most people have little difficulty with this distinction, so confusion between the roles of isa and has-a is relatively rare. Somewhat more troublesome is the difference between isa and is-implemented-in-terms-of. For example, suppose you need a template for classes representing sets of arbitrary objects, i.e., collections without duplicates. Because reuse is a wonderful thing, and because you wisely read Item 49's overview of the standard C++ library, your first instinct is to employ the library's set template. After all, why write a new template when you can use an established one written by somebody else? As you delve into set's documentation, however, you discover a limitation your application can't live with: a set requires that the elements contained within it be totally ordered, i.e., for every pair of objects a and b in the set, it must be possible to determine whether a will inherit from list. After all, in your implementation, a Set object will in fact be a list object. You thus declare your Set template like this: // the wrong way to use list for Set template class Set: public list { ... }; Everything may seem fine and dandy at this point, but in fact there is something quite wrong. As Item 35 explains, if D isa B, everything true of B is also true of D. However, a list object may contain duplicates, so if the value 3051 is inserted into a list twice, that list will contain two copies of 3051. In contrast, a Set may not contain duplicates, so if the value 3051 is inserted into a Set twice, the set contains only one copy of the value. It is thus a vicious lie that a Set isa list, because some of the things that are true for list objects are not true for Set objects. Because the relationship between these two classes isn't isa, public inheritance is the wrong way to model that relationship. The right way is to realize that a Set object can be implemented in terms of a list object: // the right way to use list for Set template class Set { public: bool member(const T& item) const; void insert(const T& item); void remove(const T& item); int cardinality() const; private: list rep; // representation for a set }; Set's member functions can lean heavily on functionality already offered by list and other parts of the standard library, so the implementation is neither difficult to write nor thrilling to read: template bool Set::member(const T& item) const { return find(rep.begin(), rep.end(), item) != rep.end(); } template void Set::insert(const T& item) { if (!member(item)) rep.push_back(item); } template void Set::remove(const T& item) { list::iterator it = find(rep.begin(), rep.end(), item); if (it != rep.end()) rep.erase(it); } template int Set::cardinality() const { return rep.size(); } These functions are simple enough that they make reasonable candidates for inlining, though I know you'd want to review the discussion in Item 33 before making any firm inlining decisions. (In the code above, functions like find, begin, end, push_back, etc., are part of the standard library's framework for working with container templates like list. You'll find an overview of this framework in Item 49 and M35.) It's worth remarking that the Set class interface fails the test of being complete and minimal (see Item 18). In terms of completeness, the primary omission is that of a way to iterate over the contents of a set, something that might well be necessary for many applications (and that is provided by all members of the standard library, including set). An additional drawback is that Set fails to follow the container class conventions embraced by the standard library (see Items 49 and M35), and that makes it more difficult to take advantage of other parts of the library when working with Sets. Nits about Set's interface, however, shouldn't be allowed to overshadow what Set got indisputably right: the relationship between Set and list. That relationship is not isa (though it initially looked like it might be), it's "is-implemented-in-terms-of," and the use of layering to implement that relationship is something of which any class designer may be justly proud. Incidentally, when you use layering to relate two classes, you create a compile-time dependency between those classes. For information on why this should concern you, as well as what you can do to allay your worries, turn to Item 34. Back to Item 39: Avoid casts down the inheritance hierarchy. Continue to Item 41: Differentiate between inheritance and templates.