在C++面向对象的世界里面主要是围绕着类、继承和虚函数,而在这个世界里面虚函数的实现就是靠派生类重写基类的虚函数,但是这很容易出错, 这是符合墨菲定律
的。因为重写
听起来像重载
,然而这两个其实是不相关的。
class Base {
public:
virtual void doWork();
.....
};
class Devired : public Base {
public:
virtual void doWork();
};
重写
需要满足一定的要求:
- 基类要重写的函数必须是虚函数
- 基类和派生类要重写的函数名称必须相同
- 基类和派生类的参数类型必须相同
- 基类和派生类要重写的函数的常量性必须相同
- 基类和派生类要重写的函数的返回值类型和异常规格说明要兼容
除了上面这些要求外,C++11中又添加了新的要求。
- 基类和派生类要重写的函数的引用标识符必须相同
引用标识符? 我相信很多人不是太清楚这个C++11中引入的新特性,它其实和成员函数的const修饰符差不多,只能用于修饰成员函数,引用标识符有两个,一个是 &
,用这个修饰的成员函数只允许被*this
是左值来调用。另外一个是&&
,值允许被*this
是右值来调用。
class Widget {
public:
void doWork() & {
std::cout << "I am & doWork" << std::endl;
}
void doWork() && {
std::cout << "I am && doWork" << std::endl;
}
};
Widget makeWidget() {
return Widget();
}
int main() {
Widget w;
w.doWork();
makeWidget().doWork();
}
上面的程序中,w
是个左值,所以调用的是输出I am & doWork
的doWork
函数,而makeWidget
返回的是一个右值的Widget
,所有调用的是被&&
修饰的doWork
函数。
上面这些都是重写
基类虚函数的要求,可想而知一点点小错误都会导致很大的差异,如果代码中进行了错误的重写
这是很难察觉的,毕竟错误的重写
从语法角度来说也是正确的,只是语意变了,下面是一些典型的重写
错误。
class Basse {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3()&;
void mf4() const;
};
class Dervired : public Base {
public:
virtual void mf1(); // 却少const
virtual void mf2(unsigned int x); // 参数类型不同
virtual void mf3() &&; // 引用标识符不同
void mf4() const; // 基类对应函数不是虚函数
};
看到这里你应该明白了,重写真的容易出错了吧,智能一点的编译器可能会提示我们,但这不是绝对的,要想从根本上解决这个问题还的要从语言本身上下功夫,C++11中引入的override
关键字就是起到这个作用,用于修饰派生类,表明这是一个重写
函数,那么此时编译器会进行函数签名的校验,如果发现不同就会直接报错。