effective c++学习笔记四

 
effective c++学习笔记
 
 
继承和面向对象设计
virtual函数意味着“接口必须被继承”
non-virtual函数意味着“接口和实现都必须被继承”
 
 
 
条款32:确定你的public继承塑模出is-a关系
 
class D以public继承class B:每一个类型的D的对象同时也是一个类型为B的对象,反之不成立。
B可以派上用场的地方 D都可以派上用场 反之不成立。
 
 
 
 
条款33:避免遮掩继承而来的名称
 
c++的名称遮掩规则所做的唯一事情就是:遮掩名称 至于名称是否应和相同或者不同的类型,并不重要。
继承类的作用域被嵌套在基类的作用域内。
 
如果继承类定义了与基类重名的函数,那么基类的函数将被屏蔽,不再被继承,即使参数不同也是如此,而且不论函数是virtual还是non-virtual都适用。
基本理由:防止你在程序库或者应用框架内建立新的继承类时附带疏远从基类继承重载函数。
如果你使用public继承而不继承那些重载函数,那么就违反了is-a的关系。
缺省遮掩行为可以用using声明推翻:
public:
using Base::func();
或者用转交函数 virtual func(){Base::func();}
 
 
 
 
条款34:区分接口继承和实现继承
 
public继承概念可以分为函数接口继承和函数实现继承。
纯虚函数的目的就是为了让继承类只继承函数接口
普通虚函数的目的是让继承类可以继承该函数的接口和缺省实现
纯虚函数必须在继承类中重新声明,但是他也可以拥有自己的实现。但只有明确提出申请时才可以被调用。
class shape{
public:
virtual void draw() const = 0;}
shape *ps = new rectangle();
ps->shape::draw();
 
如果成员函数是non-virtual函数,意味着他并不打算在继承类中有不同的行为。不变形远比特异性更强烈。
non-virtual函数是为了令继承类继承函数的接口以及一份强制性的实现。
 
 
 
 
条款35:考虑virtual函数以外的其他选择
 
1.借助non-virtual interface手法实现template method模式
class base
{
public:
    int func(){   //wrapper
    ......//do sth    调用之前设定好场景,互斥器日志项等
    int retVal = dofunc();
    ......//do sth   调用之后的清理工作
    return retVal;
private:
    virtual int dofunc(){...}//继承类可以重新定义他
}
 
2.使用函数指针实现strategy模式
 
3.使用tr1::function完成strategy模式
这样的对象可以持有任何可调用之物,函数指针、函数对象、或者成员函数指针,只要其签名式兼容于需求端。
 
4.古典strategy模式
将继承体系内的virtual函数替换为另一个继承体系内的virtual函数。
 
 
 
 
条款36:绝不重新定义继承而来的non-virtual函数
 
如果重新定义就会形成静态绑定
B *pb = &x;
pb->mf();
D *pd = &x;
pd->mf();
两者的调用结果将会不一样。
 
条款7中的“多态性基类的析构函数应该是virtual”是本条款的 一个特例,因为如果析构函数不是virtual,那么在继承类中就不应该重新定义他。
 
 
 
 
条款37:绝不重新定义继承而来的缺省参数值
 
因为已经明确不应该重新定义non-virtual函数,那么只需要讨论virtual函数。
virtual函数为动态绑定,而缺省参数值为静态绑定。
静态类型:程序中被声明时所采用的类型。
动态类型是指目前所指对象的类型。
如果在继承类中改变了缺省值,那么会导致用基类指针指向派生类对象后再调用该函数时值的来源不明。
在virtual函数中使用默认值会引起不必要的麻烦,可以用non-virtual interface手法代替之。
 
 
 
 
条款38:通过复合塑模出has-a或者“根据某物实现出”(is-implemented-in-terms-of)
 
复合是类型中的一种关系,当某种类型的对象内含其他类型的对象,就是这种关系。
 
程序中的对象其实相当于显示世界中的某些事物 这样的对象属于应用域部分,表现出has-a关系;
其他对象则是纯粹的实现细节上的人工制品,比如缓冲区、互斥锁等,相当于实现域的部分,表现为is-implemented-in-terms-of关系
 
 
 
 
条款39:明智而谨慎地使用private继承
 
如果继承关系是private,那么编译器不会自动将一个继承类对象转换成一个基类对象。
由private关系继承的所有成员,在继承类中都会变成private属性。
 
private继承意味着is-implemented-in-terms-of
用意是为了利用B内已经备妥的某些特性,不是因为两者之间有任何观念上的关系。
private继承意味着实现部分被继承,而接口部分被略去,在设计层面没有意义,只在实现上有意义。
尽可能使用复合,必要时才使用private继承。
 
private继承主要用于“当一个意欲成为继承类的类想访问一个意欲成为基类的protect部分,或者是想重新定义一个或者多个virtual函数”,但这时候两者之间的关系其实是is-implemented-in-terms-of,private继承可以成为合适的策略,但是也可以选择“public继承+复合”。
 
而有一种特殊情况,可以选择private继承,就是处理的class不带任何数据 。
c++规定一个独立的对象(非附属)必须有非零大小,面对大小为0的独立对象,默认安插一个char到空对象内。
class empty{};
class D{
empty e;
int x;
}
sizeof(D)>sizeof(int)
 
但如果是继承就不会发生这样的情况
class empty{};
class D:private empty{
int x;
}
sizeof(D)=sizeof(int)
所谓的空白基类最优化(empty base optimization)EBO
 
 
 
 
条款40:明智而谨慎地使用多重继承
 
当使用多继承时,可能会从多个基类继承相同的名称,可能会造成更多的歧义。
c++解析重载函数调用时的规则:在看到是否有个函数可取用之前,先确定这个函数对此调用时最佳匹配。
 
菱形多重继承,是否打算让基类内的成员变量经由每一条路径被复制?
如果不是,需要将带有此数据的class变成virtual base class,必须将所有直接继承自他的class采用virtual继承。
 
但是你必须为virtual继承付出代价:
1)若派生自virtual bases而需要初始化,必须认知其virtual base而无论那些base有多远
2)当一个新的继承类加入后,它必须承担其virtual base的初始化责任。
=>非必要就不要用virtual继承,如果要使用,则尽量不要在其中放置数据(类似于接口)。
 
 
posted @ 2012-09-02 11:47  w0w0  阅读(188)  评论(0编辑  收藏  举报