目录

1 多态性

1.1 编译整体过程

1.2 两种形式的多态

2 相关概念

2.1 虚函数

2.1.1 虚函数实现原理

2.2.2 虚函数为何不可以声明为 inline ?

2.2.3 构造函数为什么不能为虚函数?

2.2.4 析构函数为什么要虚函数?

2.2.5 构造函数和析构函数可以调用虚函数吗,为什么?

2.2.6 哪些函数不能是虚函数?

2.2 接口继承与实现继承


1 多态性

同样的接口访问功能不同的函数,从而实现“一个接口,多种方法”。

在C++中,多态性的实现和联编(也称绑定)这一概念有关。C++支持两种多态性:编译时多态性,运行时多态性。

1.1 编译整体过程

  • 1.      预处理:包含宏替换,条件编译,include导入文件(针对C/C++)
  • 2.      编译:  包含词法分析,语法分析,语义分析,中间代码生成与优化,生成汇编文件
  • 3.      汇编:  将汇编文件编译成2进制的机器码
  • 4.      链接:  将目标文件与外部符号进行链接,得到一个二进制可执行文件

1.2 两种形式的多态

联编:一个源程序经过编译、链接,成为可执行文件的过程。

函数的联编:在编译或运行将函数的调用,与相应的函数体连接在一起的过程。

  • 静态联编(前期联编):在运行之前就完成的联编称之为~。

    • 静态联编支持的多态性称为编译时多态性(静态多态性)。

      • 编译时多态性:在C++中,编译时多态性是通过函数重载模板实现的。

        • 比如利用函数重载机制,在调用同名函数时,编译系统会根据实参的具体情况,确定索要调用的是哪个函数。
        • 比如当编译器遇到一个模板定义时,它并不生成代码。只有当遇到实例化出模板的一个特定版本时,编译器才会生成代码。
  • 动态联编(后期联编):在程序运行之时才完成的联编称之为~
    • 动态联编所支持的多态性称为运行时多态(动态多态)。

      • 运行时多态性:在C++中,运行时多态性是通过虚函数来实现的。

        • 比如运行基类指针指向派生类的对象(Animal*a=new Dog(); a->eat();),以调用派生类的函数。
        • 那么在继承中要构成多态还需要两个条件:
          • a. 调用函数的对象必须是指针或者引用
          • b. 被调用的函数必须是虚函数,且完成了虚函数的重写

2 相关概念

2.1 虚函数

虚函数是在基类中被声明为 virtual,并在派生类中重新定义的成员函数,可实现成员函数的动态重载。

  • 虚函数实现原理:虚函数表和虚函数指针。

抽象类:包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。

  • 纯虚函数: virtual int fun() = 0;
  • 纯虚函数引入原因?
    • 1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
    • 2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以 派生出老虎、孔 雀等子类,但动物本身生成对象明显不合常理。

2.1.1 虚函数实现原理

每一个含有虚函数(无论是其本身的,还是继承而来的)的类都至少有一个与之对应的虚函数表,其中存放着该类所有的虚函数对应的函数指针。例:

x86下:sizeof(B) = 8 (因为long的4 + vptr的4)

相关概念:static 成员函数不能被 virtual 修饰,static 成员不属于任何对象或实例,所以加上 virtual 没有任何实际意义;静态成员函数没有 this 指针,虚函数的实现是为每一个对象分配一个 vptr 指针,而 vptr 是通过 this 指针调用的,所以不能为virtual;虚函数的调用关系,this->vptr->ctable->virtual function

2.2.2 虚函数为何不可以声明为 inline ?

虚函数用于实现运行时的多态。而内联函数用于提高效率。内联函数的原理是,在编译期间,对调用内联函数的地方的代码替换成函数代码。内联函数对于程序中需要频繁使用和调用的函数非常有用。总之,虚函数要求在运行时进行类型确定,而内敛函数要求在编译期完成相关的函数替换

2.2.3 构造函数为什么不能为虚函数?

