假设有一个名为Person的类,它包含一个名为age()的虚函数。根据语言语义,vtable是每个类而不是每个对象。它是VPTR,它是每个对象并指向vtable。
问题:
如果我构建这个程序(假设存在main()):
是否会创建vtable,即使创建单个对象,vtable是否存在?
编译器在vtable中为age()放置的地址是内存中的一种静态地址吗?
或者是编译器在内部创建一些对象以获取age()的地址(因为age()将处理一些数据成员,这些成员只有在构造了一个对象时才会存在)或者这背后还有其他一些魔力吗?
根据我的理解,答案如下:
我尝试在上面的程序中运行“nm”只是为了看看我是否可以找出vtable,但没有运气。有办法吗?
请建议。
答案 0 :(得分:3)
由于它是所有实现定义的,我的回答描述了一些“常见实现”
v-table与机器代码本身一样存储在可执行文件中,并由OS加载程序加载到内存中。操作系统不关心要加载的数据:字符串文字,机器代码,vtable,常量数据等......
假设你有:
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和虚拟继承,您必须做更复杂的事情。
我不明白你的意思,但没有。
答案 1 :(得分:1)
所有这些的答案完全依赖于编译器。不要求物理vtbl
存在;这只是实现该语言的一种非常常见的方式。但是对于任何这一点都没有通用的ABI,而且作为开发人员,你不应该担心这个问题。
答案 2 :(得分:1)
这完全取决于编译器,你应该把整个答案作为一种方式来完成:
是否会创建vtable,即使创建单个对象,vtable是否存在?
取决于编译器和程序。例如,GCC将在翻译单元中创建vtable,其中定义了类定义中未定义的第一个虚函数(是否创建了任何对象),但在某些情况下可能根本不会生成vtable或者即使没有创建对象也可以生成一个。
编译器在vtable中为age()放置的地址是内存中的一种静态地址吗?
这通常由链接器/加载器解决。链接程序时,链接器通常会解析函数的(相对)地址,并将其作为第一步注入vtable。当程序加载到内存中时,这些地址被固定到函数所在的内存地址(这取决于加载程序可能因执行而异)。
或者是编译器在内部创建一些对象以获取age()的地址(因为age()将处理一些数据成员,这些成员只有在构造了一个对象时才会存在)或者还有其他一些魔法在这背后?
我不太关注整个问题。只有当您的程序请求时,编译器才会创建任何具有成员age
的类型的对象。成员函数可能访问/修改给定类型的对象的数据成员,但是通过传递给所有非静态成员的隐式this
指针来处理对这些成员的访问。
答案 3 :(得分:0)
“根据语言语义,”没有vtable这样的东西。 C ++规范没有详细说明虚拟调度的实现方式。编译器可以使用vtable。或者它可以使用别的东西;这取决于编译器。
特定的编译器当然可以使用vtable。但它可以随心所欲地做任何事情;这是一个实现细节。简而言之,如果不调查您正在使用的特定编译器,就无法知道。
真的,这有多重要吗?