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 最优化.这对致力于"对象尺寸最小化"的程序库开发者而言,可能很重要.
相关文章推荐
- c/c++获取时间和计算时间差的几种方法总结
- C++内存分配方式——(别人的博客)
- 详解C++中的指针结构体数组以及指向结构体变量的指针
- C/C++学习笔记---primer基础知识
- 引用与指针c++
- C++读取windows系统性能技术器(PDH)
- C语言之内存四区1
- C++虚函数与纯虚函数的区别
- 出学C++之while (std::cin >> value)问题
- C++基本知识总结
- C/C++学习笔记----指针的理解
- C++多态的实现原理
- 冒泡排序的C++实现
- 一个技术博客,关于PE_C++_驱动_病毒
- C++中结构体的类型定义和初始化以及变量引用
- C++内存管理
- C语言实现单链表-02版
- c++11——可变参数模板
- C++的深拷贝与浅拷贝
- 基于C语言中野指针的深入解析