我们复现条款32的例子,并以private继承替换public继承:
class Person {...};
class Student : private Person {...};
void eat(const Person& p);
void study(const Student& s);
Person p;
Student s;
eat(p); // ok
study(s); // error
private继承意味着implemented-in-terms-of(根据某物实现出)。如果你让D以private继承B,你的用意是为了采用B内已经完备的某些特性,不是因为B和D存在任何观念上的关系。
条款38指出复合的意义也是is-implemented-in-terms-of,应当尽可能使用复合,必要时才使用private继承。主要是当protected成员或virtual函数牵扯进来时,必要时才使用private继承。
我们让Widget class记录每个成员函数的被调用次数。运行期间我们将周期性检查那份信息。为了这项工作,我们需要设定某种计时器。
我们创建一个Timer对象,可调整为以我们需要的任何频率检查信息:
class Timer {
public:
explicit Timer(int tickFrequency);
virtual void onTick() const;
...
};
为了让Widget重新定义Timer内的virtual函数,Widget必须继承自Timer。我们必须以private形式继承Timer:
class Widget : private Timer {
private:
virtual void onTick() const;
...
};
Timer的public onTick函数在Widget内变成private,使得客户不会调用它。
这是个好设计,但是private继承并非绝对必要,我们可以使用嵌套类:
class Widget {
private:
class WidgetTimer : public Timer {
public:
virtual void onTick() const;
...
};
WidgetTimer timer;
...
};
你或许会想设计Widget使它得以拥有派生类,但你可能会想阻止派生类重定义onTick。如果以嵌套类的形式实现,派生类将无法取用WidgetTimer,因此无法继承或重定义它的虚函数。
有一种激进的情况涉及空间最优化,可能会促使你选择private继承而不是继承加复合。这个情况只适适用于你所处理的class不带任何数据时,这种空类对象不使用任何空间,然而C++中凡是独立的对象都必须有非零大小,所以如果你这样做:
class Empty {};
class HoldsAnInt {
private:
int x;
Empty e;
};
你会发现HoldsAnInt的内存大小大于int的内存大小。而空类只有在独立存在的情况下有非零大小,当我们使用继承,而不是内含:
class HoldsAnInt : private Empty {
private:
int x;
};
此时HoldsAnInt和int的内存大小相等。这就是所谓的空基类优化(EBO, empty base optimization)。
请记住
- private继承意味着is-implemented-in-terms-of。它通常比复合级别低。但是当派生类需要访问protected基类的成员,或需要重新定义继承而来的virtual函数时,这么设计是合理的。
- 和复合不同,private继承可以造成空基类优化。这对致力于对象大小最小化的程序库开发者而言,可能很重要。