虚函数

https://blog.csdn.net/zwe7616175/article/details/81334711
https://zhidao.baidu.com/question/123304290.html


说明:

使用虚函数,系统会有一定的空间开销。当一个类带有虚函数时,编译系统会为该类构造一个虚函数表(位于类内其他成员前面),是一个指针数组,存放每个虚函数的入口地址。系统在进行动态关联的时间开销很少,提高了多态性的效率。

upload successful

编译器生成的析构函数都是非虚的,除非是一个子类,其父类有个虚析构函数,此时的虚函数特性继承自基类。有虚函数的类,一般情况下要定义一个虚析构函数。

纯虚函数不能new。


构造函数不能声明为虚函数的原因?

  1. 构造一个对象时,必须知道对象实际类型,而虚函数是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功,编译器就无法知道对象的实际类型,是该类本身,还是派生类,还是其他。

  2. 虚函数的执行依赖于虚函数表,而虚函数表是在构造函数中进行初始化的,即初始化虚表指针(vptr),使得正确指向虚函数表。而在构造对象期间,虚函数表(vtable)还没有被初始化,将无法进行。

什么情况下,类的析构函数应该声明为虚函数?为什么?

类A中有了虚函数就会在类的数据成员的最前面添加一个vfptr指针(void** vfptr),这个指针用来指向一个vtable表(一个函数指针数组)(一个类只有一个该表),该表存储着当前类的所有虚函数的地址。这样vfptr就成为了一个类似成员变量的存在。访问虚函数的时候通过vfptr间址找到vtable表,再间址进而找到要调用的函数。这样就在一定程度上摆脱了类型制约。

当B类继承A类的时候,因为A中有虚函数,编译器就自动的给B类添加vfprt指针和vtable表。也可以理解为B类继承来了A类中的那个vptr指针成员。(B对象的vfptr浅拷贝到A对象的vfptr)因此,A对象的vfptr所指向的是B对象的虚函数表,而B的析构函数位于书函数表0的位置,因此,这样就可以通过A类对象的指针d,找到B类对象的析构函数,从而在delete d;时,可以销毁B对象,而不会产生内存泄漏和异常。

  • 简单得来讲:析构函数设为虚函数的原因是为了防止内存泄露。在继承体系中,当基类的指针或引用指向派生类,用基类delete时,如果基类的析构函数没有声明为虚函数,只能析构基类对象,派生类对象将无法析构。因为普通函数受类型的制约,(因为没有vptr指针)使用哪个类的指针调用函数,那么所调用的就是那个累的函数。
    1
    2
    3
    4
    # bug复现
    a* aa= new b(); // b继承a
    delete aa;
    // 打印(执行) a 的析构函数,不执行b的

所以需要将基类的析构函数声明为虚函数,当撤销基类对象的同时也撤销派生类的对象,这个过程是动态关联完成的。


内联函数不能是虚函数

内联函数会在预编译时会进行代码展开,省略函数调用,因此内联函数不能是虚函数。虽然使用inline和virtual共同修饰一个函数时能够通过编译,并在调用时会表现出虚函数的性质,但这是因为编译器在函数声明中遇到virtual关键字时,会选择忽略inline关键字,不进行代码展开。

静态函数不能声明为虚函数

虚函数体现了对象在运行时的多态性,而静态函数属于整个类,不属于某个对象,不能声明为虚函数。

文章目录
| 本站总访问量次 ,本文总阅读量