Item 12: Declare overriding functions override.
Effective Modern C++ Item 12 的学习和解读。
面向对象的 C++ 主要特性是类、继承、虚函数,而这些特性的基础是继承类中重写基类的虚函数。重写(overriding)和重载(overloading)看着比较相似,但其实完全不同。重写的一个例子:
class Base {
public:
virtual void doWork(); // base class virtual function
...
};
class Derived: public Base {
public:
virtual void doWork(); // overrides Base::doWork ("virtual" is optional here)
...
};
std::unique_ptr<Base> upb = std::make_unique<Derived>();
...
upb->doWork(); // call doWork through base class ptr; derived class function is invoked
重写需要满足的条件:
- 基类的重写函数必须是虚函数。
- 基类和继承类的重写函数的函数名必须相同。
- 基类和继承类的重写函数的参数类型必须相同。
- 基类和继承类的重写函数的常量属性必须相同。
- 基类和继承类的重写的函数的返回值类型和异常规格说明要兼容。
除了 C++98 的这些限制条件,C++11 还增加了一条:
- 基类和继承类的重写函数的引用修饰符必须相同。
引用修饰符包括左值和右值两种,左值引用成员函数可以被 *this 为左值调用,右值引用成员函数可以被 *this 为右值调用。看一个例子:
#include <iostream>
class Widget {
public:
void doWork() & { // this version of doWork applies only when *this is an lvalue
std::cout << "doWork() &" << std::endl;
}
void doWork() && { // this version of doWork applies only when *this is an rvalue
std::cout << "doWork() &&" << std::endl;
}
};
Widget makeWidget() { // factory function (returns rvalue)
return Widget();
}
int main() {
Widget w;
w.doWork();
makeWidget().doWork();
}
// 打印信息
doWork() &
doWork() &&
这里,w 是个左值,所以调用的是 doWork() & 函数,而 makeWidget() 返回的是一个右值,所以调用的是 doWork() && 函数。
重写的条件是非常严格的,细小的差错将会导致完全不同的结果。看个例子:
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
void mf4() const;
};
class Derived: public Base {
public:
virtual void mf1(); // 缺少 const
virtual void mf2(unsigned int x); // 参数类型不同
virtual void mf3() &&; // 引用修饰符不同
void mf4() const; // 基类没有加 virtual 修饰
};
以上代码,有的编译器会给出警告,有的编译器给的警告不全,还有的编译器不会给警告。所以保险的做法是自己将重写函数标记为 override,这样的话,不符合重写条件的话,编译器一定会报错。
所以,当你在继承类中要重写函数的话,加上 override 吧!