C++虚函数原理

编程入门 行业动态 更新时间:2024-10-09 21:27:26

C++虚<a href=https://www.elefans.com/category/jswz/34/1771370.html style=函数原理"/>

C++虚函数原理

一、前言

虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用了子类的成员函数。这种技术可以让父类的指针有“多种形态”。这种类型的识别是在程序运行时定义的,也叫运行时多态。

二、虚函数表实现原理

虚函数的实现是由两个部分组成的,虚函数指针与虚函数表。

2.1 虚函数指针

为了指定对象的虚表,对象内部包含一个虚表的指针,来指向自己所使用的虚表。为了让每个包含虚表的类的对象都拥有一个虚表指针,编译器在类中添加了一个私有指针,*__vptr,用来指向虚表。这样,当类的对象在创建时便拥有了这个指针,且这个指针的值会自动被设置为指向类的虚表。

只有拥有虚函数的类才会拥有虚函数指针,所以拥有虚函数的类的所有对象都会因为虚函数产生额外的开销,并且也会在一定程度上降低程序速度。与JAVA不同,C++将是否使用虚函数这一权利交给了开发者,所以开发者应该谨慎的使用。

2.2 虚函数表

虚函数表是一个类的虚函数的地址表,用于索引类本身以及父类的虚函数的地 址,假如子类的虚函数重写了父类的虚函数,则对应在虚函数表中会把对应的虚函数替换为子类的 虚函数的地址;虚函数表指针存在于每个对象中(通常出于效率考虑,会放在对象的开始地址处), 它指向对象所在类的虚函数表的地址;在多继承环境下,会存在多个虚函数表指针,分别指向对应 不同基类的虚函数表。

虚表是属于类的,对于基类与派生类,基类有基类的虚函数表,派生类有派生类的虚函数表。

举个例子,定义三个类A,B,C依次继承

class ClassA
{
public:int m_data1;int m_data2;void func1() {}void func2() {}virtual void vfunc1() {}virtual void vfunc2() {}};class ClassB:public ClassA
{
public:int m_data3;void func2() {} // 非虚函数,基类的同名函数会被隐藏virtual void vfunc1() {}};class ClassC :public ClassB
{
public:int m_data1;int m_data4;void func2() {}  // 非虚函数,基类的同名函数会被隐藏virtual void vfunc1() {}};

可以看到,在子类的虚函数表中,基类函数的虚函数表项仍然保留,子类自己的虚函数将跟在表后,如果子类重写了父类的虚函数,虚函数表会更新。比如,classA的虚函数是vFunc1,vFunc2,ClassB继承ClassA,它重写了vFunc2,就会替换该函数地址,以此类推。

三、一些遗留问题

1、 构造函数是否能为虚函数?

不能,

1)如果构造函数是虚函数,那么就需要通过虚表指针vtable 来调用,但此时面对一块没有初始化的内存,到哪里去找 vtable 呢?毕竟,vtable 是在构造函数中才初始化的啊,而不是在其之前。因此构造函数不能为虚函数。

2)构造函数在进行调用时还不存在父类和子类的概念,父类只会调用父类的构造函数,子类调用子类 的,因此不存在动态绑定的概念

2、析构函数可以是虚函数吗?

可以,而且最好是虚的,因为此时 vtable 已经初始化了;况且我们通常通过基类的指针来销毁对象,如果析构函数不为虚的话,就不能正确识别对象类型,从而不能正确销毁对象。

3、使用虚函数的时候,子类也要使用virtual关键字吗?

父类使用虚函数是为了让子类重写,那子类重写的时候也需要带virtual关键字吗?比如:

class Base{virtual bool init();
};class Derived : public Base {virtual bool init(); //这里的vitual是必须的吗?好像不用也能编译通过呃…..
};

C++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。因此,在子类从新声明该虚函数时,可以加,也可以不加,建议父类加virtual,子类加override,这样清晰明了

class Base {virtual bool init();
};class Derived : public Base {bool init() override; // 这样写更清晰
};

4、构造函数中可以调用虚函数吗?

可以,但是没有动态绑定的效果,父类构造函数中调用的仍然是父类版本的函数,子类中调用的仍然是子类版本的函数

5、内联函数、静态成员函数可以是虚函数吗?

内联函数可以,但是不要这么写。内联函数需要在编译阶段展开,而虚函数是运行时动态绑定的,编译时无法展开,当然你可以写 inline virtual 这样的函数,它也不报错,因为内联是编译器决定的,并不跟是否有关键字inline有关系。而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时(运行期)不可以内联;

静态成员函数不可以,因为它是以类为单位的函数,与具体对象无关,无this指针,虚函数是与对象动态绑定的,必定是成员函数,成员函数肯定有this指针,因此是两个冲突的概念;

参考:

C++:构造函数和析构函数能否为虚函数 - 止战 - 博客园

C++虚函数详解_Whitesad_的博客-CSDN博客_c++虚函数

C++ 虚函数、虚函数表剖析_年年年年年的博客-CSDN博客

更多推荐

C++虚函数原理

本文发布于:2024-02-07 10:24:57,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1756423.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:函数   原理

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!