Effective C++ 笔记 第六部分 继承与面对对象设计

32.确定你的public继承塑模出is-a关系(Make sure public inheritance models “is-a”)


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


33.避免遮掩继承而来的名称(Avoid hiding inherited names.)


derived classes内的名称会遮掩base classes内的名称。在public继承下从来没有人希望如此。
为了让被遮掩的名称再见天日,可使用using声明式或转交函数。

编译器的名称查找法则:首先查找local作用域,在查找外围作用域,然后是namespace,最后是global作用域。derived class作用域优先于base class被查找。
由于编译器的名称掩盖规则,在derived class的同名函数会覆盖掉base class的对应函数,即使参数类型不同。对于参数类型不同的可以使用using来声明。

class base{
public:
    void func(int a);
};

class derived: public base{
public:
    using base::func;//让base class内名为func的所有东西在derived作用域内都可见
    void func();
};

int main(int argc, const char * argv[]) {
    derived d;
    d.func(5);//若无using语句则出错;
    return NULL;
}

34.区分接口继承和实现继承(Differentiate between inheritance of interface and inheritance of implementation)


接口继承和实现继承不同。在public继承之下,derived classes总是继承base class的接口。
pure virtual函数只具体指定接口继承。
简朴的(非纯)impure virtual函数具体指定接口继承及缺省实现继承。
non-virtual函数具体指定接口继承以及强制性实现继承。

声明一个non-virtual函数的目的是为了令derived class继承函数的接口及一份强制性实现。non-virtual函数代表的意义是不变性凌驾特异性,绝不应该在derived class中被重新定义。

声明一个pure virtual函数的目的是为了让derived classes只继承函数接口。

声明非纯虚函数impure virtual函数的目的是让derived classes继承该函数的接口和缺省实现。即某个derived class不想对base class的接口做改变,可使用base class提供的缺省版本。

然而impure virtual函数并不安全,因为其提供的缺省版本并不能适合所有derived class,而impure virtual函数并不会强制derived class对其重写。所有我们将缺省版本以non-virtual方式实现,把接口定义为virtual函数。

class base{
public:
    virtual void fun() = 0;//接口
protected:
    void defaultFun();//fun的缺省行为
};

若想使用缺省行为,在virtual的实现版本中调用缺省行为的函数即可。
或者在base class的pure virtual函数中提供缺省行为,在derived class中若使用缺省行为则调用。但会丧失让两个函数有不同保护级别的机会

class base{
public:
    virtual void fun() = 0;//接口
};

void base::fun(){
    //缺省行为
}

class derived: public base{
public:
    virtual void fun(){
        base::fun();//使用缺省行为
    }
};

35.考虑virtual函数以外的其他选择(Consider alternatives to virtual functions.)


virtual函数的替代方案包括NVI手法及Strategy(策略)设计模式的多种形式。NVI手法自身是一个特殊形式的Template Method设计模式。
将机能从成员函数移到class外部函数,带来的一个缺点是,非成员函数无法访问class的non-public成员。
tr1::function对象的行为就像一般函数指针。这样的对象可接纳”与给定之目标签名式兼容”的所有可调用物。

NVI手法:non-virtual interface,另客户通过public non-virtual成员函数间接调用private virtual函数。这个non-virtual成员函数成为virtual函数的外覆器。

class GameCharacter{
public:
//public non-virtual成员函数,virtual函数的外覆器
    int healthValue() const{
        //do something
        int retVal = doHealthValue();//调用private virtual函数
        //do something
        return retVal;
    }
private:
//private virtual函数
    virtual int doHealthValue() const{
        计算healthValue并返回
    }
};

在调用private virtual函数之前可做些事前工作,之后也可以做些时候工作,例如使用互斥锁。若直接提供virtual函数则需要由客户做这些事。
virtual函数不一定是private的,可以是protected的。
在derived class中重新定义virtual函数即可。

Strategy模式参考


36.绝不重新定义继承而来的non-virtual函数(Never redefine an inherited non-virtual function.)


绝不要重新定义继承而来的non-virtual函数。


37.绝不重新定义继承而来的缺省参数值(Never redefine a function’s inherited default parameter value.)


绝不重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定,而virtual函数——你唯一应该覆写的东西——都是动态绑定的。

静态绑定又称前期绑定,early binding。
静态类型就是他在程序中被声明时所采用的类型。
动态类型指目前所指对象的类型。

class Shape{
public:
    enum ShapeColor{ Red, Green, Blue };
    virtual void draw(ShapeColor color = Red)const = 0;
};

class Rectangle: public Shape{
public:
    virtual void draw(ShapeColor color = Green) const;
};

class Circle: public Shape{
public:
    virtual void draw(ShapeColor color) const;
};

int main(int argc, const char * argv[]) {
    Shape* ps;                  //ps静态类型为Shape*
    Shape* pc = new Circle;     //pc静态类型为Shape*
    Shape* pr = new Rectangle;  //pr静态类型为Shape*

    ps = pc;    //ps的动态类型为Circle*
    ps = pr;    //ps的动态类型为Rectangle*

    pr->draw(); //调用Rectangle::draw(Shape::Red)
                //因为pr的静态类型是Shape,所有此调用的缺省值来自Shape class

    return NULL;
}

C++这样做的原因是避免在运行期决定适当的参数缺省值所带来的效率问题。


38.通过复合塑模出has-a或”根据某物实现出”(Model “has-a” or “is-implemented-in-terms-of” through composition)


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

复合是类型之间的一种关系,当某种类型的对象内包含它种类型的对象,便是这种关系。
应用域:程序中对象相当于你所塑造的时间中的某些事物,例如人、汽车。
实现域:实现细节上的人工制品,如缓冲区,互斥锁,二叉查找树。

has-a:在复合模式中一个对象拥有另一个对象,是has-a,例如人拥有名字。
implemented-in-terms-of:指实现一个对象需要依赖于其他对象(如链表)。


39.明智而审慎的使用private继承(Use private inheritance judiciously.)


Private继承意味is-implemented-in-terms of.他通常比复合的级别低。但是当derived class需要访问protected base class的成员,或需要重新定义继承而来的virtual函数时,这么设计是合理的。
和复合不同,private继承可以造成empty base最优化。这对致力于”对象尺寸最小化”的程序库开发者而言,可能很重要。

若继承关系为private,那么编译器不会自动将一个derived class对象转换为一个base class对象。有private base class继承而来的所有成员,在derived class中都会变成private属性。
private继承意味着implemented-in-terms-of(根据某物实现出),如果让class D以private形式继承class B,意味着你希望采用B内已经备妥的某些特性,不是因为B和D存在任何观念是的关系。

无论什么时候,只要可以,你还是应该选择复合。除了当derived class需要访问protected base class的成员,或需要重新定义继承而来的virtual函数时,需要private继承。


40.明智而审慎的使用多重继承(Use multiple inheritance judiciously.)


多重继承比单一继承复制。它可能导致新的歧义性,以及对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、付费专栏及课程。

余额充值