《Effective C++》读书笔记6.继承与面向对象设计

继承与面向对象设计

条款32:确定你的public继承塑模出is-a关系

public继承意味is-a。适用于base classes身上的每一件事情一定适用于derived classes身上,以为每一个derived class对象也一定是一个base class对象


条款33:避免遮掩继承而来的名称

derived class作用域被嵌套进base class作用域内,类似于local和global。derived中的函数会遮掩base中的所有同名函数,即使他们参数不同
解决方案 用using声明或者用转交函数

class Base{
public:
	virtual void mf1();
	virtual void mf1(int);
	...
};
class Derived:private Base{
public:
	using Base::mf1;//using 申明
	virtual void mf1(int x)//转交函数
	{
		Base::mf1(x);
	}
	...
};

条款34:区分接口继承和实现继承

身为class设计者,有时你会希望derived classes只继承成员函数的接口;有时你又会希望derived class同时继承函数的接口和实现,但又能override它们所继承的实现;有时你希望derived classes同时继承函数的接口和实现,并且不允许覆写任何东西。

接口继承和实现继承不同(就像声明和定义)。

pure virtual函数指定接口继承

简朴的impure virtual函数指定接口继承和缺省实现继承

non-virtual函数具体指定接口继承以及强制性实现继承

pure virtual函数:只提供接口

impure virtual函数:提供接口和缺省实现。这里,接口和缺省实现应该分开。这里有2种方法

– 将这种函数变成pure virtual的形式,同时提供另一个函数作为缺省实现,在派生类中,暴露这个缺省实现(protected)

class Airplane{
public:
	virtual void fly(const Airport& destination)=0;
	...
protected:
	void defaultFly(const Airport& destination);//缺省实现,供派生类调用
}

– 将它声明为pure virtual并提供实现,在派生类的对应函数中调用基类的缺省实现(调用纯虚函数的唯一途径是“调用时明确指出其class类型”

non-virtual是为了令derived classes继承函数的接口和一份强制实现。它代表的意义是不变性凌驾于特异性

两大错误:所有函数声明为non-virtual(使得derived classes没有空余空间进行特化);所有函数声明为virtual(立场不坚定,有些函数就是不该被重定义)

virtual函数的成本:80-20法则(程序80%的执行时间花在20%的代码上),当你担心virtual的成本,请先将精力放在20%的代码上


条款35:考虑virtual函数以外的其他选择

假设我们现在写一款游戏,不同角色攻击时会释放不同的技能,可能我们会想到使用virtual函数,让特殊角色都继承于GameCharacter,GameCharacter提供了一份缺省实现,特殊角色可针对自己的情况改写攻击函数。

当然这也是我们最常规的办法,除此之外,也存在着许多其他的方式供我们选择。

Non-Virtual Interface的手法实现Template Method模式

lass GameCharacter{
public:
    void attack(){
        beforeAttack();
        doAttack();
        afterAttack();
    }
private:
    virtual void doAttack(){
        ...
    }
};

这就是所谓的NVI手法,attack是作为doAttack的外覆器。

NVI的优势在于我们可以在进行实际操作前后做些处理,正如我们beforeAttack(),afterAttack()写的那样。

基于Function Pointers实现的Strategy模式
我们可以让不同角色保存一个函数指针,该函数指针执行特殊攻击操作。但是这样存在一个问题,就是函数指针指向的函数可能需要访问对象的私有元素,这样可能就需要采用friend关键字来为函数特殊访问权限。

基于tr1::function实现的Strategy模式
与上面的Function Pointers相似,只不过std::function具有更好的封装,可以保存成员函数。


条款36:绝不重新定义继承而来的non-virtual函数

父类的non-virtual函数体现了某种不变性,一旦子类改变定义,便是对is-a关系的违反。如果希望子类对某些函数表现出特异性,这时就需要virtual关键字,virtual函数通过虚函数表的机制,向子类提供了一种保证:你可以重新定义我,我将仍然维护is-a关系。


条款37:绝不重新定义继承而来的缺省参数值

virtual函数是动态绑定的,而缺省参数值是静态绑定
这意味着你在调用一个定义于derived class内中的virtual函数的同时,使用的是base class为它指定的缺省参数指


条款38:通过复合塑模出has-a或“根据某物实现出”

复合是类型之间的一种关系,当某种类型的对象内含它种类型的对象,便是这种关系。

程序中的对象其实相当于你所塑造的世界中的某些事物,例如人、汽车、视频画面等等。这样的对象属于应用域部分。其他对象则纯粹是实现细节上的人工制品,像是缓冲区、互斥器、查找树等等。这些对象属于实现域部分。复合发生于应用域内对象之间,表现出has-a关系,当发生于实现域内则是表现is-implemented-in-terms-of关系。


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

如果classes之间的继承关系是private,编译器不会自动将一个derived class对象转换为一个base class对象。

class Person{
...
};
class Student:private Person{
...
};
void eat(const Person&);
void study(const Student&);

Person p;
Student s;
eat(p);//正确
eat(s);//错误,无法转化

private继承意味着implemented-in-terms-of。如果让D以private形式继承B,用意是采用B中已经备妥的某些特性,不是因为B和D有任何观念上的关系
和复合不同,private继承可以造成empty base最优化。这有利于空间。一般情况下依然用复合实现implemented-in-terms-of


条款40:明智而审慎地使用多重继承

多重继承比单一继承复杂。可能导致歧义性,以及对virtual继承的需要。

virtual继承会增加大小、速度、初始化复杂度等等成本。如果virtual base classes不带任何数据,将是最有使用价值的情况。

多重继承有正当用途。其中一个情节涉及”public继承某个Interface class”和“private继承某个协助实现的class“两相组合

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值