What I Learned from Scott Meyers' Effective C++, Part 6
Chapter 6: Inheritance and Object-Oriented Design
In my previous chapter, we discussed Implementations. In this chapter, we'll talk about what I learned in chapter 6 of the book, items 32 to 40.
Item 32: Make sure public inheritance models “is-a.”
Public inheritance by design implies an "is-a" relationship. Everything that applies to your base classes must also apply to your derived classes because every derived class object "is" a base class object.
Item 33: Avoid hiding inherited names
Derived class names implicitly hide names in base classes and in the public inheritance approach this isn't the desired behaviour. You can utilise "using" declarations or forwarding functions to make these visible again.
Item 34: Differentiate between inheritance of interface and inheritance of implementation
It's essential in class design to differentiate between the two ideas, inheritance of implementation and inheritance of interface. In the first, you directly inherit what and how the class does what it does, in the latter you only inherit the what. Under public inheritance, derived classes always inherit base class interfaces. You can specify inheritance of interface via pure virtual functions, you can specify inheritance of interface and a default implementation via impure virtual functions and non-virtual functions specify inheritance of interface and a mandatory implementation.
Item 35: Consider alternatives to virtual functions
Programmers often forget that they have alternatives to virtual functions when composing object-oriented systems. These patterns can often provide viable alternatives to virtual methods. Such as the NVI idiom and various forms of the Strategy design pattern and the Template Method design pattern. One disadvantage of these patterns is that you lose private access to your class members.
Item 36: Never redefine an inherited non-virtual function
Save yourself some headaches, never redefine an inherited non-virtual function. You can internalize this better if you remember our rule about non-virtual destructors in polymorphic classes.
Item 37: Never redefine a function's inherited default parameter value
Again save yourself some trouble and don't redefine a function's inherited default parameter value. Default parameter values in C++ are statically bound for performance reasons while virtual functions are dynamically bound.
Item 38: Model “has-a” or “is-implemented-in-terms-of” through composition
When you're implementing a class that "is-implemented-in-terms-of" or "has-a" type you're composing your class of those types and composition here has a different meaning than in public inheritance. It is crucial to understand the differences between has-a, is-a and is-implemented-in-terms-of concepts
Item 39: Use private inheritance judiciously
Private inheritance means is-implemented-in-terms-of. Often it's not the approach you'd want to take but it can make sense when a derived class needs access to protected base class members or needs to redefine inherited virtual functions. Private inheritance can also enable empty base optimization(EBO) which can be a neat trick on rare occasions for space optimizations.
Item 40: Use multiple inheritance judiciously
Multiple inheritance is often more complex than single inheritance and it can lead to ambiguity within your program and the need to use virtual inheritance. Virtual inheritance creates size, speed and complexity of initialization and assignment costs. It can be practical when virtual base classes have no data such as an interface. You can combine public inheritance with private inheritance in some cases to prove a legitimate use case for multiple inheritance. Still, try to use it judiciously.
That's it for this chapter, next article will be about Chapter 7, Templates and Generic Programming