虚拟指针大小因类数据成员而异

virtual pointer size varies based on class data members

本文关键字:数据成员 指针 虚拟      更新时间:2023-10-16

正如这个问题的解决方案之一(虚拟指针C++的大小)中所解释的那样,您可以通过以下方式计算虚拟指针大小:

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;
}

但是当我在 cpp.sh http://cpp.sh/7o5av 上尝试这样做时,如果没有数据(成员变量),我得到的大小为 7,数据大小为 12,所以我无法理解这种行为,任何见解都会有所帮助,我知道空类的大小是 1,其次是数据成员,我希望这应该是 11 而不是 7

您得到没有数据成员的7,因为空类的大小1,而虚拟类包含指向大小为 8:8-1=7的虚拟表的指针。

当涉及数据成员时,您获得的结果取决于成员的实际类型。如果您使用int则差异12,因为 vptr 必须与8的倍数对齐。 这意味着 int 数据成员占用字节 frm 0 到 4,并且 vptr 不能存储在字节4处,而是从字节 8 开始存储。因此,虚拟结构的总大小8+8=16。尝试使用双精度,您将看到差异8,如以下代码所示。

#include <iostream>
using namespace std;
struct virtual_base {
double data;
virtual_base() {}
virtual ~virtual_base() {}
};
struct non_virtual_base {
double data;
non_virtual_base() {}
~non_virtual_base() {}
};
int main() {
std::cout << sizeof( virtual_base ) - sizeof( non_virtual_base ) << 'n';
return 0;
} 

在这里尝试: https://www.ideone.com/Ycpg64

virtual_base只包含vftable指针,在您的平台上显然是8个字节。virtual_base也有一个 int,vftable对齐为 8 个字节。所以它是这样的:

4 bytes for int |  4 padding bytes  |  8 bytes for vftable pointer  | 
| x | x | x | x |    |    |    |    | v | v | v | v | v | v | v | v |

请看一下这个。这可能会有所帮助。

对于int数据成员:

  • sizeof(virtual_base) is16 [4(整数)+ 4(填充) +8(vftable)] 字节
  • sizeof(non_virtual_base)是 4 个字节,即int的大小。

如果没有int数据成员:

  • sizeof(virtual_base) is8 个字节,即 vftable 指针的大小。没有填充。
  • 没有任何数据成员sizeof(non_virtual_base)为 1 个字节。

当您在C++中将任何函数声明为virtual时,该类会收到一个隐藏成员vptr,该成员指向vtable。这用于选择在使用动态多态性时实际应调用哪个函数。

免责声明:我将使用您发布的在线编译器的结果,以及您设置的选项(C++14,O2 优化)

我们可以很容易地看到空类的sizeof等于 1,sizeof具有虚函数的类是 8。
然后,对于数据成员,没有虚函数的类得到sizeof4,有虚函数的类得到sizeof16。
我们还可以检查sizeof指针类型(例如int*)是否等于8

所以,这里会发生什么:默认情况下,空类被分配为大小 1。但是,具有虚函数的类必须具有vptr成员,该成员本身的长度为 8 个字节。这给你 8-1 = 7

当您将成员添加到非虚拟类时,它只是获取其所有成员的大小,在本例中它是 int,因此您的大小为 4。
对于虚拟类,您已经有大小为 8 的vptr,并向其添加大小为 4 的 int。在这里,结构对齐的机制开始发挥作用。据推测,它正在编译的系统只允许访问偏移量为 8 的字节,因此编译器为了优化访问时间,添加了人工填充字节,这些字节不保存任何数据。基本上,类的对象如下所示:
[int(4B)|padding(4B)|vptr(8B)]
这导致类的大小为 16。所以我们得到 16 - 4 = 12。