当前位置:编程学习 > C/C++ >>

C++派生类中定义基类的虚函数时需注意的事项

先给出文字说明,然后再给出代码解释:
  如果我们决定改写基类所提供的虚拟函数,那么派生类所提供的新定义,其函数型别必须完全符合基类所声明的函数原型,包括:参数列、返回型别、常量性(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.zzzyk.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 ); }

 

摘自  ziyoudefeng
 
补充:软件开发 , C++ ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,