正是虚函数改写,使得通过基类接口调用派生类函数成为可能:
class Base {
public:
virtual void doWork(); // 基类中的虚函数
];
class Derived : public Base {
public:
virtual void doWork(); // 改写了Base::doWork
};
std::unique_ptr<Base> upb = std::makr_unique<Derived>(); // 创建基类指针,指向派生类对象
upb->doWork(); // 派生类函数被调用
如果doWork
这个改写动作真的要发生,要满足以下几个条件
- 基类中的函数必须是虚函数
- 基类和派生类中的函数名字必须完全相同(除开析构函数)
- 基类和派生类的函数形参类型完全相同
- 基类和派生类的函数常量性必须完全相同
- 基类和派生类的函数返回值和异常规格必须兼容
- 基类和派生类的函数引用饰词,引用饰词为限制成员函数仅用于左值或右值;带引用饰词的成员函数,不必是虚函数
class Base {
public:
virtual void doWork() &; // 仅在*this是左值时调用
virtual void doWork() &&; // 仅在*this是右值时调用
];
Widget makeWidget(); // 工厂函数(返回右值)
Widget w; // 工厂函数(返回左值)
w.doWork(); // 左值调用Widget::doWork()
makeWidget().doWork(); // 右值调用Widget::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 void mf1() ;
virtual void mf2(unsigned int x);
virtual void mf3() &&;
virtual void mf4() const;
};
上述Derived
显然不可能通过编译,因为一旦写成这样,编译器就会吹毛求疵检视所有与改写的相关问题
// 后续,待完善