a. 从存储空间角度,指向 vtable 虚函数表的指针是存储在对象的内存空间的。假设构造函数是虚的,但是对象还没有实例化,也就是内存空间还没有,怎么找 vtable 呢?所以构造函数不能是虚函数。

b. 从使用角度,构造函数是在创建对象时自己主动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。

2.2.4 析构函数为什么要虚函数?

a. 从使用角度,我们往往通过基类的指针来销毁对象。这时候假设析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用析构函数。

b. 因此,C++中基类采用 virtual 虚析构函数是为了防止内存泄漏。具体地说,如果派生类中申请了内存空间,并在其析构函数中对这些内存空间进行释放。假设基类中采用的是非虚析构函数,当删除基类指针指向的派生类对象时,就不会触发动态绑定,因而只会调用基类的析构函数,而不会调用派生类的析构函数。那么在这种情况下,派生类中申请的空间就得不到释放从而产生内存泄漏。

2.2.5 构造函数和析构函数可以调用虚函数吗,为什么?

1) 在 C++中,不提倡在构造函数和析构函数中调用虚函数;

2) 构造函数和析构函数调用虚函数时都不使用动态联编,并不会发挥虚函数动态绑定的特性,跟普通函数没区别;此时,则运行的是为构造函数或析构函数自身类型定义的版本;

3) 因为父类对象会在子类之前进行构造,此时子类部分的数据成员还未初始化,因此调用子类的虚函数是不安全的,故而 C++不会进行动态联编;

4) 析构函数是用来销毁一个对象的,在销毁一个对象时,先调用子类的析构函数,然后再调用基类的析构函数。所以在调用基类的析构函数时,派生类对象的数据成员已经销毁,这个时候再调用子类的虚函数没有任何意义。

2.2.6 哪些函数不能是虚函数?

  • 1) 构造函数,构造函数初始化对象,派生类必须知道基类函数干了什么,才能进行构造;当有虚函数时,每一个类有一个虚表,每一个对象有一个虚表指针,虚表指针在构造函数中初始化;
  • 2) 内联函数,内联函数表示在编译阶段进行函数体的替换操作,而虚函数意味着在运行期间进行类型确定,所以内联函数不能是虚函数;
  • 3) 静态函数,静态函数不属于对象属于类,静态成员函数没有 this 指针,因此静态函数设置为虚函数没有任何意义。
  • 4) 友元函数,友元函数不属于类的成员函数,不能被继承。对于没有继承特性的函数没有虚函数的说法。

  • 5) 普通函数,普通函数不属于类的成员函数,不具有继承特性,因此普通函数没有虚函数。

2.2 接口继承与实现继承

  • 实现继承:普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现本身(自己使用父类的方法,不需要在子类中再定义)。
  • 接口继承:虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。

C++_ 多态与虚函数介绍相关推荐

  1. C++中类的多态与虚函数的使用(转)

    C++中类的多态与虚函数的使用 http://www.cnblogs.com/fangyukuan/archive/2010/05/30/1747449.html 类的多态特性是支持面向对象的语言最主 ...

  2. C++中类的多态与虚函数的使用

    C++中类的多态与虚函数的使用 http://www.cnblogs.com/fangyukuan/archive/2010/05/30/1747449.html 类的多态特性是支持面向对象的语言最主 ...

  3. 复习笔记(五)——C++多态和虚函数

    静态绑定与动态绑定 静态绑定: -编译时就能确定一条函数调用语句要调用的函数 -在程序编译时多态性体现在函数和运算符的重载上 动态绑定: -运行时才能确定函数调用语句调用的函数 -程序运行时的多态性通 ...

  4. C++初步之核心编程篇五:多态与虚函数类

    C++初步之核心编程篇五:多态与虚函数类 文章目录 C++初步之核心编程篇五:多态与虚函数类 1. 多态的概要 2. 多态案例一-计算器类 3. 纯虚函数和抽象类 4. 多态案例二-制作饮品 5. 虚 ...

  5. 【阿里面试】C++多态和虚函数

    文章目录 一.C++的面试常考点 二.阿里真题 2.1 现在假设有一个编译好的C++程序,编译没有错误,但是运行时报错,报错如下:你正在调用一个纯虚函数(Pure virtual function c ...

  6. C++ 多态、虚函数、虚方法

    子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override).通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写 ...

  7. PKU C++程序设计实习 学习笔记3 多态与虚函数

    第六章 多态与虚函数 6.1 多态和虚函数的基本概念 引言 多态是面向对象程序设计里面非常重要的这个机制.它能很有效的提高程序的可扩充性. 有些程序设计语言有被对象继承的概念,但是没有多态的概念,那这 ...

  8. C++学习之第十一天-多态、虚函数

    一.选择题 1.下列关于动态联编的描述中,错误的是(D). A.动态联编是以虚函数为基础 B.动态联编是运行时确定所调用的函数代码的 C.动态联编调用函数操作是指向对象的指针或对象引用 D.动态联编是 ...

  9. C++ 纯虚函数介绍

    C++纯虚函数介绍 纯虚函数是在基类中声明的虚函数,在基类中没有定义,但是要求任何派生类(子类)都要定义该虚函数自己的实现方法. 基类中实现纯虚函数方法在函数原型后面加一个等于0. 一旦一个类中有纯虚 ...

  10. C++学习12:C++多态、虚函数、虚析构函数、纯虚函数、抽象类

    一 多态概述 C++中的多态分为静态多态和动态多态.静态多态是函数重载,在编译阶段就能确定调用哪个函数.动态多态是由继承产生的,指同一个属性或行为在基类及其各派生类中具有不同的语义,不同的对象根据所接 ...

最新文章

  1. 铁乐学Python_Day35_Socket模块3和hmac模块
  2. python 正则表达式 前瞻_正则表达式 For Python
  3. 生成树的概念,最小生成树Prim算法 Kruskal算法
  4. Python常用函数与技巧总结(二)
  5. (五)Java工具类ArrayUtils详解
  6. ML之DTRFRExtraTRGBR:基于四种算法(DT、RFR、ExtraTR、GBR)对Boston(波士顿房价)数据集(506,13+1)进行价格回归预测并对比各自性能
  7. CF954I Yet Another String Matching Problem 并查集、FFT
  8. html快捷键_Mac进阶:掌握这 5 个冷门快捷键,让Mac更好用
  9. 服务器网络销售软文,关于云服务器的软文
  10. -bash: wget 未找到命令的解决办法
  11. 量子力学概论_科学网—《量子力学导论》潘必才 - 中国科大出版社的博文
  12. java sybase 驱动_Java连接Sybase数据库
  13. Github创建的个人简历
  14. 立创EDA超详细的PCB设计流程
  15. 5.3.5—二叉查找树—Convert Sorted List to Binary Sear Tree
  16. 好用的ROS开发软件(持续更新~)
  17. Cortex-M3处理器的舞台
  18. 魅族便签导出,实践中
  19. 多径效应(时间色散、多普勒效应)及其他描述多径传播影响的参数(电平通过率、平均衰落时间)
  20. XAMPP下载安装、control.ini拒绝访问、Apache和mysql启动报错、与本地MySQL端口冲突问题

热门文章

  1. Angular 2 ViewChild ViewChildren
  2. 第三周 3.14 --- 3.20
  3. 【转】SIP 中的Dialog,call,session 和 transaction
  4. duilib学习 --- 360demo 学习
  5. UIDatePicker 显示时间和打印时间不一样
  6. windows/linux 查看端口占用
  7. 把后端传递过来的base64图片保存到本地
  8. SqlServer删除表中某几列重复的记录并保留一条
  9. 运行uiautomatorviewer.bat报错No suitable Java found
  10. Protobuf C++类中成员函数GetCachedSize()与ByteSize()的区别