C++派生类中定义基类的虚函数时需注意的事项
3550 点击·0 回帖
![]() | ![]() | |
![]() | 先给出文字说明,然后再给出代码解释: 如果我们决定改写基类所提供的虚拟函数,那么派生类所提供的新定义,其函数型别必须完全符合基类所声明的函数原型,包括:参数列、返回型别、常量性(const-ness)。 下面给出程序说明:基类num_sequence中声明虚拟函数what_am_i(),派生类中改写该函数。 1、正确的写法 1.1 基类的声明 1 #pragma once 2 3 class num_sequence 4 { 5 public: 6 num_sequence(void); 7 virtual const char* what_am_i() const { return "num_sequence \n"; } //注意这里的两个const 8 virtual ~num_sequence(void); 1.2 派生类中正确的改写 #pragma once #include "num_sequence.h" class Fibonacci : public num_sequence { public: Fibonacci(void); virtual const char* what_am_i() const { return "Fibonacci \n"; } //同样注意这里的两个const,少哪个都不行, //后面详解少其中任何一个const的运行情况www.atcpu.com ~Fibonacci(void); }; 上述是正确的改写,下面给出两种缺少const的错误改写: 2.1 少 函数后面的const(即第二个const) Wrong1 1 #pragma once 2 #include "num_sequence.h" 3 4 class Fibonacci : 5 public num_sequence 6 { 7 public: 8 Fibonacci(void); 9 virtual const char* what_am_i() { return "Fibonacci \n"; } //注意这里少了const 10 ~Fibonacci(void); 11 }; main函数中测试: 1 // EssentialCppP162.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 6 #include "Fibonacci.h" 7 #include <iostream> 8 9 using namespace std; 10 int _tmain(int argc, _TCHAR* argv[]) 11 { 12 Fibonacci b; 13 num_sequence p; 14 num_sequence *pp = &b; 15 cout << pp->what_am_i(); 16 cout << b.what_am_i(); 17 return 0; 18 } 输出的结果为: num_sequence Fibonacci 请按任意键继续. . . 解释:这里子类Fibonacci中并没有改写基类的what_am_i(),而是重新定义了一个what_am_i()函数(PS:这里说成是重载what_am_i()更合适)。所以尽管pp是指向子类的指针,但子类没有重定义该虚函数,最后就调用的是基类的what_am_i()函数,输出num_sequence。而b.what_am_i()则因为b为非const,会调用Fibonacci中的what_am_i())。(PPS:这里如果是const num_sequence *pp = &b; cout << pp->what_am_i(); 输出也是num_sequence,原因不说了。) PS:这里Essential C++ P161上说的是在 intel C++编译器上编译时,会输出警告: warning #653: "const char *Fibonacci::what_am_i()" does not match "num_sequence::what_am_i" -- virtual function override intended? 但我在VS200中文版中测试时候完全没有警告,所以写改写基类虚拟函数时候一定要小心,尽量用ctrl+c从基类中复制过来,防止手动敲入函数名字时出错。 2.2 少函数返回类型中的的const(即前面的那个const) Wrong2 1 #pragma once 2 #include "num_sequence.h" 3 4 class Fibonacci : 5 public num_sequence 6 { 7 public: 8 Fibonacci(void); 9 virtual char* what_am_i() const { return "Fibonacci \n"; } 10 ~Fibonacci(void); 11 }; 此种情况编译器不会通过编译,因为函数重载不是根据返回类型来定的,所以编译器会认为这里的what_am_i()是继承的基类的函数。然后根据本篇文章开头说的函数型别必须完全符合基类的声明,这里就会报错。VS2008下报错为: 1>e:\vsprog\临时测试文件夹\essentialcppp162\essentialcppp162\fibonacci.h(11) : error C2555: “Fibonacci::what_am_i”: 重写虚函数返回类型有差异,且不是来自“num_sequence::what_am_i”的协变 1>e:\vsprog\临时测试文件夹\essentialcppp162\essentialcppp162\num_sequence.h(7) : 参见“num_sequence::what_am_i”的声明 2.3 两个const都少了 Wrong3 1 #pragma once 2 #include "num_sequence.h" 3 4 class Fibonacci : 5 public num_sequence 6 { 7 public: 8 Fibonacci(void); 9 virtual char* what_am_i() { return "Fibonacci \n"; } 10 ~Fibonacci(void); 11 }; 这和第一种错误一样,都是一个重载的函数,而不是改写基类的虚拟函数。。 关于继承基类的虚拟函数的说明就到此结束。最后说一下改写基类的虚拟函数时,子类中声明不一定非得加上关键词virtual。编译器会依据两个函数的原型声明,决定某个函数是否会改写其基类中的同名函数( 比如这里1.2中可以写成这样const char* what_am_i() const { return "Fibonacci \n"; } )。 ============================= 补充: “返回型别必须完全吻合” 这一规则有个例外:当基类的虚拟函数返回某个基类形式(通常是pointer或reference)时:派生类中的同名函数便可以返回该基类所派生出来的型别:举例如下(尚不知道这里实际工程项目中的用处): 基类num_sequnece: virtual num_sequence *clone() = 0; 子类Fibonacci: [virtual] Fibonacci *clone() { return new Fibonacci ( *this ); } | |
![]() | ![]() |