Item 39:明智地使用private继承 Effective C++笔记
来源:互联网 发布:js yui在线压缩 编辑:程序博客网 时间:2024/05/23 10:53
Item 39: Use private inheritance judiciously.
Item 32提出public继承表示"is-a"的关系,这是因为编译器会在需要的时候将子类对象隐式转换为父类对象。 然而private继承则不然:
class Person { ... };class Student: private Person { ... }; // inheritance is now privatevoid eat(const Person& p); // anyone can eatPerson p; // p is a PersonStudent s; // s is a Studenteat(p); // fine, p is a Personeat(s); // error! a Student isn't a Person
Person
可以eat
,但Student
却不能eat
。这是private继承和public继承的不同之处:
- 编译器不会把子类对象转换为父类对象
- 父类成员(即使是public、protected)都变成了private
子类继承了父类的实现,而没有继承任何接口(因为public成员都变成private了)。 因此private继承是软件实现中的概念,与软件设计无关。 private继承和对象组合类似,都可以表示"is-implemented-in-terms-with"的关系。那么它们有什么区别呢? 在面向对象设计中,对象组合往往比继承提供更大的灵活性,只要可以使用对象组合就不要用private继承。
private继承
我们的Widget
类需要执行周期性任务,于是希望继承Timer
的实现。 因为Widget
不是一个Timer
,所以我们选择了private继承:
class Timer {public: explicit Timer(int tickFrequency); virtual void onTick() const; // automatically called for each tick};class Widget: private Timer {private: virtual void onTick() const; // look at Widget usage data, etc.};
在Widget
中重写虚函数onTick
,使得Widget
可以周期性地执行某个任务。为什么Widget
要把onTick
声明为private
呢? 因为onTick
只是Widget
的内部实现而非公共接口,我们不希望客户调用它(Item 18指出接口应设计得不易被误用)。
private继承的实现非常简单,而且有时只能使用private继承:
- 当
Widget
需要访问Timer
的protected成员时。因为对象组合后只能访问public成员,而private继承后可以访问protected成员。 - 当
Widget
需要重写Timer
的虚函数时。比如上面的例子中,由于需要重写onTick
单纯的对象组合是做不到的。
对象组合
我们知道对象组合也可以表达"is-implemented-in-terms-of"的关系, 上面的需求当然也可以使用对象组合的方式实现。但由于需要重写(override)Timer
的虚函数,所以还是需要一个继承关系的:
class Widget {private: class WidgetTimer: public Timer { public: virtual void onTick() const; }; WidgetTimer timer;};
内部类WidgetTimer
public继承自Timer
,然后在Widget
中保存一个WidgetTimer
对象。 这是public继承+对象组合的方式,比private继承略为复杂。但对象组合仍然拥有它的好处:
- 你可能希望禁止
Widget
的子类重定义onTick
。在Java中可以使用finel
关键字,在C#中可以使用sealed
。 在C++中虽然没有这些关键字,但你可以使用public继承+对象组合的方式来做到这一点。上述例子便是。 - 减小
Widget
和Timer
的编译依赖。如果是private继承,在定义Widget
的文件中势必需要引入#include"timer.h"
。 但如果采用对象组合的方式,你可以把WidgetTimer
放到另一个文件中,在Widget
中保存WidgetTimer
的指针并声明WidgetTimer
即可, 见Item 31。
EBO特性
我们讲虽然对象组合优于private继承,但有些特殊情况下仍然可以选择private继承。 需要EBO(empty base optimization)的场景便是另一个特例。 由于技术原因,C++中的独立空对象也必须拥有非零的大小,请看:
class Empty {}; class HoldsAnInt {private: int x; Empty e; };
Empty e
是一个空对象,但你会发现sizeof(HoldsAnInt) > sizeof(int)
。 因为C++中独立空对象必须有非零大小,所以编译器会在Empty
里面插入一个char
,这样Empty
大小就是1。 由于字节对齐的原因,在多数编译器中HoldsAnInt
的大小通常为2*sizeof(int)
。更多字节对齐和空对象大小的讨论见Item 7。 但如果你继承了Empty
,情况便会不同:
class HoldsAnInt: private Empty {private: int x;};
这时sizeof(HoldsAnInt) == sizeof(int)
,这就是空基类优化(empty base optimization,EBO)。 当你需要EBO来减小对象大小时,可以使用private继承的方式。
继承一个空对象有什么用呢?虽然空对象不可以有非静态成员,但它可以包含typedef, enum, 静态成员,非虚函数 (因为虚函数的存在会导致一个徐函数指针,它将不再是空对象)。 STL就定义了很多有用的空对象,比如unary_function
, binary_function
等。
总结
- private继承的语义是"is-implemented-in-terms-of",通常不如对象组合。但有时却是有用的:比如方法protected成员、重写虚函数。
- 不同于对象组合,private继承可以应用EBO,库的开发者可以用它来减小对象大小。
除非注明,本博客文章均为原创,转载请以链接形式标明本文地址: http://harttle.com/2015/09/06/effective-cpp-39.html
- Item 39:明智地使用private继承 Effective C++笔记
- Effective C++ Item 39 明智而审慎地使用 private 继承
- Item 40:明智地使用多继承 Effective C++笔记
- Effective C++:条款39:明智而审慎地使用private继承
- 读书笔记《Effective C++》条款39:明智而审慎地使用private继承
- C++之明智审慎地使用private继承(39)---《Effective C++》
- 《Effective C++》读书笔记之item39:明智而审慎地使用private继承
- 《Effective C++》 40:明智而审慎地使用多重继承
- 条款39:明智而审慎地使用private继承
- 条款39:明智而审慎地使用private继承
- 条款39、明智审慎地使用private继承
- 条款39:明智而审慎地使用private继承
- [effectiv c++]条款39:明智而审慎地使用private继承
- Effective C++ Item 40 明智而审慎地使用多重继承
- effective C++笔记之条款41、42: 区分继承和模板、明智地使用私有继承
- item39:明智而审慎地使用private继承
- 《Effective C++》读书笔记之item40:明智而审慎地使用多重继承
- Effective C++:条款40:明智而审慎地使用多重继承
- 我读过的最好的epoll讲解
- bin/ld.exe: error: cannot find -landroid等问题
- 选择排序——堆排序
- 文章标题
- 杭电acm--1064
- Item 39:明智地使用private继承 Effective C++笔记
- parseInt中的转换细节
- 黑马程序员——网络编程
- Java中弹出对话框中的几种方式
- 文章标题
- HDU2025 查找最大元素
- Coursera_Stanford_ML_ex4_神经网络训练 作业记录
- html元素与间距
- 那些看着高大上的名词