6.继承与面向对象

Inheritance and Object-Oriented Design #

32.Make sure public inheritance models “is-a” #

public inheritance(公开继承)意味"is-a"(是一种)的关系

33.Avoid hideing inherited names #

derived classed 内的名称会遮掩base classed内的名称

class Base{
private:
    int x;
public:
    virtual void mf1() = 0;
    virtual void mf1(int);
    void mf3();
    void mf3(double);
}
class Derived : public Base{
public:
    virtual void mf1();
    void mf3();
    void mf4();
}

Derived d;
int x;
...
d.mf1();
d.mf1(x);//错误
d.mf2();
d.mf3();
d.mf3(x);//错误,因为Derived::mf3遮掩了一个名为Base::mf3但是类型不同的Base函数

下面使用using解决上面的问题

class Base{
private:
    int x;
public:
    virtual void mf1() = 0;
    virtual void mf1(int);
    void mf3();
    void mf3(double);
}
class Derived : public Base{
public:
    using Base::mf1;//让Base class内名为mf1和mf3的所有东西
    using Base::mf3;//在Derived 作用域内都可以(并且public)
    virtual void mf1();
    void mf3();
    void mf4();
}

Derived d;
int x;
...
d.mf1();
d.mf1(x);//正常
d.mf2();
d.mf3();
d.mf3(x);//正常

using声明式会令继承而来的某给定名称之所有同名函数在derived class中都可见

class Base{
private:
    int x;
public:
    virtual void mf1() = 0;
    virtual void mf1(int);
}
//Derived唯一想继承的mf1是那个无参数版本,通过转交函数实现
class Derived : privata Base{
public:
    virtual void mf1()//转交函数(forwarding function)
    {
        Base::mf1();
    }
    void mf3();
    void mf4();
}

Derived d;
int x;
...
d.mf1();
d.mf1(x);//正常

34.Differentiate between inheritance of interface and inheritance of implementation #

  • 声明pure virtual函数的目的是为了让derived classed 只继承函数接口

  • 声明impure virtual函数的目的是为了让derived classed继承该函数的接口和缺省实现

  • 声明non-virtual函数的目的是为了让derived classed继承函数的接口及一份强制性实现

    class Airplane{
    public:
        virtual void fly(const Airport& destination) = 0;
        ...
     protected:
        void defaultFly(const Airport& destination);
    };
    
    void Airplane::defaultFly(const Airport& destination)
    {
        //缺省行为
    }
    
    class ModelA: public Airplane{
    public:
        virtual void fly(const Airport& destination)
        {
            defaultFly(destination);
        }
        ...
    };
    
    class ModelB: public Airplane{
    public:
        virtual void fly(const Airport& destination)
        {
            defaultFly(destination);
        }
        ...
    };
    

35.Consider alternatives to virtual functions #

  • 藉由Non-Virtual Interface手法实现Template Method模式

    class GameCharacter{
    public:
        int healthValue() const
        {
            ...
            int retVal = doHealthValue();
            ...
            return retVal;
        }
    private:
        virtual int doHealthValue() const	//derived classes可重新定义
        {
            ...
        }
    }
    

    令客户通过public non-virtual成员函数间接调用private virtual函数,称为non-virtual interface(NVI)手法,把这个non-virtual函数(healthValue)称为virtual函数的外覆器(wrapper)

  • 藉由Function Pointers实现Strategy模式

    class GameCharacter;
    int defaultHealthCalc(const GameCharacter& gc);
    class GameCharacter{
    public:
        typedef int (*HealthCalcFunc)(const GameCharacter&);
        explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc):healthFunc(hcf)
        {
        }
        int healthValue() const
        {
            return healthFunc(*this);
        }
    private:
        HealthCalcFunc healthFunc;
    }
    
    class EvilBadGuy: public GameCharacter{
    public:
        explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc):GameCharacter(hcf)
        {
           ...
        }
        ...
    };
    int loseHealthQuickly(const GameCharacter&);
    int loseHealthSlowly(const GameCharacter&);
    
    EvilBadGuy ebg1(loseHealthQuickly);
    EvilBadGuy ebg2(loseHealthSlowly);
    

    古典的strategy模式

    class GameCharacter;
    class HealthCalcFunc{
    public:
        virtual int calc(const GameCharacter& gc) const
        {
            ...
        }
    };
    HealthCalcFunc defaultHealthCalc;
    class GameCharacter{
    public:
        explict GameCharacter(HealthCalcFunc* phcf = &defaultHealthCalc):pHealthCalc(phcf)
        {}
        int healthValue() const 
        {
            return pHealthCalc->calc(*this);
        }
    private:
        HealthCalcFunc* pHealthCalc;
    };
    

36.Never redefine an inherited non-virtual function #

声明了virtual函数是动态绑定的(dynamically bound)

class B{
public:
    void mf();
    ...
};

class D:public B{
public:
    void mf();
    ...  
};

D x;
B* pB = &x;
pB->mf();//调用B::mf
D* pD = &x;
pD->mf();//调用D::mf
  • 绝对不要重新定义继承而来的non-virtual函数

37.Never redefine a function’s inherited default parameter value #

缺省参数值是静态绑定的。如果缺省参数值是动态绑定的,编译器就必须有某种办法在运行期为virtual函数决定适当的参数缺省值,为了运行期效率和编译器实现上的简易度,C++做了取舍。

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;
};

Shape* ps;
Shape* pc = new Circle;
Shape* pr = new Rectangle;

pr->draw();//调用Rectangle::draw(Shape::Red);

可以参考条款35中NVI(non-virtual interface)手法:令base class内的一个public non-virtual函数调用private virtual 函数,后者可被derived classes重新定义

class Shape{
public:
    enum ShapeColor{Red,Green,Blue};
    void draw(ShapeColor color = Red) //这样设计可以保证draw方法的默认参数总为Red
    {
        doDraw(color);
    }
private:
    virtual void doDraw(ShapeColor color) const = 0;
};

class Rectangle:public Shape{
public:
    ...
private:    
    virtual void doDraw(ShapeColor color) const;
};

38.Model “has-a” or “is-implemented-in-terms-of” through composition #

复合(composition)意味着有一个(has-a)或者根据某物实现出(is-implemented-in-terms-of)

在应用域(application domain),复合意味has-a(有一个),在实现域(implementation domain)复合意味is-implemented-in-terms-of(根据某物实现出)

39.Use private inheritance judiciously #

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

由private base class继承而来的所有成员,在derived class中都会变成private属性,纵使它们在base class 中原本是protected或public属性

复合和private继承都意味is-implemented-in-terms-of,无论什么时候,只要可以,你还是应该选择复合

40.Use multiple inheritance judiciously #

virtual继承会增加大小、速度、初始化复杂度等等成本

尽量不要使用多继承