Chapter 17. Derived Classes

FAQ 17.01 What is the purpose of this chapter?

image

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.

FAQ 17.02 How does C++ express inheritance?

image

Here is a typical C++ inheritance declaration.

image

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:

image

UML uses the following notation to show inheritance.

image

FAQ 17.03 What is a concrete derived class?

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:

image

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.

image

FAQ 17.04 Why can't a derived class access the 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_.

image

The designer of a base class gives derived classes access to implementation details by declaring them as protected:.

FAQ 17.05 How can a base class protect derived classes so that changes to the base class will not affect them?

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.

image

FAQ 17.06 Can a derived class pointer be converted into a pointer to its public base class?

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.

image

FAQ 17.07 How can a class 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.

image

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).

FAQ 17.08 How can a class 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.

image

Here is the C++ syntax for composition (that is, Y has-a X). This is the preferred solution.

image

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.

image

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.

image

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.

FAQ 17.09 How can a class 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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset