您的位置:首页 > 编程语言 > C语言/C++

Effective C++——条款39(第6章)

2015-09-15 17:26 363 查看

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

Use private inheritance judiciously

条款32曾经论证过C++如何将 public 继承视为is-a关系.现在看看 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
eat(s);                // error! 难道Student不是Person??
显然 private 继承并不意味is-a关系,那么它意味什么?

第一条规则是:如果 class 之间的继承关系是 private,编译器不会自动将一个derived class 对象(例如Student)转换为一个base class 对象(例如Person).这个 public 继承的情况不同.这也就是为什么通过s调用eat会失败的原因.第二条规则是:
private base class 继承而来的所有成员,在derived class 中都会变成 private 属性
,纵然它们在base class 中原本是 protected 或 public 属性.

private 继承意味着implementation-in-terms-of(根据某物实现出).如果让 class D以 private 形式继承 class B,用意是为了采用 class B内已经备妥的某些特性,不是因为B对象和D对象存在有任何挂念上的关系.private 继承纯粹是一种实现技术(这就是为什么继承自一个 private base class 的每样东西都在 class 内是 private:因为它们都只是实现枝节而已).借用条款34提出的术语,private
继承意味只有实现部分被继承,接口部分应略去.如果D以 private 形式继承B,意思是D对象根据B对象实现而得,再没有其他意涵了.private 继承在软件"设计"层面上没有意义,其意义只涉及软件实现层面.

private 继承意味is-implementation-in-terms-of(根据某物实现出),这个事实令人不安,因为条款38指出复合(compostion)的意义也是这样.如何在两者之间取舍?答案很简单,尽可能使用复合,必要时才使用
private 继承.何时才是必要?
主要是当 protected 成员或 virtual 函数牵涉进来的时候.

有一种激进情况涉及空间最优化,可能促使选择"private继承"而不是"继承加复合".这种情况只适用于处理的 class 不带任何数据时.这样的 class 没有non-static 成员变量,没有 virtual 函数(因为这种函数额存在会为每个对象带来一个vptr,详见条款7),也没有 virtual
base class(因为这种base class 也会招致体积上的额外开销,详见条款40).于是这种所谓的empty class 对象不使用任何空间,因为没有隶属对象的数据需要存储.然而由于技术上的理由,C++裁定凡是独立对象都必须有非零大小,所以如果这样:

class Empty {};        // no data
class HoldsAnInt {
private:
    int x;
    Empty e;        // 应该不需要任何内存
};
测试 sizeof(HoldsAnInt) > sizeof(int),竟然发现Empty成员变量要求内存.在大多数情况下,sizeof(Empty)获得1,因为面对"大小为零的独立对象",通常C++官方勒令插入一个 char 到空对象内.然而齐位需求(alignment,详见条款50)可能造成编译器为类似HoldaAnInt这样的
class 加上一些衬垫,所以有可能HoldaAnInt不只获得一个 char 大小,可能被放大到一个 int 大小.

这个约束不适用于derived class 对象内的base class 成分,因为它们并非独立.如果继承Empty,而不是内含一个那种类型的对象:

class HoldsAnInt : private Empty {
private:
    int x;
};
几乎可以确定 sizeof(HoldsAnInt) == sizeof(int),这是所谓的EBO(empty base optimization;空白基类最优化).如果是一个程序库开发人员,客户非常在意空间,那么值得EBO.但EBO一般只在单一继承下才可行.

STL就有许多技术用途的empty class,其中内含有用的成员,包括base class unary_function和binary_function,这些是"用户自定义的函数对象"通常会继承的class.

当面对"并不存在is-a关系"的两个 class,其中一个需要访问另一个的 protected 成员,或需要重新定义其一或多个 virtual 函数,private 继承极有可能成为正确的设计策略.即便如此,一个混合了 public 继承和复合的设计,往往也能够得到想要的行为."明智而审慎地使用private继承"意味,在考虑过所有其他方案后,如果仍然认为 private 继承是"表现程序内两个class之间的关系"的最佳方法,这才用它.

注意:

private 继承意味着is-implemented-in-terms-of(根据某物实现出).它通常比复合的级别低,但当derived class 需要访问 protected base class 的成员,或需要重新定义继承而来的 virtual 函数时,这么设计是合理的.

和复合(compostion)不同,private 继承可以造成empty base 最优化.这对致力于"对象尺寸最小化"的程序库开发者而言,可能很重要.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: