原文转自 http://blog.csdn.net/fire_lord/article/details/8540592

1.简介

C++为我们提供了继承和虚函数的重写特性。 在派生类中,重写虚函数不强制要求使用 virtual 关键字,并且C++并没有要求强制检查虚函数的重写是否真正覆盖了基类的虚函数,因为在派生类中,也可以增添新的虚函数,以供下一级的派生类重写。由于检查的不够严谨,就有可能产生错误。

2.有可能产生的错误

最常见的错误就是派生类想要重写基类的虚函数,但是由于参数类型的不同,导致结果和预期不同,例如:

  1. #include"stdafx.h"
  2. #include<iostream>
  3.  
  4. using namespace std;
  5.  
  6. class BaseClass
  7. {
  8. public:
  9. BaseClass(){}
  10. ~BaseClass(){}
  11. public:
  12. virtual void func(int param) { cout << "BaseClass::func. param = " << param <<endl; }
  13. virtual void func2() const { cout<< "BaseClass::func2."<<endl;}
  14. };
  15.  
  16. class DeriveClass : public BaseClass
  17. {
  18. public:
  19. DeriveClass(){}
  20. ~DeriveClass(){}
  21. public:
  22. virtual void func(float param) { cout << "DeriveClass::func. param = " << param <<endl; }
  23. virtual void func2() { cout<< "DeriveClass::func2."<<endl;}
  24. };
  25.  
  26. int main()
  27. {
  28. BaseClass* pBase = new BaseClass();
  29. pBase->func(1);
  30. pBase->func2();
  31.  
  32. cout<<endl;
  33.  
  34. DeriveClass* pDerive = new DeriveClass();
  35. pDerive->func(4);
  36. pDerive->func2();
  37.  
  38. cout<<endl;
  39.  
  40. BaseClass* pTest = new DeriveClass();
  41. pTest->func(5);
  42. pTest->func2();
  43.  
  44. cout<<endl;
  45.  
  46. system("pause");
  47. return 0;
  48. }

输出结果为

对于函数func,派生类和基类的参数类型不同,虽然函数的返回值和函数名相同,并使用了virtual进行虚函数的声明,但实际上派生类并没有重写基类的虚函数,而是定义了自己的虚函数。使用const 进行限定也是一样,在基类中func2 为 const 函数,而派生类没有 const 进行限定,因此,派生类也并没有重写基类的虚函数。 pTest 的输出结果和我们原本预期的并不相同,其调用的是基类的虚函数。由上图输出结果可以看出。

3. override

为了避免上述问题的发生,C++11增添了override, override并不是一个关键字,而是一个用于标记虚函数重写的标识符,使用override 标记的类成员函数表示我们希望其重写基类相对应的虚函数。如果没有重写,编译器会报错。例如:

  1. class BaseClass
  2. {
  3. public:
  4. BaseClass(){}
  5. ~BaseClass(){}
  6. public:
  7. virtual void func(int param) { cout << "BaseClass::func. param = " << param <<endl; }
  8. virtual void func2() const { cout<< "BaseClass::func2."<<endl;}
  9. };
  10.  
  11. class DeriveClass : public BaseClass
  12. {
  13. public:
  14. DeriveClass(){}
  15. ~DeriveClass(){}
  16. public:
  17. //error C3668: 'DeriveClass::func' : method with override specifier 'override' did not override any base class methods
  18. virtual void func(float param) override { cout << "DeriveClass::func. param = " << param <<endl; }
  19.  
  20. //error C3668: 'DeriveClass::func2' : method with override specifier 'override' did not override any base class methods
  21. virtual void func2() override { cout<< "DeriveClass::func2."<<endl;}
  22. };

对DeriveClass 的两个成员函数使用了 override ,编译器知道我们期望重写基类的虚函数,但是由于基类并没有相匹配的虚函数,因此编译出错,出错信息如上述注释部分所示。

4.final

在一些情况下,我们并不希望某个成员函数再被任何派生类所继承,在Java语言中,有final来进行限定,C++11也提供了 final (同 override一样不是关键字,只是特殊的标识符)。通过使用final对虚函数的限定,任何子类不能重写该函数,例如:

  1. class BaseClass
  2. {
  3. public:
  4. BaseClass(){}
  5. ~BaseClass(){}
  6. public:
  7. virtual void func(int param) final { cout << "BaseClass::func. param = " << param <<endl; }
  8. };
  9.  
  10. class DeriveClass : public BaseClass
  11. {
  12. public:
  13. DeriveClass(){}
  14. ~DeriveClass(){}
  15. public:
  16. //error C3248: 'BaseClass::func': function declared as 'final' cannot be overridden by 'DeriveClass::func'
  17. virtual void func(int param) override { cout << "DeriveClass::func. param = " << param <<endl; }
  18.  
  19. };

