C++ has a second means of implementing the has-a relationship: private inheritance. With private inheritance, public and protected members of the base class become private members of the derived class. This means the methods of the base class do not become part of the public interface of the derived object. They can be used, however, inside the member functions of the derived class.

Let’s look at the interface topic more closely. With public inheritance, the public methods of the base class become public methods of the derived class. In short, the derived class inherits the base-class interface. This is part of the is-a relationship. With private inheritance, the public methods of the base class become private methods of the derived class. In short, the derived class does not inherit the base-class interface. As you saw with contained objects, this lack of inheritance is part of the has-a relationship.

With private inheritance, a class does inherit the implementation. For example, if you base a Student class on a string class, the Student class winds up with an inherited string class component that can be used to store a string. Furthermore, the Student methods can use the string methods internally to access the string component.

Containment adds an object to a class as a named member object, whereas private inheritance adds an object to a class as an unnamed inherited object. This book uses the term subobject to denote an object added by inheritance or by containment.

Private inheritance, then, provides the same features as containment: Acquire the implementation, don’t acquire the interface. Therefore it, too, can be used to implement a has-a relationship. In fact, you can produce a Student class that uses private inheritance and has the same public interface as the containment version. Thus the differences between the two approaches affect the implementation, not the interface. Let’s see how you can use private inheritance to redesign the Student class.

A New Version of the Student Class Example

To get private inheritance, use the keyword private instead of public when defining the class. (Actually, private is the default, so omitting an access qualifier also leads to private inheritance.) The Student class should inherit from two classes, so the declaration should list both:

class Student : private std::string, private std::valarray

{

public:

    ...

};

Having more than one base class is called multiple inheritance (MI). In general, MI, particularly public MI, can lead to problems that have to be resolved with additional syntax rules. We’ll talk about such matters later in this chapter. But in this particular case, MI causes no problems.

Note that the new class doesn’t need private data. That’s because the two inherited base classes already provide all the needed data members. The containment version of this example provides two explicitly named objects as members. Private inheritance, however, provides two nameless subobjects as inherited members. This is the first of the main differences in the two approaches.

Initializing Base-Class Components

Having implicitly inherited components instead of member objects affects the coding of this example because you can no longer use name and scores to describe the objects. Instead, you have to go back to the techniques you used for public inheritance. For example, consider constructors. Containment uses this constructor:

Student(const char * str, const double * pd, int n)

   : name(str), scores(pd, n) {}      // use object names for containment

The new version should use the member initializer list syntax for inherited classes, which uses the class name instead of a member name to identify a constructor:

Student(const char * str, const double * pd, int n)

   : std::string(str), ArrayDb(pd, n) {}  // use class names for inheritance

Here, as in the preceding example, ArrayDb is a typedef for std::valarray. Be sure to note that the member initializer list uses terms such as std::string(str) instead of name(str). This is the second main difference in the two approaches

Listing 14.4 shows the new class declaration. The only changes are the omission of explicit object names and the use of class names instead of member names in the inline constructors.

Listing 14.4. studenti.h

// studenti.h -- defining a Student class using private inheritance

#ifndef STUDENTC_H_

#define STUDENTC_H_

#include

#include

#include

class Student : private std::string, private std::valarray

{

private:

    typedef std::valarray ArrayDb;

    // private method for scores output

    std::ostream & arr_out(std::ostream & os) const;

public:

    Student() : std::string("Null Student"), ArrayDb() {}

Перейти на страницу:

Все книги серии Developer's Library

Похожие книги