zoukankan      html  css  js  c++  java
  • 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);
    9 };

      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的运行情况
    ~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 ); }




     

  • 相关阅读:
    Shell while循环
    Shell for循环
    针对各主流数据mysql、sqlserver、oracle中文乱码问题。
    robots.txt网站爬虫文件设置
    MySql数据类型
    重温国产thinkphp
    CI分支kohana在线文档
    JIRA官方:为什么要用JIRA?
    百度地图V2.0实践项目开发工具类bmap.util.js V1.4
    Eclipse被汉化后恢复EN模式
  • 原文地址:https://www.cnblogs.com/ziyoudefeng/p/2407659.html
Copyright © 2011-2022 走看看