|
重点
- 将被重写的函数加上修饰符override.
- 成员函数引用限定符可以区别对待左值和右值对象 (*this).
在C++中, 我们可以通过在基类中声明函数为virtual并在派生类中覆盖重写它们来实现多态性. 但是, 很容易忽略一些细节而误入歧途. 编译器甚至可能不会警告我们写错了. 让我们来看一些没能成功被覆盖的错误重写例子:
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() &&;
virtual void mf4() const;
}
上面在 Derived 类中声明的 4 个函数都没有成功覆盖它们在 Base 类中的对应函数. 为什么?
- mf1 在 Base 中被声明为 const,但在 Derived 中没有
- mf2 在 Base 中入参是 int, 但在 Derived 中是 unsigned int
- mf3 在 Base 中是左值限定的, 但在 Derived 中是右值限定的
- mf4 未在 Base 中声明为virtual
为了在派生类中达成覆盖重写, 以下条件需要被满足:
- 基类函数必须是虚函数virtual
- 基函数名和派生函数名必须相同 (析构函数除外)
- 基函数和派生函数的参数类型必须相同
- 基函数和派生函数的const修饰符必须相同
- 基函数和派生函数的返回类型和异常规范必须兼容
- 基函数和派生函数的引用限定符必须相同
从 C++11 开始, override 关键提供了明确告诉编译器我们想要覆盖重写函数的能力. 如果这种覆盖不成功, 即如果我们的代码忽略了上面提到的某些要求, 编译器将明确拒绝并告诉我们问题出在哪里.
因此, 这里我们倡议的准则是, 每当打算覆盖重写基类中的虚函数时, 用 override 关键字并让编译器帮我们检查重写是否奏效.
关于引用限定符, 我们将额外讲解一点知识. 这是为了区分对象(*this)是左值对象还是右值对象. 这不是很常见的用法, 但有时确实会出现. 例如:
class Widget {
public:
using DataType = std::vector<double>;
...
DataType& data() & { // for lvalue Widgets, return lvalue
return values;
}
DataType&& data() && { // for rvalue Widgets, return rvalue
return std::move(values);
}
...
private:
DataType values;
};
auto vals1 = w.data(); // call lvalue overload, copy-construct vals1
auto vals2 = makeWidget().data();// call rvalue overload, move-construct vals2
<hr/>欢迎大家指出不足或错误, 进行提问和讨论. |
|