vtable中虚函数的地址

时间:2011-12-30 19:04:40

标签: c++

假设有一个名为Person的类,它包含一个名为age()的虚函数。根据语言语义,vtable是每个类而不是每个对象。它是VPTR,它是每个对象并指向vtable。

问题:

如果我构建这个程序(假设存在main()):

  1. 是否会创建vtable,即使创建单个对象,vtable是否存在?

  2. 编译器在vtable中为age()放置的地址是内存中的一种静态地址吗?

  3. 或者是编译器在内部创建一些对象以获取age()的地址(因为age()将处理一些数据成员,这些成员只有在构造了一个对象时才会存在)或者这背后还有其他一些魔力吗?

  4. 根据我的理解,答案如下:

    1. 不确定
    2. 我尝试在上面的程序中运行“nm”只是为了看看我是否可以找出vtable,但没有运气。有办法吗?

      请建议。

4 个答案:

答案 0 :(得分:3)

由于它是所有实现定义的,我的回答描述了一些“常见实现”

  1. v-table与机器代码本身一样存储在可执行文件中,并由OS加载程序加载到内存中。操作系统不关心要加载的数据:字符串文字,机器代码,vtable,常量数据等......

  2. 假设你有:

    struct A {
        int x;
        virtual void f() { cout << x; }
    };
    
    void g(A* a) { a->f(); }
    

    生成的代码看起来(语义上)类似于:

    // pseudocode, not C++
    struct A {
        void *vtable;
        int x;
    };
    
    void A_f(A* this) { cout << this->x; }
    
    void* A_vtable[] = { &A_f };
    
    void g(A* a) { ((void(*)(A*))(((void**)a->vtable)[0]))(a); }
    

    是的,这是静态数据。

    当然上面的代码非常简单。要支持RTTI和虚拟继承,您必须做更复杂的事情。

  3. 我不明白你的意思,但没有。

答案 1 :(得分:1)

所有这些的答案完全依赖于编译器。不要求物理vtbl存在;这只是实现该语言的一种非常常见的方式。但是对于任何这一点都没有通用的ABI,而且作为开发人员,你不应该担心这个问题。

答案 2 :(得分:1)

这完全取决于编译器,你应该把整个答案作为一种方式来完成:

  

是否会创建vtable,即使创建单个对象,vtable是否存在?

取决于编译器和程序。例如,GCC将在翻译单元中创建vtable,其中定义了类定义中未定义的第一个虚函数(是否创建了任何对象),但在某些情况下可能根本不会生成vtable或者即使没有创建对象也可以生成一个。

  

编译器在vtable中为age()放置的地址是内存中的一种静态地址吗?

这通常由链接器/加载器解决。链接程序时,链接器通常会解析函数的(相对)地址,并将其作为第一步注入vtable。当程序加载到内存中时,这些地址被固定到函数所在的内存地址(这取决于加载程序可能因执行而异)。

  

或者是编译器在内部创建一些对象以获取age()的地址(因为age()将处理一些数据成员,这些成员只有在构造了一个对象时才会存在)或者还有其他一些魔法在这背后?

我不太关注整个问题。只有当您的程序请求时,编译器才会创建任何具有成员age的类型的对象。成员函数可能访问/修改给定类型的对象的数据成员,但是通过传递给所有非静态成员的隐式this指针来处理对这些成员的访问。

答案 3 :(得分:0)

“根据语言语义,”没有vtable这样的东西。 C ++规范没有详细说明虚拟调度的实现方式。编译器可以使用vtable。或者它可以使用别的东西;这取决于编译器。

特定的编译器当然可以使用vtable。但它可以随心所欲地做任何事情;这是一个实现细节。简而言之,如果不调查您正在使用的特定编译器,就无法知道。

真的,这有多重要吗?