虚拟方法表在C++中存储在哪里

Where does virtual method table store in C++?

本文关键字:存储 在哪里 C++ 方法 虚拟      更新时间:2023-10-16

我想知道类对象(不是实例,而是类)是如何存储在内存中的?

class A {
public:
    int a;
    virtual void f();
    virtual ~A();
};
class B : public A {
public:
    int b;
    void f() final override;
};

我知道,在这种继承(B源自A)的情况下,通常(标准中没有强烈描述)我们有:

memory: ....AB...

其中AB是B的类对象(如果我理解正确的话)。如果我们更深入(尝试使用clanggcc),我们可以看到类似的东西(同样,在标准中没有强烈描述):

A
    vtptr*
    int a
B
    vtptr*
    int b

好的,现在我们看看ab属性存储在哪里。我们还看到了指向虚拟方法表的指针。但是vtptr*(虚拟方法表)实际存储在哪里?为什么不去上课呢?还是真的?

此外,还有一个问题:我能够通过更改指针(简单逻辑)来更改虚拟方法表。我还可以安全地更改指向其方法的指针吗?

附言:在你的问题中,你可以回答gcc和clang。附言:如果我哪里错了,请在你的答案中也指出。

C++标准没有规定虚拟函数机制应该如何实现。在实践中,所有C++实现都为每个类使用一个虚拟函数表,并在具有虚拟函数的类(称为多态类)的每个对象中使用虚拟函数表指针。然而,细节可能会有所不同,特别是对于多重继承和虚拟继承。

您可以在Stanley Lippman的经典著作《C++对象模型内幕》中了解常见的选择。

问";其中;存储虚拟功能表。它很像任何静态变量:它的位置取决于实现,并且几乎是任意的。关于

"为什么不去上课呢?

…类本身没有存储在任何地方,它们不是对象,所以这没有意义,对不起。

对于给定的实现,您可以更有意义地问每个对象中存储的vtable指针在哪里?

通常是在对象的开头,但如果你从一个非多态类派生,并添加一个虚拟函数,那么你可能会在其他地方获得vtable指针。或者不是。后一种可能性是Derived*Base*static_cast(反之亦然)可以进行地址调整的主要原因,即不同于简单的reinterpret_cast

阅读关于虚拟方法表的wiki页面。

存储的vtable(本身)在哪里是特定于实现的(编译器、链接器、操作系统特定)。但它通常存储在可执行文件的代码段中(就像文字字符串一样)。因此,对象通常(即没有多重继承)以指向其vtable的_vptr指针开始。使用多个或虚拟继承,您可以拥有多个vtable指针。

正如所评论的,你不应该关心这些细节。如果您真的很关心,请让编译器转储内部表示或发出的程序集代码。(例如使用g++ -fdump-tree-all -fverbose-asm -S编译)

但是vtptr*(虚拟方法表)实际上在哪里[点]?为什么不去上课呢?还是真的?

它可能在任何地方。。。谁在乎呢?它的工作方式通常是这样的。。。假设class A:有一个隐藏的static成员

VDT A::vdt = {
    { address of A::f code, 
      address of A::~A code },
    miscellaneous type-specific information needed for dynamic cast etc.
};

确切的布局是未知的,但很可能存在virtual成员函数的地址数组。与任何static信息一样,地址与任何给定对象实例的地址无关。。。对象中指向虚拟调度表的指针允许这种解耦。

此外,还有一个问题:我能够通过更改指针(简单逻辑)来更改虚拟方法表。我还可以安全地更改指向其方法的指针吗?

这是不安全的,即使它表面上有效,有时也可能不会得到一致的遵守(例如,在编译器能够在编译时确定要调用的特定覆盖的情况下,它可能会完全绕过运行时虚拟调度表咨询)。