C++11 之 override

1  公有继承

  公有继承包含两有:一是 “接口”
(interface),二凡 “实现”
(implementation)

  如下 Shape 类中,三单成员函数,代表三栽持续方式:

class Shape {
public:
    virtual void Draw() const = 0;    // 1) 纯虚函数
    virtual void Error(const std::string& msg);  // 2) 普通虚函数
    int ObjectID() const;  // 3) 非虚函数
};

class Rectangle: public Shape { ... };
class Ellipse: public Shape { ... };

1.1  纯虚函数 (pure virtual)

  纯虚函数,继承的是基类成员函数的 接口,且一旦当派生类中再次写该函数的 实现**


Shape *ps1 = new Rectangle;
ps1->Draw(); // calls Rectangle::Draw

Shape *ps2 = new Ellipse;
ps2->Draw(); // calls Ellipse::Draw

  若想调用基类的 Draw(),须加上 类作用域操作符 ::

ps1->Shape::Draw(); // calls Shape::draw

1.2  普通虚函数

  普通虚函数,对诺以基类中定义一个差省之兑现 (default
implementation),表示继续的是基类成员函数的接口和欠省之兑现,由派生类自行选择是否再次写该函数。

  实际上,允许普通虚函数同时继续接口及缺省贯彻是危在旦夕的 如下,
ModelA 和 ModelB 是 Airplane 的点滴种植飞行器项目,且两岸的飞方式完全相同

class Airplane {
public:
    virtual void Fly(const Airport& destination);
};
class ModelA: public Airplane { ... };
class ModelB: public Airplane { ... };

  这是名列前茅的面向对象设计,两个像样共享一个风味 — Fly,则 Fly 可于基类中落实,并由个别独差使生类继承的

  现增加一个初的机型号 ModelC,其航空方式以及 ModelA,ModelB
并不相同,假如不小心忘了于 ModelC
中重复写新的 Fly 函数

class ModelC: public Airplane {
    ... // no fly function is declared
};

  则调用 ModelC 中的 fly 函数,就是调用 Airplane::Fly,但是 ModelC 的飞行方式同缺少省之并不相同

Airplane *pa = new ModelC;
pa->Fly(Qingdao); // calls Airplane::fly!

  这就是前方所说的,普通虚函数同时继续接口和缺省实现是危急的极是基类中实现缺省行为
(behavior),但偏偏发于派生类要求时才提供该缺省作为

1.2.1  方法一 

  一种植办法是 纯虚函数 +
缺省贯彻,因为是纯虚函数,所以只有接口被连续,其缺少省之兑现非会见给接续。派生类设想采取该缺省的实现,必须显式的调用

class Airplane {
public:
    virtual void Fly(const Airport& destination) = 0;
};

void Airplane::Fly(const Airport& destination)
{ 
    // a pure virtual function default code for flying an airplane to the given destination
}

class ModelA: public Airplane {
public:
    virtual void Fly(const Airport& destination) { Airplane::Fly(destination); }
};

  这样于派生类 ModelC 中,即使一不小心忘记重写 Fly 函数,也非会见调用
Airplane 的缺省贯彻

class ModelC: public Airplane {
public:
    virtual void Fly(const Airport& destination);
};

void ModelC::Fly(const Airport& destination)
{
    // code for flying a ModelC airplane to the given destination
}

1.2.2  方法二 

  可以见到,上面问题之关键就在于,一不小心在派生类 ModelC 中忘记重写 fly
函数,C++11 中使要字
override,可以免这样的“一不小心”

1.3  非虚函数

  非虚成员函数没有 virtual 关键字,表示派生类不但继续了接口,而且持续了一个强制实现 (mandatory
implementation)

  既然继承了一个强制的实现,则在派生类中,无须再定义 (redefine)
继承自基类的分子函数,如下:

  使用指针调用 ObjectID
函数,则都是调用的
Shape::ObjectID()

Rectangel rc; // rc is an object of type Rectangle

Shape *pB = &rc; // get pointer to rc
pB->ObjectID(); // call ObjectID() through pointer

Rectangle *pD = &rc; // get pointer to rc
pD->ObjectID(); // call ObjectID() through pointer

  如果在派生类中再度定义了延续自基类的积极分子函数 ObjectID 呢?

class Rectangel : public Shape {
public:
    int ObjectID() const; // hides Shape::ObjectID
};

pB->ObjectID(); // calls Shape::ObjectID()
pD->ObjectID(); // calls Rectagle::ObjectID()

  这,派生类中再定义的成员函数会 “隐藏” (hide) 继承自基类的积极分子函数

  这是以非虚函数是 “静态绑定” 的,pB 被声称的是 Shape*
类型的指针,则通过 pB 调用的非虚函数都是基类中之,既设 pB 指向的凡派生类

  与“静态绑定”相对的是虚函数的“动态绑定”,即无 pB 被声称也
Shape* 还是 Rectangle* 类型,其调用的虚函数取决于 pB 实际对的靶子类型

 

 2  重写 (override)

  于 1.2.2 中关系 override 关键字,可以免派生类吃忘记重写虚函数的缪

  下面坐更写虚函数时,容易犯的季个谬误也条例,详细阐述的

class Base {
public:
    virtual void mf1() const;
    virtual void mf2(int x);
    virtual void mf3() &;
    void mf4() const;    // is not declared virtual in Base
};

class Derived: public Base {
public:
    virtual void mf1();        // declared const in Base, but not in Derived.
    virtual void mf2(unsigned int x);    // takes an int in Base, but an unsigned int in Derived
    virtual void mf3() &&;    // is lvalue-qualified in Base, but rvalue-qualified in Derived.
    void mf4() const;        
};

  以派生类中,重写 (override) 继承自基类成员函数的兑现 (implementation)
时,要满足如下条件:

  一虚:基类中,成员函数声明也虚拟的 (virtual)

  二容:基类和派生类吃,成员函数的回来路及深规格 (exception specification)
必须配合

  四同:基类和派生类中,成员函数叫、形参类型、常量属性 (constness) 和 引用限定符 (reference qualifier)
必须完全相同

 
如此多之限制条件,导致了虚函数更写如上述代码,极容易因一个免小心要失误

  C++11 中的 override 关键字,好显式的在派生类中扬言,哪些成员函数需要给还写,如果没有让再度写,则编译器会报错。

class Derived: public Base {
public:
    virtual void mf1() override;
    virtual void mf2(unsigned int x) override;
    virtual void mf3() && override;
    virtual void mf4() const override;
};

  如此这般,即使不小心漏写了虚函数还写的某苛刻条件,也得以经编译器的报错,快速改正错误

class Derived: public Base {
public:
    virtual void mf1() const override;  // adding "virtual" is OK, but not necessary
    virtual void mf2(int x) override;
    void mf3() & override;
    void mf4() const override; 
}; 

 

小结:

1)  国有继承

  纯虚函数      => 继承的是:接口 (interface)

  普通虚函数   => 继承的是:接口 + 缺省兑现 (default
implementation)

  非虚成员函数 =>继承的是:接口 + 强制实现 (mandatory
implementation)

2)  不要再次定义一个继承自基类的非虚函数 (never redefine an
inherited non-virtual function)

3)  在声明需要更写的函数后,加要字 override

 

参考资料:

 <Effective C++_3rd> item 34, item 36

 <Effective Modern C++> item 12