【问题标题】:virtual pointer size varies based on class data members虚拟指针大小因类数据成员而异
【发布时间】:2018-09-01 21:19:14
【问题描述】:

正如该问题的解决方案之一 (Size of virtual pointer-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

【问题讨论】:

  • 好的,那么以编程方式计算 vptr 大小的可靠方式是什么
  • 问题是没有数据的非虚拟类的大小为1,即使它实际上没有任何东西,因为大小永远不会是0。所以sizeof(non_virtual_base)并不是真的显示结构使用了多少空间。
  • 还要注意对齐问题:如果 vTable 不完全是 N*64 位长,编译器可能会在成员之间添加一些填充空间。
  • 但是一个空的虚拟类不需要在大小上加1,因为VPTR使用空间(它就像一个额外的数据成员)。这就是为什么您需要使用具有至少 1 个数据成员的类才能正常工作。
  • @PhilipV vtable 不在每个对象中,它是每个类的数据结构。该对象只包含一个指向它的指针。

标签: c++ virtual


【解决方案1】:

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

当涉及数据成员时,您获得的结果取决于成员的实际类型。如果您使用int,那么区别是12,因为vptr 必须与8 的倍数对齐。 这意味着 int 数据成员占用从 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

【讨论】:

  • 那么有没有可靠的方法来以编程方式获取 vptr 的大小
  • 那么为什么这个问题stackoverflow.com/questions/1604176/size-of-virtual-pointer-c 如果它只是等于指针大小
  • 我可以安全地假设 sizeof(void*) 与 vptr 的大小相同
  • @Kapil 他们实际上要问的是,当对象具有虚拟指针时,它使用了多少额外空间。由于对齐,开销可能不仅仅是指针的大小。为什么要关心指针大小本身?
【解决方案2】:

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 |

请查看this。这可能会有所帮助。

使用int 数据成员:

  • sizeof(virtual_base) is 16 [4(int)+ 4(padding) +8(vftable)] 字节
  • sizeof(non_virtual_base) 是 4 个字节,即 int 的大小。

没有int 数据成员:

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

【讨论】:

    【解决方案3】:

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-14
      • 2021-10-31
      • 1970-01-01
      • 2018-03-18
      相关资源
      最近更新 更多