[content]
Item32: Make sure public inheritance models “is-a”
DO: Public inheritance means “is-a” relationship. Everything that applies to base classes must also apply to derived classes. Every derived class object “is a” base object.
// Mathematical "is-a" relationship is different from the "is-a" relationship in inheritance.
class Rectangle {...};
void MakeBigger(Rectangle& r) {
r.SetWidth(r.width() + 10); // only add 10 to width.
}
class Square : public Rectangle {...}
Square s;
MakeBigger(s); // s is no longer a square anymore.
Item 33: Avoid hiding inherited names
DO: Do not define functions with the same name if it is not a virtual function, regardless if their parameter signature is the same or not.
WHY: Names in derived classes hide names in base classes. The base class function then will not be inherited by the derived class at all. Under public inheritance, this is never desirable.
class Base {
public:
void f1(int);
void f2(int);
}
class Derived : public Base {
public:
void f2(); // Suspicious, it is not an overload for base class function.
}
Derived d;
d.f1(0); // fine, Derived class inherits f1 from Base class.
d.f2(); // fine. Derived class defines f2.
d.f2(0); // error! Derived class hide f2 in Base class. It is not inherited at all.
DO: To make the hidden names, inherited / visible again, employ using declarations or forwarding functions.
Item34: Differentiate between inheritance of interface and inheritance of implementation
DO: Inheritance of interface is different from inheritance of implementation. Under public inheritance, derived classes always inherit base class interfaces.
DO: How to use “virtual” and its complications
1. Pure virtual functions specify inheritance of interface only
2. Non-pure virtual functions specify inheritance of interface plus inheritance of a default implementation
3. Non-virtual functions specify inheritance of interface plus inheritance of a mandatory implementation. (names in derived class will hide names in base class, not overload)
Item35: Consider alternatives to virtual functions
DO: Non-virtual interface (NVI) idiom is to have clients call private virtual functions indirectly through public non-virtual member functions. (A wrapper function for virtual functions)
DO: Strategy pattern: replace virtual function with virtual functions in another hierarchy.
Item36: Never redefine an inherited non-virtual function
WHY: The non-virtual function is bound to the type statically. So if we have a base class pointer pointing to a derived class, calling the non-virtual function will be the one in the base class.
Item37: Never redefine a function’s inherited default parameter value
WHY: Virtual functions are dynamically bound, meaning that the particular function called is determined by the dynamic type of the object through which it is invoked.
WHY: The default parameter values are statically bound. You may end up invoking a virtual function defined in a derived class but using a default parameter value from a base class. So never redefine default parameter value in virtual functions.
Item38: Model “has-a” or “is-implemented-in-terms-of” through composition
DO: In the application domain, composition means “has-a”. In the implementation domain, it means “is-implemented-in-terms-of”.
// Use composition to reuse code.
template<class T>
class Set {
public: ...
private:
std::list<T> rep;
}
Item39: Use private inheritance judiciously
DO: Private inheritance means “is-implemented-in-terms-of”. It is usually inferior to composition, only use when it is a must: You need to access protected members.
WHY: Private inheritance is purely an implementation technique. Everything you inherit from a private base class becomes private in your class: it is all implementation detail. Private inheritance means nothing during software design, only during software implementation.
Item40: Use multiple inheritance judiciously
DO: Avoid “Deadly MI diamond”, Whether you want the data members in the base class to be replicated for each of the paths.
// The data member in File class will be replicated.
class File {...};
class InputFile: public File {...};
class OutputFile: public File {...};
class IOFile : public InputFile,
public OutputFile {...};
// The data member in File class is not replicated.
// Virtual base class has higher cost in size and speed.
class File {...};
class InputFile: virtual public File {...};
class OutputFile: virtual public File {...};
class IOFile : public InputFile,
public OutputFile {...};
DO: Multiple inheritance does have legitimate uses. One scenario involves combining public inheritance from an Interface class with private inheritance from a class that helps with implementation.