为意在改写的函数添加override声明
class Base{
public:
virtual void doWork(); //基类中的虚函数
};
class Derived : public Base{
public:
virtual void doWork(); //改写了Base::dowork
};
std::unique_ptr<Base> upb = std::make_unique<Derived>(); //创建基类指针,指向派生类对象
upb->doWork(); //通过基类指针调用doWork
//结果是派生类函数被调用
如果要改写动作真的发生,以下要求必须满足:
- 基类中的函数必须是虚函数
- 基类和派生类中的函数名字必须完全相同(析构函数例外)
- 基类和派生类中的函数形参类型必须完全相同
- 基类和派生类中的函数返回值和异常规格必须兼容
-
基类和派生类中的函数引用饰词必须完全相同
- 函数引用饰词是为了限制成员函数仅用于左值或右值,带有引用饰词的成员函数,不必是虚函数。
class Widget{
public:
void doWork() &; //这个版本的doWork仅在*this是左值时调用
void doWork() &&; //这个版本的doWork仅在*this是右值时调用
};
Widget makeWidget(); //工厂函数
Widget w; //普通对象(左值)
w.doWork(); //以左值调用Widget::doWork
//即Widget::doWork &
makeWidget().doWork(); //以右值调用Widget::doWork
//即Widget::doWork &&
如果基类中的虚函数带有引用饰词,则派生类要对该函数进行改写,必须也带有完全相同的引用饰词。如果不然,那么这些声明了的函数在派生类中依然存在,只是它们不会改写基类中的任何函数。
C++11提供了一种方法来显式地标明派生类中的函数是为了改写基类版本,为其加上override
声明。
class Base{
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
virtual void mf4() const;
};
class Derived : public Base{
public:
virtual void mf1() const override;
virtual void mf2(int x) override;
virtual void mf3() & override;
virtual void mf4() const override;
};
C++11中的两个语境关键字:final
和override
,它们的特色是,语言保留这两个关键字,但仅在特定语境下保留。对于override
的情况下,它仅出现在成员函数声明的末尾时才有保留意义。这就意味着,如果你有一些遗留代码,其中已经用过override
这个名字的话,你不必为了升级到C++11而改名。
如果我们想撰写一个函数,仅接受传入左值实参,则会声明一个非const
左值引用形参:
void doSomething(Widget& w); //仅接受左值的Widget类型
如果我们想撰写一个函数,仅接受传入右值实参,
void doSomething(Widget&& w); //仅接受右值的Widget类型
成员函数引用饰词的作用就是针对发起成员函数调用的对象,即*this
,加一些区分度。这和成员函数声明末尾加一个const
的情形一摸一样:后者表明发起成员函数调用的对象,即*this
,应该是const
。
class Widget{
public:
using DataType = std::vector<double>;
DataType& data() & //对于左值类型,返回左值
{ return values; }
DataType data() &&
{ return std::move(values); } //对于右值Widget类型,返回右值
private:
DataType values;
};
左值引用类型的重载版本,返回的是左值引用(即一个左值);而右值引用类型的重载版本,则返回的是一个临时对象(即一个右值)。
//创建Widget类型对象的工厂函数
Widget makeWidget();
Widget w;
auto vals1 = w.data(); //调用Widget::data的左值重载版本
//vals1采用复制构造完成初始化
auto vals2 = makeWidget().data(); //调用Widget::data的右值重载版本
//vals2采用移动构造函数完成初始化
要点速记
- 为意在改写的而函数添加
override
声明 - 成员函数引用饰词使得对于左值和右值对象的处理能够区分开