由注释部分可以看出,基类的func函数使用Final进行限定后,派生类无法再重写改函数。

5.总结

C++11新增添的 override 和 final 说明符可以使得虚函数的继承更加明确和安全。遵循新的规则,可以增进代码的可读性,使用final可以更好的对派生和重写虚函数进行限制。

C++11学习笔记(5) —— override and final (转)的更多相关文章

  1. C++11 学习笔记 std::function和bind绑定器

    C++11 学习笔记 std::function和bind绑定器 一.std::function C++中的可调用对象虽然具有比较统一操作形式(除了类成员指针之外,都是后面加括号进行调用),但定义方法 ...

  2. C++11学习笔记

    C++11 1.long long新类型 2.列表初始化 int t=0; int t={0}; int t(0); int t{0}; 注意:如果我们使用列表初始化有丢失信息的风险,则编译器报错 l ...

  3. C++11之重写说明符override和final

    关于 本文代码演示环境: win10 + vs2017 一个困扰 之前MFC用的多了,发现一个问题: 子类窗口的某个函数是否重载了基类的函数.解决办法是: 打开基类的代码,一个个排查. 这只是一个具体 ...

  4. PHP学习笔记三十【final】

    <?php //final不能去修饰属性(变量) //如果希望类不希望被继承就可以使用final关键字 final class Person() { public function sayHi( ...

  5. linux0.11学习笔记(1)

    公布软件包包括内容: bootimage.Z - 具有美国键盘代码的压缩启动映像文件: rootimage.Z - 以1200kB 压缩的根文件系统映像文件: linux-0.11.tar.Z- 内核 ...

  6. C++11学习笔记之三lamda表达式,std::function, std::bind

    //lamda //first lamda [] {}; // second lamda []() //or no need () when paramater is null { std::cout ...

  7. 802.11学习笔记1-WIFI参数含义

    研究下wifi参数的含义 #The word of "Default" must not be removed Default CountryRegion= CountryRegi ...

  8. Linux就该这么学11学习笔记

    参考链接:https://i.cnblogs.com/EditPosts.aspx?opt=1 文件传输协议 一般来讲,人们将计算机联网的首要目的就是获取资料,而文件传输是一种非常重要的获取资料的方式 ...

  9. 【DirectX 11学习笔记】世界矩阵的理解-运动合成

    最近在看龙书,写一下自己的学习理解,主要是物体运动的合成. 物体于局部坐标系内构建,每个物体拥有自己的局部坐标系以及相应的顶点矩阵A,并通过世界矩阵变换到唯一的世界坐标系. 物体在某时刻发生了位移和旋 ...

随机推荐

  1. linux文件或文件夹常见操作

    创建文件夹 mkdir [-p] DirName  在工作目录下,建立一个名为 A 新的子目录 : mkdir A  在工作目录下的 B目录中,建立一个名为 T 的子目录:    若 B 目录不存在, ...

  2. 遍历Map的两种方式

    取出map集合中所有元素的方式一:keySet()方法. 可以将map集合中的键都取出存放到set集合中.对set集合进行迭代.迭代完成,再通过get方法对获取到的键进行值的获取. Set keySe ...

  3. skynet 学习笔记-netpack模块(1)

    int luaopen_netpack(lua_State *L) { luaL_checkversion(L); luaL_Reg l[] = { { "pop", lpop } ...

  4. iOS应用架构谈part3 网络层设计方案

    前言 网络层在一个App中也是一个不可缺少的部分,工程师们在网络层能够发挥的空间也比较大.另外,苹果对网络请求部分已经做了很好的封装,业界的AFNetworking也被广泛使用.其它的ASIHttpR ...

  5. [转]LLE

    原始特征的数量可能很大,或者说样本是处于一个高维空间中,通过映射或变换的方法,降高维数据降低到低维空间中的数据,这个过程叫特征提取,也称降维. 特征提取得基本任务研究从众多特征中求出那些对分类最有效的 ...

  6. NOIP2013 表达式求值

    题目描述 Description 给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值. 输入描述 Input Description 输入仅有一行,为需要你计算的表达式,表达式中只包含数字. ...

  7. Python爬虫系列-Urllib库详解

    Urllib库详解 Python内置的Http请求库: * urllib.request 请求模块 * urllib.error 异常处理模块 * urllib.parse url解析模块 * url ...

  8. LeetCode(189) Rotate Array

    题目 Rotate an array of n elements to the right by k steps. For example, with n = 7 and k = 3, the arr ...

  9. js中xml文件加载

  10. 安装liteIDE on mac

    download and install: http://sourceforge.net/projects/liteide/files/ 解决不能编译,没有自动完成的问题: http://stacko ...