C ++中虚拟表的虚拟指针(VPTR)的大小是多少?这也不是一个家庭作业问题......只是我在阅读 C++ 书籍时想到的一个问题。
6 回答
一篇与此主题相关的优秀文章是Member Function Pointers and the Fastest Possible C++ Delegates。本文深入探讨了许多不同编译器的成员函数指针的实现。本文讨论了 vtable 指针的所有细微差别,特别是考虑到多重(和虚拟)继承。
请注意,为了优雅地处理多重继承,一个对象中可以有多个 VPTR,但通常每个都可能是一个简单的体系结构相关指针。
试着运行这样的东西来看看你的编译器是如何布置的:
#include <iostream>
using namespace std;
struct Base
{
int B;
virtual ~Base() {}
};
struct Base2
{
int B2;
virtual ~Base2() {}
};
struct Derived : public Base, public Base2
{
int D;
};
int main(int argc, char* argv[])
{
cout << "Base:" << sizeof (Base) << endl;
cout << "Base2:" << sizeof (Base2) << endl;
cout << "Derived:" << sizeof (Derived) << endl;
Derived *d = new Derived();
cout << d << endl;
cout << static_cast<Base*>(d) << endl;
cout << &(d->B) << endl;
cout << static_cast<Base2*>(d) << endl;
cout << &(d->B2) << endl;
cout << &(d->D) << endl;
delete d;
return 0;
}
在我的 32 位编译器上,这为两个基类提供了 8 个字节,为派生类提供了 20 个字节(当编译为 64 位时,这些值加倍):
4 bytes Derived/Base VPTR
4 bytes int B
4 bytes Derived/Base2 VPTR
4 bytes int B2
4 bytes int D
您可以通过查看前 8 个字节了解如何将 Derived 视为 Base,以及如何通过查看后 8 个字节将其视为 Base2。
这取决于您的实施,但很容易找到。对于这个程序
#include <iostream>
struct virtual_base {
int data;
virtual_base() {}
virtual ~virtual_base() {}
};
struct non_virtual_base {
int data;
non_virtual_base() {}
~non_virtual_base() {}
};
int main() {
std::cout << sizeof( virtual_base ) - sizeof( non_virtual_base ) << '\n';
return 0;
}
mine (VC 2008) 将打印 4,因此在这种情况下,多态性的成本是 4 字节。
可能与普通指针大小相同……在 32 位机器上通常为 4 个字节。但这将取决于编译器,一些编译器可能会做不同的事情。
很可能是任何其他指针的大小。尝试这样的事情来找出你的编译器和机器:
#include <iostream>
struct base {
base() {}
virtual ~base() {}
};
int main( int argc, char **argv ) {
std::cout << sizeof( base ) << std::endl;
}
虚函数表中的指针一般与系统中的常规指针大小相同。通常为每种类型计算一个虚函数表,并且每个对象实例将包含一个指向其类型表的指针,因此包含虚函数的对象sizeof(void *)
实例将比不包含虚函数的对象实例使用更多的字节。从多个基类型派生的类型必须可转换为任何基类型,因此可以根据需要包含指向基类型的虚函数表的多个指针。当然,所有这些都取决于编译器。