Avoid inheritance taxes: Inheritance is the second-tightest coupling relationship in
C++, second only to friendship. Tight coupling is undesirable and should be
avoided where possible. Therefore, prefer composition to inheritance unless you
know that the latter truly benefits your design.
Discussion
Inheritance is often overused, even by experienced developers. A sound rule of
software engineering is to minimize coupling: If a relationship can be expressed in
more than one way, use the weakest relationship that's practical.
Given that inheritance is nearly the strongest relationship we can express in C++,
second only to friendship, it's only really appropriate when there is no equivalent
weaker alternative. If you can express a class relationship using composition alone,
you should prefer that.
In this context, "composition" means simply embedding a member variable of a
type within another type. This way, you can hold and use the object in ways that al-
low you control over the strength of the coupling.
Composition has important advantages over inheritance:
Greater flexibility without affecting calling code: A private
data member is under your control. You can switch from holding it by
value to holding by (smart) pointer or Pimpl (see Item 43) without
breaking client code; you would only need to change the implementations of
the class's own member functions that use it. If you decide you need
different functionality, you can easily change the type of the member or
the manner of holding it while keeping the class's public interface
consistent. In contrast, if you begin with a public inheritance relation-
ship, it is likely that clients have already come to depend on the
inheritance; you have therefore committed your class to it and cannot
easily change your base class decision later on. (See Item 37.)
Greater compile-time insulation, shorter compile times: Holding
an object by pointer (preferably a smart pointer), rather than as a direct
member or base class, can also allow you to reduce header dependencies
because declaring a pointer to an object doesn't require that object's
full class definition. By contrast, inheritance always requires the full
definition of the base class to be visible. A common technique is to
aggregate all private members behind a single opaque pointer, called a
Pimpl (see Item 43).
Less weirdness: Inheriting from a type can cause name lookup to
pull in functions and function templates defined in the same namespace as
that type. This is very subtle and hard to debug. (See also Item 58.)
Wider applicability: Some classes were not designed to be bases
in the first place (and see Item 35). Most classes, however, can fulfill
the role of a member.
Great robustness and safety: The tighter coupling of
inheritance makes it more dif- ficult to write error-safe code. (See [Sutter02] §23.)
Less complexity and fragility: Inheritance exposes you to
additional complications, such as name hiding and other complications that
can arise in the presence of later changes to the base class.
Of course, these are not arguments against inheritance per se.
Inheritance affords a great deal of power, including substitutability
and/or the ability to override virtual functions (see Items 36 through 39,
and Exceptions below). But don't pay for what you don't need; unless you
need inheritance's power, don't endure its drawbacks.
Exceptions
Do use public inheritance to model substitutability. (See Item 37.)
Even if you don't need to provide a substitutability relationship to all callers, you do
need nonpublic inheritance if you need any of the following, in rough order from
most common (the first two points) to exceedingly rare (the rest):
If you need to override a virtual function.
If you need access to a protected member.
If you need to construct the used object before, or destroy it after, a base class.
If you need to worry about virtual base classes.
If you know you benefit from the empty base class optimization, including that it
matters in this case and that your target compiler(s) actually perform it in this
case. (See Item 8.)
If you need controlled polymorphism. That is, if you need a substitutability rela-
tionship, but that relationship should be visible only to selected code (via
friendship).
Note: For browsing convenience, this bibliography is also available
online.
The bold references are hyperlinks in the online bibliography.
[Alexandrescu02a] A. Alexandrescu. "Multithreading and the C++ Type System"
(InformIT website, February 2002).
[Alexandrescu04] A. Alexandrescu. "Lock-Free Data Structures"
(>cite>C/C++ Users Journal, 22(10), October 2004).
[Butenhof97] D. Butenhof. Programming with POSIX Threads
(Addison- Wesley, 1997).
[Cargill92] T. Cargill. C++ Programming Style
(Addison-Wesley, 1992).
[Cline99] M. Cline, G. Lomow, and M. Girou. C++ FAQs (2nd Edition)
(Addison-Wesley, 1999).
[Dewhurst03] S. Dewhurst. C++ Gotchas (Addison-Wesley, 2003).
[Henney00] K. Henney. "C++ Patterns: Executing Around Sequences"
(EuroPLoP 2000 proceedings).
[Henney01] K. Henney. "C++ Patterns: Reference Accounting" (EuroPLoP
2001 proceedings).
[Lakos96] J. Lakos. Large-Scale C++ Software Design (Addison-Wesley,
1996).
[McConnell93] S. McConnell. Code Complete (Microsoft Press, 1993).
[Meyers97] S. Meyers. Effective C++ (2nd Edition) (Addison-Wesley, 1997).
[Meyers04] S. Meyers and A. Alexandrescu. "C++ and the Perils of Double-
Checked Locking, Part 1" and "�Part 2" (Dr. Dobb's Journal, 29(7,8), July and August 2004).
[Schmidt01] D. Schmidt, M. Stal, H. Rohnert, F. Buschmann. Pattern-
Oriented Software Architecture, Volume 2: Patterns for Concurrent
and Networked Objects (Wiley, 2001).
[Stroustrup94] B. Stroustrup. The Design and Evolution of C++ (Addison-
Wesley, 1994).
[Stroustrup00] B. Stroustrup. The C++ Programming Language (Special 3rd Edition)
(Addison-Wesley, 2000).
[Sutter00] H. Sutter. Exceptional C++ (Addison-Wesley, 2000).
[Sutter02] H. Sutter. More Exceptional C++ (Addison-Wesley, 2002).
[Sutter04c] H. Sutter. "'Just Enough' Thread Safety" (C/C++ Users Journal,
22(9), September 2004).
About the Authors
Herb Sutter is the author of three highly acclaimed books,
Exceptional C++ Style, Exceptional C++, and
More Exceptional C++ (Addison-Wesley). He chairs the ISO C++
standards committee, and is contributing editor and columnist for
C/C++ Users Journal. As a software architect for Microsoft,
Sutter leads the design of the C++ language extensions for .NET programming.
Andrei Alexandrescu is the author of the award-winning book
Modern C++ Design (Addison-Wesley, 2001) and is a columnist for
C/C++ Uses Journal.