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

条款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 最优化.这对致力于"对象尺寸最小化"的程序库开发者而言,可能很重要.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值