0

我在 C++ 中有两个类:

class Base
{
  int baseField;
 public:
  Base();
  ~Base();
  T BaseMethod();
  virtual SomeMethod()=0;
};

class Derived : public Base
{
  int derivedField;
 public:
  Derived()
  ~Derived();
  T DerivedMethod();
  virtual SomeMethod() {...}; // Some implementation
};

我的问题是它是如何在内存中表示的,即这个指针、vptr(vtable)字段和这个类的方法指针在哪里。好的,它依赖于编译器,但有一些不成文的标准。我对 Visual Studio 和 gcc 编译器最感兴趣。

[编辑:我放了一种虚拟方法来改进示例,之前没有任何虚拟方法]

有没有一些关于这个主题的书详细写

4

4 回答 4

11

您的类没有虚表指针,因为它们没有虚函数。

就像你说的那样,即使他们有,C++ 也没有指定如何实现虚函数——每个编译器都可以以不同的方式实现,不需要 vtable 的存在。

在实践中,理智的实现(对于单继承)是在类的开头有一个虚拟表指针,在它之后是普通的类成员。没有“方法指针”之类的东西——普通的成员函数在编译时被解析,而虚函数的指针按照它们的声明顺序在 vtable 中。

这是一个示例布局:

struct Base {               + Base ------------+     + Base vtable ----+
    virtual void foo();     | * vptr        ---|---> | * void (*foo)() |
    T bases_field;          | * base_field     |     +-----------------+
};                          +------------------+

struct Derived {            + Derived ---------+     + Derived vtable -+
    T derived_field;        | * CBase instance |---> | * void (*foo)() |
};                          | * derived_field  |     +-----------------+
                            +------------------+

(不按内存中占用的字节进行缩放。)

因此,每个实例都包含一个指向虚拟表的指针。每个类只有一个虚拟表。即一个在所有实例之间CBase共享,一个在所有实例之间共享CDerived

于 2013-07-16T16:29:10.400 回答
2

如果该类包含至少一个虚函数(您的没有)并且不使用多重继承(这变得复杂),则该类通常包含一个隐藏指针,指向指向虚函数的函数指针数组。该数组称为虚函数表,并在类的所有实例之间共享。这些字段的布局方式与 C 结构相同。您提供的功能不存储在类中。链接器知道它们的地址,链接器在调用函数时插入调用地址。this 指针不存储在类中。所有非静态类函数都将 this 指针作为隐藏参数。这被类上调用的函数压入堆栈,该函数必须知道对象的地址。

于 2013-07-16T16:32:49.653 回答
1
  • The this pointer is not stored. It is used to find the current object but there is no need to store it in the object.

  • Since you don't have any virtual method, there wont be any vptr table.

So in your example, you'll get something which is similar to a C struct with two fields.

于 2013-07-16T16:30:55.017 回答
1

好的,它依赖于编译器,但有一些不成文的标准。我对 Visual Studio 和 gcc 编译器最感兴趣。

它根本不是不成文。它也不是真正的编译器依赖。它依赖于 ABI。那是另一回事。

MSVC 几乎是唯一一个遵循没有真正在外部记录的约定的人,即使那样,您也可以询问 Clang 或 MinGW 开发人员。

GCC 在大多数系统上都遵循 Itanium ABI,我相信 ARM 也有指定的 ABI。这些文件清楚地详细说明了正在发生的事情。

于 2013-07-16T16:59:21.350 回答