In C ++, public inheritance is considered as a-a relationship. Now let's look at private inheritance:
All members inherited from the private base class will become private attributes in the derived class, even if they are originally protected or public in the base class. Private inheritance means implemented-in-term-of (based on something ).
Article 38 states that the significance of composition is also "is-implemented-in-term-of". How can we choose between them? Use compound as much as possible to use private inheritance only if necessary: when a person who wants to become a derived class wants to access a protected component intended to become a base class, or wants to redefine virtual functions, another radical situation is the strong spatial relationship.
A widget Class records the number of calls to each member function. Periodically review the information during runtime. To do this, we need to set a timer so that we can see if it is time to collect statistics.
To reuse existing code, we found Timer:
Class timer {
Public:
Explicit timer (INT tickfrequency );
Virtual void ontick () const;
};
Each tick calls a virtual function. We can redefine the virtual function so that the latter can retrieve the current status of the widget.
To allow widgets to redefine the virtual functions in timer, Widgets must inherit from timer. However, public inheritance is not appropriate because widgets are not a timer. You cannot call ontick for a widget. in concept, it is not part of the widet interface.
We must use private to inherit Timer:
Class Widget: Private timer {
PRIVATE:
Virtual void ontick () const;
};
Once again, putting ontick in the public interface will cause the customer to think they can call it, which violates Clause 18.
This design is good, but it is not worth a few RMB. Private multiplication is not absolutely necessary. If we decide to replace it with composite, we only need to declare a nested private class in the widget. The latter inherits timer in the form of public and redefines ontick, put an object of this type in the Widget:
Class widget {
PRIVATE:
Class widgettimer: Public timer {
Public:
Virtual void ontick () const;
};
Widgettimer timer;
};
This design is more complex than just using private inheritance, but there are two reasons you may be willing or should choose such public inheritance and combination:
First, you may want to design a widget so that it can have derived classes, but you may also want to prevent derived clssses from redefining ontick. If widgets inherit from timer, the above idea is impossible, and even private inheritance is impossible. (Article 35 states that derived class can redefine virtual functions, even if they cannot be called) If widgettimer is a private member within the widget and inherits timer, the derived classes of a widget cannot use widgettimer, so it cannot inherit it or redefine its virtual function. Some are similar to Java final or C # sealed.
Second, you may want to minimize the compilation dependency of the widget. If the widget inherits timer, the timer definition must be visible when the widget is compiled. Therefore, the file that defines the widget must contain timer. h. However, if the widgettimer removes the widget and the widget contains a pointer pointing to the widgettimer, the widget can only carry a simple widgettimer declarative expression and no longer need to include anything related to the timer. Such decoupling may be an important measure for large systems.
There is also a radical situation that only applies when the class you process does not contain any data. Such classes does not have a non-static member variable and does not have a virtual function (this function will bring a vptr to each object ), there is also no virtual base classes (such base classes will also incur additional overhead on the volume, see Clause 40 ). This so-called empty class object does not use any space because there is no data belonging to the object to be stored. However, for technical reasons, C ++ determines that all objects are independent (not affiliated) all objects must have a non-zero size:
Class empty {};
Class holdsanint {
PRIVATE:
Int X;
Empty E; // no memory is needed
};
You will find that sizeof (holdsanint)> sizeof (INT); an empty member variable actually requires memory. In most compilers, sizeof (empty) gets 1, because in the face of "a zero-size independent object", C ++ officially inserts a char to a null object. However, the homogeneous requirement (alignment) may cause the compiler to add some padding (padding) to classes like holdsanint, so it is possible that the holdsanint object has more than one char size, in fact, it is enlarged to one more Int.
The independent (non-affiliated) constraint does not apply to the base class components in the derived class object, because they are not independent. If you inherit empty, instead of containing an object of that type:
Class holdsanint: private empty {
PRIVATE:
Int X;
};
Almost certainly sizeof (holdsanint) = sizeof (INT ). This is the so-called EBO (empty base optimization: optimization of the blank base class). If you are a Library Development member and your customers are very concerned about space, it is worth noting that EBO. It is also worth noting that generally, EBO is only feasible under single inheritance (rather than multi-inheritance.
In reality, the "empty" class is not really empty. Although they have never had non-static member variables, they often contain typedefs, enums, static member variables, or non-virtual functions. STL has many technical purposes for empty classes, which contain useful members (usually typedefs), including base classes unary_function and binary_function, these are the classes inherited by "User-Defined Function objects. Thanks to the extensive practices of EBO, this inheritance rarely increases the size of derived classes.
Despite this, most classes are not empty, so EBO is rarely a legitimate reason for private inheritance. Both composite and private inheritance mean is-implemented-in-term-of, but composite is easier to understand. Therefore, you should choose composite whenever you can.