C++中虚表指针的存放位置

本文通过一个简单的C++示例程序展示了虚函数表指针(Vptr)在对象内存布局中的实际位置,并纠正了一个常见的误解,即Vptr并非位于对象的末尾而是位于对象的起始位置。

以前一直以为C++为了兼容C而把虚函数表指针Vptr放在了对象末尾,而且在《inside the C++ object model》里面也是这么描述的。但是我错了,今天愕然发现不是这样的。而且无论是在g++编译器还是在ms的编译器上都不是这样的!

#include <iostream>
#include <cstdlib>

using namespace std;

#ifndef BYTE
#define BYTE char
#endif

#define myMain main

class A{
private:
    int _a;
    int _b;
public:
    A(int a=0,int b=0):_a(a),_b(b){}
    virtual ~A(){cout<<"A destructor"<<endl;}
    friend ostream &operator<<(ostream& out , const A &a){out<<a._a;return out;}
};

class B : public A{
private:
    int b;
public:
    virtual ~B(){cout<<"B destructor"<<endl;}
};

int myMain(int argc , char *argv[])
{
    A a(12,13);
    cout<<a<<endl;
    int *b=reinterpret_cast<int*>(&a);
    cout<<*b<<endl;
    b++;
    cout<<*b<<endl;
    b++;
    cout<<*b<<endl;
    system("pause");
    return EXIT_SUCCESS;
}

程序的输出为:

12
4463692
12
13

显然上面的为虚表指针的值,下面为成员变量的值,这说明C++中虚表指针是放在对象的开头的!!!


### C++虚表存放位置与内存布局 在 C++ 中,虚函数机制通过虚表(vtable)和虚指针(vptr)实现。以下是对虚表存放位置及内存布局的详细解析。 #### 虚表与虚指针的基本概念 虚表是一个由编译器生成的函数指针数组,存储了中所有虚函数的地址。每个包含虚函数都有一个对应的虚表,而每个对象则持有一个指向该虚表指针,称为虚指针(vptr)。在运行时,通过虚指针可以动态调用正确的虚函数实现[^1]。 #### 虚指针位置指针的具体位置因编译器而异: - 在 Visual Studio 的编译器中,虚指针通常被放置在对象内存布局的起始位置(即 0 地址偏移处)。这意味着对象的首地址实际上是指向虚表指针[^2]。 - 对于其他编译器(如 GCC 或 Clang),虽然标准未强制规定虚指针必须位于对象的起始位置,但大多数实现也遵循似的策略,即将虚指针放在对象的开头。 #### 内存布局示例 以下是一个简单的定义及其内存布局分析: ```cpp class Base { public: virtual void f() {} int a; double b; }; ``` 假设 `sizeof(void*)` 为 4 字节(32 位系统),`sizeof(int)` 为 4 字节,`sizeof(double)` 为 8 字节,则 `Base` 的对象内存布局如下: - **虚指针(vptr)**:占用 4 字节,位于对象的起始位置。 - **成员变量 `a`**:占用 4 字节,紧跟在虚指针之后。 - **成员变量 `b`**:占用 8 字节,位于 `a` 之后。 总大小为 `4 + 4 + 8 = 16` 字节(可能因对齐规则有所同)[^1]。 #### 示例代码验证虚指针位置 以下代码演示如何通过指针操作验证虚指针位置: ```cpp #include <iostream> using namespace std; class Base { public: virtual void f() {} int a; double b; }; int main() { Base obj; char* pObj = reinterpret_cast<char*>(&obj); // 对象首地址 void (**vptr)() = *reinterpret_cast<void *(**) *>(&obj); // 获取虚指针 cout << "Object address: " << hex << &obj << endl; cout << "Vptr address: " << (void*)pObj << endl; cout << "Vtable address: " << vptr << endl; return 0; } ``` 运行结果表明,虚指针位于对象的起始位置,且其值指向虚表的地址[^2]。 #### 注意事项 - 虚表本身是由编译器管理的静态数据结构,随对象的创建而改变。 - 每个派生实例的虚指针会指向其自身的虚表,从而实现多态性。 - 标准并未明确规定虚指针位置,因此同编译器可能有同的实现方式。 ### 总结 C++虚表存放位置主要依赖于编译器实现。以 Visual Studio 为例,虚指针通常位于对象内存布局的起始位置,随后是成员变量。这种设计使得虚函数调用高效且易于实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值