Chapter 37. Private and Protected Inheritance

FAQ 37.01 What are private inheritance and protected inheritance?

Has-a, not is-a. From the user's perspective, private and protected inheritance are semantically similar to composition, but they are very different from normal public inheritance. Thus private and protected inheritance are a lot more like “has-a” than “is-a” (more precisely, than “is-substitutable-for”). Here are the ways they are like “has-a”.

Like normal composition, private and protected inheritance cause an inner object to be contained inside the outer object. With normal composition, this inner object is called a member object. The syntax for doing this with private inheritance is different than the syntax for normal composition, but the idea is the same. With private and protected inheritance, the inner object is called the base class subobject. The important thing to note is that the outer object contains the inner object in both cases.

Like normal composition, private and protected inheritance prevent normal users from directly accessing the inner object. With normal composition, this is normally done by declaring the member object in the private: or protected: part of the outer object. The syntax for doing this with private inheritance is different than the syntax for normal composition, but the idea is the same. With private and protected inheritance, this encapsulation is done automagically: normal users are not allowed to convert a derived pointer to its private or protected base class pointer (this is different than the normal is-a conversion of public inheritance; see FAQ 2.24).

Like normal composition, private and protected inheritance allow the outer object to select specific features of the inner object that users can access, and users are prevented from accessing any other features than the ones explicitly allowed by the outer object. With normal composition, the outer object grants normal users access to specific features of the inner object by call-through member functions. A call-through member function is often a one-line function that simply calls the corresponding member function on the inner object. The syntax for doing this with private inheritance is different than that for normal composition, but the idea is the same. With private and protected inheritance, there are two options: either the derived class defines a call-through member function that calls the corresponding member function of the private or protected base class subobject, or the using syntax can be used to make a base class member function public: (see FAQ 37.06).

FAQ 37.02 What is the difference between private inheritance and protected inheritance?

image

In private inheritance, the relationship with the base class is a private decision; only members and friends of the privately derived class can exploit the relationship with the private base class. In protected inheritance, the relationship with the base class is a protected decision, so members and friends of the protected derived class and members and friends of classes derived from the protected derived class can exploit the protected inheritance relationship, but normal users cannot.

Protected inheritance is less restrictive than private inheritance and therefore introduces more coupling between the derived class and the base class. With protected inheritance, if the relationship between the protected base class and the derived class is changed (or if the protected operations of the protected base class change), the effects may reach beyond the protected derived class and its friends to classes derived from the protected derived class, classes derived from those derived classes, and so on.

This is a for-better-for-worse situation; derived classes have more coupling, but they also have the ability to exploit the relationship between the derived class and the base class.

FAQ 37.03 What is the syntax and semantics for private and protected inheritance?

The following example shows a simple has-a relationship between a car object and its engine object; it uses normal composition, where the Engine member object appears in the private: section of the Car class.

image

Obviously composition does not create an is-a relationship: a CarA is not a kind-of an Engine. In particular, users cannot legally convert a CarA* to an Engine*. Also note that a CarA object contains exactly one Engine object (though it could be made to contain more than one).

Private inheritance accomplishes essentially the same thing. In the following example, CarB is said to privately inherit from Engine (if the symbols : private Engine are changed to : protected Engine, CarB is said to protectedly inherit from Engine).

image

Just as with normal composition, there is no is-a relationship: a CarB is not a kind-of Engine. In particular, normal (nonfriend) users cannot legally convert a CarB* to an Engine*. Also like normal composition, a CarB object contains exactly one Engine object (however, unlike normal composition, private and protected inheritance does not allow a second Engine subobject to appear as a second private and/or protected base class).

The main difference between composition and private/protected inheritance is access to the protected members of Engine. With private/protected inheritance, members and friends of CarB can access the protected: members of Engine (in this case, they can access both Engine::publ() and Engine::prot()). However, with normal composition, members and friends of CarA can only access Engine::publ(); they are forbidden to access Engine::prot(). The usual reason people use private/protected inheritance is for this additional access authority; but note that the extra authority carries with it extra responsibility.

Another difference between composition and private/protected inheritance is the ability to convert a CarB* to an Engine*. With private/protected inheritance, members and friends of CarB can convert a CarB* to an Engine*. With normal composition this is not possible: no one can legally convert a CarA* to an Engine*.

There are several caveats when using private/protected inheritance. Simple composition (has-a) is needed if it is necessary to contain several member objects of the same class; private or protected inheritance can introduce unnecessary multiple inheritance.

FAQ 37.04 When should normal has-a be used, rather than private or protected inheritance?

Use normal has-a when you can; use private or protected inheritance when you have to.

Normal composition (has-a) is preferred because it leads to fewer dependencies between classes. Private and protected inheritance are more expensive to maintain because they increase the number of classes that have access to the protected parts of other classes—they increase coupling.

Private or protected inheritance is often used when the goal is has-a but the interface of the contained class is insufficient. In this case, an alternative to private or protected inheritance is improving the public interface of the base class so that simple composition can be used. If you cannot change the interface of the base class (for example, because the source code is not available), you can create one derived class (often using public inheritance) that has an improved interface. This derived class with its improved interface is then used via simple composition (has-a).

FAQ 37.05 What are the access rules for public, protected, and private inheritance?

In the following example, class B has a public: member, a protected: member, and a private: member.

image

Class PrivD privately inherits from B, class ProtD protectedly inherits from B, and PublD publicly inherits from B.

image

With private inheritance, the public and protected parts of B become private in PrivD. This means that PrivD can access these member functions, but user code and classes derived from PrivD cannot access them.

With protected inheritance, the public and protected parts of B become protected in ProtD. This means that members and friends of ProtD can access these member functions, as can classes derived from ProtD, but user code cannot access them.

With public inheritance, the public parts of B become public on PublD, and the protected parts of B remain protected in PublD.

In all three cases, the private parts of B are inaccessible to the derived classes (PrivD, ProtD, and PublD) as well as to user code.

FAQ 37.06 In a private or protected derived class, how can a member function that was public in the base class be made public in the derived class?

The name (not the entire signature) of the member function should be declared in the public interface of the derived class preceded by the keyword using. For example, to make the member function B::f(int,char,float) public in PrivD, say this:

image

The syntax for doing this with protected inheritance is identical.

There are two limitations to this technique: overloaded names can't be distinguished, and a base member cannot be made public if it was protected in the base class (that is, this technique cannot be used to make Base::g(double,char) public in the derived class). When necessary, both these limitations can be avoided by defining a call-through member function in the privately/protectedly derived class, as shown in the following example.

image

FAQ 37.07 Should a pointer be cast from a private or protected derived class to its base class?

No.

Within a member function or friend function of a private or protected derived class, the relationship to the base class is known and the upward pointer or reference conversion takes place automatically without a cast.

In normal user code, the relationship to a private or protected base class is inaccessible and the conversion is illegal. Users should not perform a cast because private or protected inheritance is a nonpublic decision of the derived class. The cast will subtly break at some future date if/when the private or protected derived class chooses to change or remove the private/protected inheritance relationship.

The conclusion is that only a class and its friends have the right to convert a pointer to the class's nonpublic base class. The member functions and friend functions of the privately/protectedly derived class don't need a cast because the relationship with the base class is directly accessible to them.

Here's an even simpler conclusion—don't use pointer casts unless there is an overriding reason to do so!

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

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