虚指针存在证明及虚函数表

C++多态的实现原理是依赖虚指针来辨别具体使用家族类中的哪一个函数。以下就来证明虚指针的存在。
我们知道,指针的大小在一般情况下是4个字节,所以我们建立一个虚函数,再来计算该类的大小,便可以验证虚函数的存在。

代码如下:

#include <iostream>
using namespace std;

class A
{
public:
                 void print ()
                {

                }
private:
                 int a ;
};

class B
{
public:
                 virtual void print()
                {

                }
private:
                 int a ;
};


void main ()
{
                 cout << "sizeof(A)" << sizeof( A) << endl ;
                 cout << "sizeof(B)" << sizeof( B) << endl ;
                 system("pause" );
}

运行结果:
这里写图片描述
A类与B类的,唯一差别就是在B类中 print()变成了一个虚函数,实验结果看到B类却比A类多出了4个字节,那说明在B类中,编译器隐藏的添加了一个虚指针的成员变量。

接下来在验证,一个类中,有多个虚函数,会不会分配多个虚指针:

#include <iostream>
using namespace std;

class B
{
public:
                 virtual void print()
                {

                }
                 virtual void print2()
                {

                }
private:
                 int a ;
};

void main ()
{
                 cout << "sizeof(B)" << sizeof( B) << endl ;
                 system("pause" );
}

运行结果:
这里写图片描述
这就说明了,一个类中不管有几个虚函数,都只会生成一个虚指针变量。
这个虚指针变量实质上是指向一个虚函数表。我们先来看下什么是虚函数表。

虚函数表定义:
类的虚函数表是一块连续的内存,每个内存单元中记录一个JMP指令的地址。注意的是,编译器会为每个有虚函数的类创建一个虚函数表,该虚函数表将被该类的所有对象共享。类的每个虚成员占据虚函数表中的一行。如果类中有N个虚函数,那么其虚函数表将有N*4字节的大小。也就是说虚函数表是一块连续的内存,每一个虚成员,都会占据一块内存空间。

在平时设计代码是如果明确不需要使用虚函数,那就不要随便使用:
1 主要原因是,虚指针调用虚函数是在程序运行时进行的,所以需要通过寻址操作才能找到真正应该调用的函数,而普通成员函数是在编译时就确定了调用的函数,不需要寻址操作,因此在效率上,虚函数的效率要低很多。
2 其次,因为他会在虚函数表中多占据一块内存空间,浪费空间。

<think>好的,我现在需要解决用户关于C++中派生类对象虚函数表指针(vptr)是否覆盖基类vptr的问题。首先,我得回忆一下C++的虚函数机制。根据之前的引用内容,每个含有虚函数的类都有一个虚表(vtable),对象则包含一个指向该表的vptr。当派生类继承基类时,如何处理这两个vptr呢?用户的问题核心是派生类的vptr是否会覆盖基类的vptr。根据引用[4],基类的vptr位于数据成员的最前面,而派生类如果也有虚函数,是否会有自己的vptr?或者是否替换了基类的vptr?比如,当派生类覆盖基类的虚函数时,它的vtable中的函数地址会被更新,但vptr本身的数量呢?根据引用[1]和[3],每个对象的内存布局中有一个vptr指向其类的vtable。当派生类继承基类时,如果基类已经有vptr,那么派生类可能继承这个vptr,但会指向自己的vtable。也就是说,派生类对象可能只有一个vptr,该vptr在构造时被初始化为指向派生类的vtable,从而覆盖了基类原有的vptr。这样,在内存布局中,基类的vptr位置被派生类的vptr取代,而不是存在多个vptr。例如,引用[4]提到基类有vfptr,派生类可能同样有一个vfptr,但指向自己的vtable,而不是基类的。另外,引用[5]说明调用虚函数时,通过对象的vptr找到对应的vtable。如果派生类对象有多个vptr的话,比如多继承的情况,但用户的问题可能是在单继承的情况下。单继承时,通常派生类只有一个vptr,覆盖了基类的vptr,指向自己的虚表。而如果是多继承,可能会有多个vptr,每个对应一个基类。但用户的问题可能仅指单继承的情况。 总结来说,在单继承的情况下,派生类对象会有一个vptr,替换了基类的vptr,指向派生类的vtable。因此,基类的vptr被覆盖,而不是存在多个。这符合内存布局的描述,即vptr位于对象的最前面,派生类在构造时会初始化自己的vptr,替换掉基类部分原有的vptr。因此,用户的问题的答案应该是肯定的:派生类的vptr覆盖了基类的vptr,使得整个对象使用派生类的虚表。</think>在C++中,派生类对象的虚函数表指针(vptr)**会覆盖基类的vptr**,这是实现多态性的核心机制。以下是具体分析: --- ### **内存布局与vptr的覆盖机制** 1. **基类对象的内存布局** 当基类包含虚函数时,其对象内存布局的起始位置会插入一个vptr,指向基类的虚表(vtable)。例如: ```cpp class Base { virtual void func() {} int data; }; ``` 内存结构为:`[vptr][data]`,vptr指向`Base::vtable`。 2. **派生类对象的内存布局** 派生类继承基类时,会继承基类的数据成员和vptr。但如果派生类覆盖了基类的虚函数或新增虚函数,编译器会为派生类生成一个新的虚表(`Derived::vtable`),并**替换基类的vptr**,使其指向派生类的虚表。例如: ```cpp class Derived : public Base { void func() override {} int derived_data; }; ``` 内存结构为:`[vptr][data][derived_data]`,其中vptr指向`Derived::vtable`[^1][^4]。 **关键点**: - 派生类对象**仅有一个vptr**(单继承场景),该vptr覆盖基类的vptr,指向自己的虚表[^1][^3]。 - 虚表的生成是**按类层级逐层覆盖**的。例如,若派生类未覆盖虚函数,其虚表可能与基类相同,但vptr仍指向派生类的虚表。 --- ### **验证vptr覆盖的示例** 通过以下代码可以验证vptr的覆盖行为: ```cpp #include <iostream> class Base { public: virtual void func() { std::cout << "Base::func" << std::endl; } int base_data = 1; }; class Derived : public Base { public: void func() override { std::cout << "Derived::func" << std::endl; } int derived_data = 2; }; int main() { Base base; Derived derived; Base* ptr = &derived; ptr->func(); // 输出 "Derived::func",证明vptr指向派生类虚表 return 0; } ``` 输出结果为`Derived::func`,说明通过基类指针调用虚函数时,实际访问的是派生类的虚表[^5]。 --- ### **多继承场景的扩展** 在多继承中,派生类可能包含**多个vptr**(每个基类对应一个vptr)。例如: ```cpp class Base1 { virtual void f1() {} }; class Base2 { virtual void f2() {} }; class Derived : public Base1, public Base2 { void f1() override {} void f2() override {} }; ``` 此时,`Derived`对象的内存布局包含两个vptr,分别对应`Base1`和`Base2`的虚表,且均被派生类的虚表覆盖[^4]。 --- ### **总结** - **单继承**:派生类对象仅有一个vptr,覆盖基类的vptr,指向自己的虚表。 - **多继承**:派生类可能包含多个vptr,每个对应不同基类,均被派生类的虚表覆盖。 - **vptr替换时机**:在对象构造时,编译器会依次初始化vptr,最终派生类的构造函数将vptr设置为指向自己的虚表[^1][^3]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值