effective C++: 6.继承与面向对象设计(五)

2014-11-24 03:15:38 · 作者: · 浏览: 1
们可以让non-virtual函数指定缺省参数,而private virtual函数负责真正的工作:
class Shape {
public:
enum ShapeColor{Red, Green, Blue};
void draw(ShapeColor color = Red) const
{
doDraw(color);
}
private:
virtual void doDraw(ShapeColor color) const = 0;//纯虚函数
};
class Rectangle : public Shape {
public:
private:
virtual void doDraw(ShapeColor color) const;//不需指定缺省参数
};
由于non-virtual函数应该绝对不被derived classes覆写(条款36),这个设计很清楚的使draw函数的color缺省参数值总为Red。


请记住:
绝对不要重新定义继承而来的缺省参数值,因为缺省参数值都是静态绑定,而virtual函数――你唯一应该覆写的东西――却是动态绑定。




条款38:通过复合塑模出has-a或“根据某物实现出”
public继承是“is-a“的关系,而复合有”has-a“或”根据某物实现出(is-implemented-in-terms-of)“的意思――当复合发生在应用域内的对象之间,表现出has-a关系;当它发生于实现域内则是表示“根据某物实现出”的关系。


请记住:
复合(composition)的意义和public继承完全不同。
在应用域(application domain),复合意味has-a(有一个)。在实现域(implementation domain),复合意味is-implemented-in-terms-of(根据某物实现出)。


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


c++中public继承视为is-a关系。现在看private继承:
class Person{...};
class Student: private Person {...};
void eat(const Person& p);
void study(const Student& s);
Person p;
Student s;
eat(p);
eat(s); //错误! 难道学生不是人?!
显然private继承不是is-a关系。


1.如果class之间的继承关系是private,编译器不会自动将一个derived对象转换为一个base对象。
2.由private base class继承而来的所有成员,在derived class中都会成为private属性,纵使它们在base class中原本是protected或public。


private继承纯粹只是一种实现技术,private继承意味着只有实现部分被继承,接口部分应略去。如果D以private形式继承B,意思是D对象根据B对象实现而得。


private继承意味着implemented-in-term-of(根据某物实现)。条款38说复合(composition)的意义也是is-implemented-in-term-of,如何进行取舍?


尽可能的使用复合,必要时才使用private继承:主要是当一个意欲成为derived class者想访问一个意欲成为base class者的protected成分,或为了重新定义virtual函数,还有一种激进情况是空间方面的厉害关系。


有个Widget class,它记录每个成员函数的被调用次数。运行期周期性的审查那份信息。为了完成这项工作,我们需要设定某种定时器,使我们知道收集统计数据的时候是否到了。
为了复用既有代码,我们发现了Timer:
class Timer {
public:
explicit Timer(int tickFrequency);
virtual void onTick() const;
};
每次滴答就调用某个virtual函数,我们可以重新定义那个virtual函数,让后者取出Widget的当时状态。
为了让Widget重新定义Timer内的virtual函数,Widget必须继承自Timer。但public继承并不适当,因为Widget并不是一个Timer。不能够对一个Widget调用onTick吧,观念上那并不是Wigdet接口的一部分。


我们必须用private继承Timer:
class Widget: private Timer{
private:
virtual void onTick() const;
};
再说一次,把onTick放进public接口内会导致客户以为他们可以调用它,那就违反了条款18.


这个设计好,但不值几文钱,private继乘并非绝对必要。如果我们决定用复合取而代之,是可以的,只要在Widget内声明一个嵌套式private class,后者以public形式继承Timer并重新定义onTick,然后放一个这种类型的对象在Widget内:
class Widget{
private:
class WidgetTimer: public Timer{
public:
virtual void onTick() const;
};
WidgetTimer timer;
};
这个设计比只是用private继承复杂一些,但是有两个理由可能你愿意或应该选择这样的public继承加复合:
首先,或许会想设计Widget使它得以拥有derived classes,但同时你可能会想阻止derived clssses 重新定义onTick。如果Widget继承自Timer,上面的想法就不可能实现,即使是private继承也不可能。(条款35说derived class可以重新定义virtual函数,即使它们不得调用它)但如果WidgetTimer是Widget内部的一个private成员并继承Timer,Widget的derived classes将无法取用WidgetTimer,因此无法继承它或重新定义它的virtual函数。
第二,或许想要将Widget的编译依存性降至最低,若Widget继承Timer,当Widget被编译时,timer的定义必须可见,所以定义Widget的那个文件必须包含Timer.h。但如果WidgetTimer移除Widget之外而Widget内含一个指针指向WidgetTimer,Widget可以只带着一个简单的WidgetTimer声明式,不再需要#include任何与timer有关的东西。对大型 系统而言,如此的解耦(decouplings)可能是重要的措施。


还有一种激进情况,只适用于你所处理的class不带任何数据时。这样的classes没有non-static成员变量,没有virtual函数(这种函数会为每个对象带来一个vptr),也没有virtual base classes(这样的base classes 也会招致体积上的额外开销,见条款40)。这种所谓的empty class对象不使用任何空间,因为没有任何隶属对象的数据要存储,然而由于技术上的理由,c++裁定凡是独立(非附属)对象都必须有非零大小: