继承是实现多态的基础。虚函数是实现多态的方法。虚函数、多态、继承都是紧密相关的概念。而继承是所有概念的基础;
多态:简单来讲就是接口一样,实现多样。多态是指通过基类的指针或者引用,在运行时动态调用实际绑定对象函数的行为。与之相对应的编译时绑定函数称为静态绑定。多态是面向对象编程的核心思想之一。多态还有个关键之处就是一切用指向基类的指针或引用来操作对象。
虚函数:刚才说到,指向基类的指针在操作它的多态类对象时,会根据不同类的对象,调用其相应的函数。这个函数就是虚函数。虚函数的虚是在于它的调用不是在编译时被确定的,而是在运行时被确定的。
虚函数就是在函数名声明前面加一个关键字 virtual。
这里顺便阐述一个概念:联编
将源代码中的函数调用解释为特定的函数代码块被称为函数名联编(binding)。就像上方所说的“绑定”,是一回事。
编译器可以在编译过程中完成联编,这被称作静态联编。又称为早期联编。
虚函数使这项工作变得更加困难。使用哪个函数不是在编译时就能确定的,因为编译器不知道用户将选择哪个类型的对象。所以编译器必须生成能够在程序运行时选择正确的虚方法的代码,这被称为动态联编,又称为晚期联编。
编译器对非虚方法使用静态联编。对虚方法采用动态联编。
因此我们可以理解虚函数的意义在于告诉编译器采用动态联编的方式。之所以这样做的目的还是为了服务于多态的要求。
虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism)。
C++的指导原则之一就是不要为不使用的特性付出代价(内存或处理时间)。
而虚函数的技术,是会造成额外的开销,为了使程序能够在运行阶段进行决策,必须采用一些方法跟踪基类指针或引用指向的对象类型,这增加了额外的的处理开销。
使用静态联编的方式就更加高效,所以默认都是使用静态联编的方式。
纯虚函数:
为了方便实现多态特性,常常需要在基类中定义虚拟函数,但是往往基类是一种抽象类,其本身是不能被实例化的。
所以为了解决上述问题,引入纯虚函数的概念。
纯虚函数在基类中声明,但没有定义。
但要求任何派生类都要定义自己的实现方法。
使用方式就是:在基类函数原型后面加"=0"
====================================================
相关阅读:
如果期望派生类重新定义一个成员函数,应该在基类中把此函数设为virtual。
允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。
在基类用virtual声明成员函数为虚函数可以在派生类中重新定义此函数,为它赋予新的功能,并能方便地被调用。
如果在派生类中没有对基类的虚函数重新定义,则派生类简单地继承其直接基类的虚函数。
如果在派生类中重新定义此函数,要求函数名、函数类型、函数参数个数和类型全部与基类的虚函数相同,并根据派生类的需要重新定义函数体。
在派生类重新声明该虚函数时,可以加virtual,也可以不加,但习惯上一般在每一层声明该函数时都加virtual,使程序更加清晰。
纯虚函数
抽象类中的虚函数不打算被调用,不应该定义它,应该把它设置为纯虚函数(在函数声明之后加上'=0'即可);
基类不在基类中实现纯虚函数的方法是在函数原型后加“=0”
virtual void funtion1()=0
纯虚函数是一种特殊的虚函数,在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。
拥有纯虚函数者为抽象类,以别于所谓的具体类;
抽象类不能产出对象实例,但是我们可以拥有指向抽象类的指针,以便于操作抽象类中的各个派生类。
为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。
为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要求在派生类中必须予以重写以实现多态性。
同时含有纯虚拟函数的类称为抽象类,它不能生成对象。
更深层次的讨论:
纯虚函数是为你的程序制定一种标准,即只要你继承了我,就必须按照我和标准来,实现我所有的方法,否则你也是虚拟的,和JAVA里的接口一样,都是制定标准,为了使程序更加通用化,可重用性提高,让所有实现它或继承自它的子类全部按同一标准来工作,你想想大家都按标准来对程序的益处是什么?
===================================================
C++中不能被声明为虚函数的函数有哪些?
普通函数
普通函数(非成员函数)只能被overload,不能被override,而且编译器会在编译时绑定函数。能重载但是不能被重写。
构造函数 //为什么构造函数不能被声明为虚函数?
1 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功。编译器无法知道对象 的实际类型,是该类本身,还是该类的一个派生类,或是更深层次的派生类。无法确定。
2 虚函数的执行依赖于虚函数表。而虚函数表在构造函数中进行初始化工作,即初始化vptr,让他指向正确的虚函数表。而在构造对象期间,虚函数表还没有被初始化,将无法进行。
派生类的对象在基类构造函数调用前还不存在。
内联成员函数
内联函数就是为了在代码中直接展开,减少函数调用花费的代价,虚函数是为了在继承后对象能够准确的执行自己的动作,这是不可能统一的。
静态成员函数
静态成员函数对于每个类来说只有一份代码,所有的对象都共享这一份代码,他也没有要动态绑定的必要性。不能被继承,只属于该类。
友元函数 //为什么友元不能作为虚函数?
C++不支持友元函数的继承,对于没有继承特性的函数没有虚函数的说法。友元函数不属于类的成员函数,不能被继承。
不能,友元函数不是成员函数,不能被继承,不支持运行时多态
==================================================
什么是C++多态?
当类之间存在这继承的关联时,就会用到多态。
C++ 多态是指:使用基类的指针和引用调用成员函数时,会根据调用函数的对象的类型来执行不同的成员函数。
通过基类引用和虚函数机制实现多态。
基类指针和引用可以指向派生类。
===================================================
什么是虚表?
虚表是虚函数表,里面存放了指向虚函数的指针。
虚表是实现多态的关键。在编译时保存一份虚表,用来躲开静态联编。
被virtual修饰的函数,指向这些函数的指针都会放进虚表当中。
每个类都会有自己的虚表。
虚指针?
是指向虚表的指针。用来告诉我们虚表在哪里。虚表一般放在模块的常量段里。(空间消耗)
虚指针是类实例对象指向虚表的指针,存在于对象头部,大小为4个字节。
C++虚表,你搞懂了吗?:https://www.jianshu.com/p/087f88a5f48a //例子很生动
===================================================
进一步总结://虚函数是什么,纯虚函数是什么
虚函数是实现多态的方式;虚函数用于告诉编译器进行动态联编。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism)。
虚函数通过继承方式来体现出多态作用。
多态的意思就是使用指向基类的指针或引用来调用其具体绑定对象的函数。
多态实际上就是晚期联编,或者叫动态联编。
指向基类的指针在操作它的多态类对象时,会根据不同类的对象,调用其相应的函数。
虚函数的虚是在于它的调用不是在编译时被确定的,而是在运行时被确定的。
纯虚函数一般在声明时末尾加上 = 0;
含有纯虚函数的类时抽象类,不能被实例化,这样的类通常被当作时基类。
继承该基类的子类必须实现该纯虚函数,否则它也不能被实例化,它也成了抽象类。
所以纯虚函数起到一种接口规范的作用。
什么是虚函数?
在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数,
用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};
实现多态性,通过指向派生类对象的基类指针或引用,访问派生类中同名覆盖成员函数。
===================================================
相关文章传送门:
C++_了解虚函数的概念:https://www.cnblogs.com/grooovvve/p/10112085.html
C++_类继承3-动态联编和静态联编:https://www.cnblogs.com/grooovvve/p/10422296.html
=====================================================
相关链接:
多态的实现原理:https://blog.csdn.net/kwanson/article/details/80379360
多态(C++):https://blog.csdn.net/ijn842/article/details/80936236
C++ 中不能声明为虚函数的函数有哪些?:https://www.cnblogs.com/daryl-blog/p/11003101.html
虚函数实现原理:https://blog.csdn.net/weixin_40237626/article/details/82313339
虚函数的介绍:https://baijiahao.baidu.com/s?id=1654573887336047916&wfr=spider&for=pc