《Effective C++》之继承与OO设计读书笔记

来源:互联网 发布:结构工程设计软件 编辑:程序博客网 时间:2024/05/16 12:31
================================================
条款35:确定你的public inheritance模塑出“is-a”的关系
1、public inheritance描述的是“is-a(是一种)”的关系(牢记这一条)。
2、每一个型别为Derived的对象同时也是一个型别为base的对象,反之不成立。
意思是Base比Derived表现出更一般化的概念,而比Derived比Base表现出更特殊化的概念。
    class Person {...};
    classs Student: private Person {...};
    void dance(const Person &p);
    void study(const Student &s);
    
    Person p; Student s;
    dance(p);     // 没问题,p是一个Person object
    dance(s);     // 没问题,s是一个Student object,而Student是一种Person。
    study(s);     // 没问题,s是一个Student object
    study(p);     // 错误,p不是一个Student。

=================================================
条款36:区分接口继承和实现继承
1、纯虚函数:动态绑定
——含有纯虚函数的类成为抽象类,不能实例化!
——必须被任何继承了他们的类重新声明定义;而且他们在基类中通常没有定义。
——声明一个纯虚函数是为了让derived classes只继承其接口。
2、普通虚拟函数:动态绑定
——是为了让derived classes继承其接口和default 行为。
至于何时使用纯虚函数,何时使用虚拟函数??没搞懂
3、非虚拟函数:静态绑定
——声明非虚拟函数是为了让derived classes继承其接口和实现。
结合37条,我们知道非虚拟函数一般是不会再derived classes中重新定义的。
补充:
静态绑定:对象声明时的类型,决定了运行时,对象调用的方法(base or derived)。
动态绑定:对象目前所代表的类型,决定了运行时,对象调用的方法。
class Shape
{
public:
    virtual void draw(int color=RED);
    void error(std::string errMsg); 
};

class Rect: public Shape
{
public:
    virtual void draw(int color=GREEN);
    void error(std::string errMsg);      // 注意:绝对不要这样!重定义非虚拟函数是非常致命的。此处仅作为说明动态静态绑定的例子。
};

Shape *ps;
Shape *pr = new Rect;      //  注意:这里可以编译通过,想想为什么。
pr->draw(RED);      // draw是虚函数,动态绑定,因此调用的是Rect::draw
pr->error("error message.\n");      // error是非虚拟函数,静态绑定,而pc声明类型为Shape,因此此处调用的是Shape::error。

==================================================
条款37:绝对不要重新定义继承而来的非虚拟函数
非虚拟函数是静态绑定的。
Rect r;
ps = &r;
r->error("error.\n");     // 此处调用Rect::error
ps->error("error.\n");     // 此处调用Shape::error
可见,凭借同一个对象r,同一个方法error,却调用了两种不同的方法,这也是软件设计中不希望出现的。
避免的方法是:软件设计阶段想明白,哪些方法使用纯虚函数,哪些用虚函数,哪些用非虚拟函数。

===================================================
条款38:绝对不要重新定义继承而来的缺省参数值
1、虚拟函数是动态绑定,而缺省形参是静态绑定的。
Rect r;
r->draw();      // 此时,color参数的值为Shape的RED,而非Rect的GREEN。
这会导致程序使用者的困惑,舍弃之。

===================================================
条款39:便面在集成体系中做向下转型(cast down)
1、只一句话:转型之于C++程序员,犹如苹果之于夏娃。
软件设计阶段多思考,尽量避免在后期实现中使用类型转换。

====================================================
条款40:通过layering技术来模塑出“has-a”或“is-implementated-in-terms-of”关系
1、layering技术,即房明老师课上的聚合技术,在类B中,含有A类的对象。
2、layering描述的是一种“有一个(has-a)”或“根据某物实现(is-implementated-in-terms-of)”的关系。
3、区分is-a和has-a:
is-a表示D是一种B,因此B满足的所有性质,D必须同时全部具有。
has-a表示D中有一个B,但是D并不要求具有B的全部特性,D只是实现中借鉴了B中的方法。
例如:用std::list实现set,必须使用has-a,因为list可以重复,而set不能,因此不能用is-a。

====================================================
条款41:区分template和inheritance
1、何时使用template:类型T不会影响class的行为。
2、何时使用inheritance:不同类型T,会有不同的行为。
例:类型为double和类型为float的加法,实现时完全相同,可以使用template。
男人买衣服的行为和女人买衣服的行为不同(购买特性完全不同),因此应该把男人和女人分别从person中public inheritance而来。


=====================================================
条款42:明智的运用private inheritance
1、private inheritance与layering一样,描述的是“根据某物实现(implemented-in-terms-of)”;
2、尽可能使用layering,必要时才使用private inheritance。何为必要?(这个不懂了)即当protectedmembers 和/或虚函数牵扯进来时。
3、如果classes之间是private inheritance,首要规则是:编译器通常不会自动将一个derived class object转换为一个base class object。
因此,下面的代码编译会出错:
    class Person {...};
    classs Student: private Person {...};
    void dance(const Person &p);
    
    Person p; Student s;
    dance(p);     // 没问题,p是一个Person object
    dance(p);     // Compile Error!Student不是一个Person!这种不合逻辑的设计,也表明了private inheritance不是一种isa的关系。
其他规则:由private base class继承而来的所有members,包括protected和public,在derived class中都变成private属性。

====================================================
条款43:明智的运用Multiple inheritance
太难了,没看。


xjs.xjtu@gmail.com
2012-09-24
原创粉丝点击