Skip to content

Latest commit

 

History

History
89 lines (68 loc) · 2.99 KB

File metadata and controls

89 lines (68 loc) · 2.99 KB

条款39:明智而谨慎地使用private继承

我们复现条款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继承可以造成空基类优化。这对致力于对象大小最小化的程序库开发者而言,可能很重要。