条款12:为意在重写的函数添加override声明
1 关于override声明
- 重写(overriding)听起来很像重载(overloading),但实际上完全没有关系,通过基类接口调用派生类函数的功能是由虚函数重写实现的:
class Base {
public:virtual void doWork(); …
};class Derived : public Base {
public:virtual void doWork(); // 重写 Base::doWork,在这里 virtual 可以不写…
}; std::unique_ptr<Base> upb = std::make_unique<Derived>();
…
upb->doWork(); // 通过基类的指针调用 doWork,将使用派生类版本的函数
// 小小的错误可以产生大大的差异。包含重写错误的代码通常是有效的,但它的含义不是想要的
要实现重写,必须满足以下几个条件。
a. 基类函数必须为虚函数。
b. 基类函数名和派生类函数名必须相同(除了析构函数)。
c. 基类函数和派生类函数的参数类型必须相同。
d. 基类函数和派生类函数的常量属性必须相同。
e. 基类函数和派生类函数的返回类型和异常规范必须兼容。
f. 基类函数和派生类函数的引用限定符必须相同。(C++11)
- 引用限定符可以将成员函数的使用限制为只左值或只右值。
class Widget {
public:…void doWork() &; // 只有当*this是左值时,这个版本的doWork才适用void doWork() && ; // 只有当*this是右值时,这个版本的doWork才适用
};
…
Widget makeWidget(); // 工厂函数(返回右值)
Widget w; // 普通对象(一个左值)
…
w.doWork(); // 调用左值版本的 Widget::doWork (i.e., Widget::doWork &)
makeWidget().doWork(); // 调用右值版本的 Widget::doWork (i.e., Widget::doWork &&)
class Widget {
public:using DataType = std::vector<double>; … DataType& data() { return values; }…
private:DataType values;
};
Widget w;
…
auto vals1 = w.data(); // 拷贝 w.values 到 vals1
Widget makeWidget();
auto vals2 = makeWidget().data(); // 拷贝 右值的 values 到 vals2
需要一种方法,指定在右值对象上调用data函数时,结果也应该是右值。
使用引用限定符重载左值和右值,可以实现:
class Widget {
public:using DataType = std::vector<double>;…DataType& data() & // 用于左值 Widget对象,{ return values; } // 返回左值DataType data() && // 用于右值 Widget对象,{ return std::move(values); } // 返回右值…
private:DataType values;
};
auto vals1 = w.data(); // 调用Widget::data的左值重写版本,拷贝构造vals1
auto vals2 = makeWidget().data(); // 调用Widget::data的右值重写版本,移动构造vals2
- 为什么这些派生类的函数不重写基类的同名函数?
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();virtual void mf2(unsigned int x);virtual void mf3() && ;void mf4() const;
};
//mf1在Base中声明为const,但在Derived中没有。
//mf2在Base中接受一个int类型参数,但在Derived中接受一个unsigned int类型参数。
//mf3在Base中是左值限定的,但在Derived中是右值限定的。
//在Base中没有将f4声明为virtual。
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;
};
override关键字,只有出现在成员函数声明的末尾时才具有保留意义。
class Warning { // 老版本C++的遗留代码
public:…void override(); // 在 C++98 和 C++11 中都同样有效…
};
2 要点速记
- 将重写函数声明为override。
- 成员函数引用限定符使得可以区别对待左值和右值对象(*this)。