This chapter discusses derived classes and inheritance at the programming language level. Part II discusses the same information on a design level and should be read before this chapter. This chapter dwells only on public, single, nonvirtual inheritance, which is the most prevalent form. Chapter 38 discusses some of the other types of inheritance.
Here is a typical C++ inheritance declaration.
This relationship can be described in several equivalent ways.
• Car
is a kind-of Vehicle
.
• Car
is derived from Vehicle
.
• Car
is a subclass of Vehicle
.
• Car
is a child class of Vehicle
(not common in the C++ community).
• Vehicle
is the base class of Car
.
• Vehicle
is the parent class of Car
(not common in the C++ community).
• Vehicle
is the super-class of Car
(not common in the C++ community).
As a consequence of the kind-of relationship, a Car
object can be treated as a Vehicle
object. For example, since function f(Vehicle&)
accepts any kind-of Vehicle
, it can be passed a Car
or an object of any other class derived from Vehicle
:
UML uses the following notation to show inheritance.
A concrete derived class is a derived class that has no pure virtual functions.
Because an abstract class cannot be instantiated directly, one or more derived classes are normally defined as implementations of the abstraction provided by the abstract class. A concrete derived class simply provides definitions for all its inherited pure virtual functions. If a definition for one of the inherited pure virtual functions is forgotten, any attempt to instantiate the class results in the compiler issuing an error message.
Abstract base class Base
has two pure virtual member functions f()
and g()
, and derived class Derived
provides a definition for f()
but not for g()
. Therefore Derived
is also abstract:
If Derived2
were derived from Derived
and Derived2
had to be concrete, Derived2
would have to provide a definition for g()
, but it wouldn't be required to override Derived::f()
. This is shown in the following example.
private:
members of its base class?A derived class can't access the private:
members of its base class because the base class intentionally hides some of its implementation details from its derived classes.
Suppose class Fred
contains a member datum or member function that is likely to change. Unless derived classes need to access this member, the base class Fred
would be wise to declare the member as private:
. This reduces the ripple effect of changes in the base class. For example, the private:
member can be removed or modified without fear of breaking derived classes. That is, the derived classes are protected from rewrites whenever the semantics or even existence of the private:
member change.
For example, class Wilma
below can access publ_
and prot_
, but cannot access priv_
.
The designer of a base class gives derived classes access to implementation details by declaring them as protected:
.
The easiest solution is to define a protected:
interface in addition to the public:
interface.
A class hierarchy is more resilient to changes if it has two distinct interfaces for two distinct sets of users.
• Its public:
interface serves unrelated classes.
• Its protected:
interface serves derived classes.
Both interfaces must be fully specified. For instance, the actual raw data of a class could be private:
with a set of protected: inline
member functions for accessing this data. These inline
member functions define an interface between the derived classes and the raw bits of the base class. Then the private:
data of the base class could be changed within reasonable bounds without affecting the derived classes. It would still be necessary to recompile the derived classes after a change to the base class, though the source code of the derived class would not need to be changed unless the protected:
interface is modified in a nonbackward compatible manner.
For example, suppose class Base
has an int
data member. Base
can ensure that derived classes do not rely on the specific data structure by making the data structure private:
(in this case, a simple int
) and defining inline protected:
members for accessing these data. Derived class Derived
accesses the value using these protected: inline
member functions.
Such conversions are possible and don't even require a pointer cast.
A publicly derived class is a kind-of its base class. By implication, the upward conversion is perfectly safe, and is quite common. For example, a pointer to a Car
is in fact already pointing at a Vehicle
, since a Car
is a kind-of a Vehicle
.
Y
be a kind-of another class X
as well as getting the bits of X
?This is easy: use public inheritance.
Here is the C++ syntax for public inheritance.
This does two distinct things. First, it provides the kind-of relationship: Y
is a kind-of X
, therefore Y
supports the same services as X
(Y
might add some new member functions as well). Second, it shares bits and code: Y
inherits X
's bits (data structures) and code (algorithms).
Y
get the bits of an existing class X
without making Y
a kind-of X
?There are three alternatives. The preferred solution is normal composition, also known as has-a. But in some cases private inheritance should be used, and in a few cases, protected inheritance should be used.
Here is the class X that will be used in each of the three following examples.
Here is the C++ syntax for composition (that is, Y
has-a X
). This is the preferred solution.
Here is the C++ syntax for private inheritance, which is semantically the same as has-a but with an increased ripple effect (changes to the protected:
part of X can break the private derived class Y2
). This is the second of the three alternatives.
Here is the C++ syntax for protected inheritance, which is semantically the same as has-a but with an even greater ripple effect than private inheritance (changes to the protected:
part of X
can break the protected derived class Y3
and can also break any classes derived from Y3
). This is the last of the three alternatives.
In all three cases, a Y
object has a X
object, and users of Y
are unaware of any relationship between Y
and X
. For example, user code will not break if the relationship between Y
and X
changes—or even is eliminated. See FAQ 37.01 for more information on private and protected inheritance.
Y
be a kind-of another class X
without inheriting the bits of X
?It's doable, but C++ has to be outfoxed.
The only mechanism provided by C++ that defines the kind-of relationship also forces inheriting the bits of the base class. If the base class's data structures are inappropriate for certain derived classes, it's necessary to outfox C++ by deferring the definition of the bits to the lower levels of the class hierarchy.
One way to do this is to define an ABC that possesses no (or few) internal data structures, then define the data structures in the concrete derived classes. In this way, the derived classes define the kind-of relationship, but they don't have any bits imposed on them by the base class. For example, both X
and Y
would inherit from this common ABC, but they would not inherit from each other. Strictly speaking, this does not achieve the original goal (Y
is not a kind-of X
), but it is normally close enough. The reason is that the bulk of the system can (hopefully) be written to use the ABC rather than X
or Y
directly, and inheritance allows both X
and Y
to be passed into the bulk of the system.