Effective C++ -- 继承与面向对象设计

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

public继承意味着is-a的关系(里氏代换原则),适用于基类的每一件事也适用于继承类。
矩形继承正方形问题:

  • 可实施与矩形的操作无法实施与正方形
  • 在编程领域,正方形是一种矩形是错误的
  • 在现实领域,正方形是一种矩形是正确的

33.避免遮盖继承而来的名称

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

Derived d;
int x;
d.mf1();    // 调用Derived::mf1
d.mf1(x);   // 错误,因为Derived::mf1遮掩了Base::mf1
d.mf2();    // 调用Base::mf2
d.mf3();    // 调用Derived::mf3
d.mf3(x);   // 错误,因为Derived::mf3遮掩了Base::mf3

继承类函数会遮掩基类的同名函数,即使参数不同。
目的是防止继承类从基类继承重载函数。

解决方法如下:

class Derived:public Base {
public:
    using Base::mf1;// 让Base class内mf1的所有东西在继承类作用域中都可见
    using Base::mf3;
    virtual void mf1();
    void mf3();
    void mf4();
};

为了让遮掩重见天日,可使用using声明式或者转交函数。

34.区分接口继承和实现继承

  1. 接口继承
  2. 接口和实现继承,同时覆写
  3. 接口和实现继承,不覆写
class Shape {
public:
    virtual void draw() const = 0;// 接口继承
    virtual void error(const string& msg);// 接口和实现继承,同时覆写
    int object() const;// 接口和实现继承,不覆写 
};

允许impure virtual函数同时指定函数声明和函数缺省行为,可能造成危险。
类A提供纯虚函数及实现fly(),期望继承类B使用fly实现,继承类C不使用fly实现。C类的实现者可能不清楚这一约定,造成类C也使用fly的实现。
解决方法:
1. 将fly改为纯虚函数,类A中实现protected defaultfly。类B实现fly中调用defaultfly。类C实现fly。
2. 将fly改为纯虚函数,类A中提供fly的定义,类B实现的fly中调用A::fly(),类C实现fly

结论:

  • 声明为纯虚函数期望继承类只继承接口。
  • 声明为虚函数目的是让继承类继承该函数的接口和缺省默认实现
  • 声明为非虚函数目的是让继承类继承函数的接口和一份强制的实现

35.考虑virtual函数以外的其他选择

  • NVI Non-Virtual Interface用来替代public virtual的方案
  • 由Function Pointers实现strategy模式
  • 古典策略模式
    遗留问题:
    tr1::function使用

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

non-virtual代表不变性凌驾与特异性。目的是令继承类继承函数的接口及一份强制实现
使用基类指针会调用到基类的版本

37.绝不重定义继承而来的缺省参数

非虚函数中不允许在继承类中重定义
在虚函数中更改继承类的缺省参数不会起作用
虚函数中更改缺省参数不起作用的原因,出于效率考虑,在执行期确定参数比编译器决定慢且更为复杂
虚函数中不要提供缺省参数,虚函数修改缺省参数后,继承类要跟着修改
使用NVI(non-virtual infterace方法,non-virtual方法中提供缺省参数,no-virtaul方法调用virtual方法)

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

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

复合的两层含义

  • has-a(应用域对象,例如汽车,视频画面)
  • is-implemented-in-terms-of(实现域,例如缓存器,互斥器)

39.明智而审慎地使用private继承

  • 如果是private继承,需要基类对象的场合,传入继承类错误,编译器不会自动将继承类对象转换为一个基类对象。
  • 由private继承而来的所有成员,在继承类中都会变为private
  • private继承意味着implemented-in-terms-of(根据某物实现)
  • 继承类和基类没有任何关系
  • private继承意味着实现被继承,接口部分被略去,private继承在设计层面没有意义
  • 和复合之间的取舍
    • 尽可能使用复合
    • 需要修改private继承基类中的虚函数时使用私有继承
    • 替代私有继承需要更改虚函数的另一种方法: 使用复合,复合类中定义一个public继承的嵌套类,复合类使用该嵌套了的对象。
class Widget {
private:
    class WidgetTimer:public Timer {
    publicvirtual void OnTick() const;
    };
    WidgetTimer timer;
}; 

以上方式也可以用来阻止继承类重新定义virtual函数

  • 使用private的另一个场合:EBO empty base optimization空白基类最优化(STL中使用 unary_function,binary_function)

你可能感兴趣的:(C++,面向对象,c++,设计)