Effective C++读书笔记---继承与面向对象设计

如果你了解C++各种特性的意义,你会发现,你对OOP的看法改变了。它不再是一项用来划分语言特性的仪典,而是可以让通过它说出你对软件系统的想法。一旦你知道该通过它说些什么,转移至C++世界也就不再是可怕的高要求了
三十二、条款32-确定你的public继承塑模出is-a关系
1.is-a并非唯一存在于classes之间的关系。别两个常见的关系是has-a(有一个)和is-implemented-in-terms-of(根据某物实现出)。这些关系将在条款38和39讨论。将上述这些重要的相互关系中的任何一个误塑为is-a而造成的错误设计,在C++中并不罕见,所以你应该确定你确实了解这些个“classes相互关系”之间的差异,并知道如何在C++中最好地塑造它们
2.“public继承”意味is-a。适用于base classes身上的每一件事情一定也适用于derived classes身上,因为每一个derived class对象也都是一个base class对象
三十三、条款33-避免遮掩继承而来的名称
1.derived classes内的名称会遮掩base classes内的名称。在public继承下从来没有人希望如此
2.为了让被遮掩的名称再见天日,可使用using声明式或转交函数(forwarding functions)
三十四、条款34-区分接口继承和实现继承
1.接口继承和实现继承不同。在public继承之下,derived classes总是继承base classes的接口
2.pure virtual函数只具体指定接口继承
3.简朴的(非纯)impure virtual函数具体指定接口继承及缺省实现继承
4.non-virtual函数具体指定接口继承以及强制实现继承。如果成员函数是个non-virtual函数,意味着它并不打算在derived classes中有不同的行为
三十五、条款35-考虑virtual函数以外的其他选择
1.籍由Non-Virtual Interface手法实现Template Method模式。这一基本设计,也就是“令客户通过public non-virtual成员函数间接调用private virtual函数”,称为non-virtual interface(NVI)手法。它是所谓Template Method设计模式(与C++ templates并无关联)的一人独特表现形式。我把这个non-virtual函数称为virtual函数的外覆器(wrapper)
2.籍由Function Pointers实现Strategy模式
3.籍由tr1::function完成Strategy模式。tr1::function对象的行为就像一般函数指针。这样的对象可接纳“与给定之目标签名式(target signature)兼容”的所有可调用(callable entities)--普通函数、函数对象、成员函数
4.将机能从成员函数移到class外部函数,带来的一个缺点是,非成员函数无法访问class的non-public成员
三十六、条款36-绝不重新定义继承而来的non-virtual函数
1.non-virtual函数都是静态绑定。这意思是pB被声明为一个pointer-to-Base,通过pB调用的non-virtual函数永远是Base所定义的版本,即使pB指向一个类型为“Base派生之class”的对象
2.non-virtual函数为class建立起一个“不变性凌驾特异性”的性质。所以绝对不要重新定义继承而来的non-virtual函数
三十七、条款37-绝不重新定义继承而来的缺省参数值
1.virtual函数系动态绑定(dynamically bound),而缺省参数值却是静态绑定(statically bound)
2.对象的所谓静态类型(static type),就是它在程序中被声明时所采用的类型。而动态类型则是指“目前所指对象的类型”,也就是说,动态类型可以表现出一个对象将会有什么行为。如:
Shape* ps;   // 静态类型为Shape*,无动态类型
Shape* pc = new Circle;  // 静态类型为Shape*,动态类型为Circle*
Shape* pr = new Rectangle; // 静态类型为Shape*,动态类型为Rectangle*
动态类型一如其名称所示,可在程序执行过程中改变(通常是经由赋值动作)
ps = pc;   // ps的动态类型如今是Circle*
ps = pr;   // ps的动态类型如今是Rectangle*
3.绝对不要重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定,而virtual函数--你唯一应该覆写的东西--却是静态绑定
三十八、条款38-通过复合塑模出has-a或“根据某物实现出”
1.复合(composition)是类型之间的一种关系,当某种类型的对象内含它种类型的对象,便是这种关系。在程序员之间复合这个术语有许多同义词,包括layering(分层),containment(内含),aggregation(聚合)和embedding(内嵌)
2.在应用域(application domain),复合意味has-a(有一个)。在实现域(implementation domain),复合意味is-implemented-in-terms-of(根据某物实现出)
三十九、条款39-明智而审慎地使用private继承
1.private继承意味is-implemented-in-terms-of(根据某物实现出)。如果你让class D以private形式继承class B,你的用意是采用了class B内已经备妥的某些特性,不是因为B对象和D对象存在有任何观念上的关系。private继承意味只有实现部分被继承,接口部分应略去。其在软件“设计”层面上没有意义,其意义只及于软件实现层面
2.条款38才刚指出复合的意义也是这样。你如何在两者之间取舍?答案很简单:尽可能使用复合,必要时才使用private继承。何时才是必要?主要是当protected成员和/或virtual函数牵扯进来的时候。当你面对两个“并不存在is-a关系”的两个classes,其中一个需要访问另一个的protected成员,或需要重新定义其一或多个virtual函数,private极有可能成为正统设计策略
3.和复合(composition)不同,private继承可以造成EBO(empty base optimization:空白其类最优化)。这对致力于“对象尺寸最小化”的程序库开发者而言,可能很重要
四十、条款40--明智而审慎地使用多重继承
1.最先要认请的一件事是,当多重继承(mutiple inheritance:MI)进入设计景框,程序有可能从一个以上的Base classes继承相同名称(如函数、typedef等等)。那会导致较多的岐义(ambiguity)机会
2.多重继承的意思是继承一个以上的Base classes,但这些base classes并不常在继承体系中又有更高级的base classes,因为那会导致要命的“钻石型MI”。
class File { ... };
class InputFile:public File { ... };
class OutputFile:public File { ... };
class IOFile:public InputFile,
  public OutputFile
{ ... };
任何时候如果你有一个继承体系而其中某个base class和某个derived class之间有一条以上的相通路线,你就必须面对这样一个问题:是否打算让base class内的成员变量经由每

一条路径被复制?C++在这场辨论中并没有倾斜立场:两个方案它都支持--虽然其缺省做法是执行复制。如果那不是你想要的,你必须令那个带有此数据的class成为一个virtual

base class,当然,前提是:你得为virtual继承付出代价
class File { ... };
class InputFile:virtual public File { ... };
class OutputFile:virtual public File { ... };
class IOFile:public InputFile,
  public OutputFile
{ ... };
3.非必要不使用virtual bases。如果必须使用,尽可能避免在其中放置数据。这么一来就不需要担心这些classes身上的初始化(或赋值)所带来的诡异事情了
4.多重继承的确有正当用途。其中一个情节涉及“public继承某个interface class”和“private继承某个协助实现的class”的两相结合

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章