【问题标题】:The size of base class object and derived class object in C++C++中基类对象和派生类对象的大小
【发布时间】:2014-11-19 02:16:46
【问题描述】:
#include <stdio.h>

class Base1
{
  public:

  virtual int virt1() { return 100; }

  int data1;
};

class Derived : public Base1
{
  public:

  virtual int virt1() { return 150; }

  int derivedData;
};

int Global1( Base1 * b1 )
{
  return b1->virt1();
}

main1()
{
  Derived * d = new Derived;

  printf( "%d %d\n", d->virt1(), Global1( d ));
  printf("size: Base1:%d\n", sizeof(Base1));
  printf("size: Derived:%d\n", sizeof(Derived));
}

我用上面的代码打印出基类和派生类的大小。我在 64 位机器上运行代码。我电脑的输出是

150 150
size: Base1:16
size: Derived:16

我还尝试使用 sizeof(int) 打印出 int 的大小,它是 4。

我有以下问题:

  1. 对于 Base1 类的大小,它应该包含一个 vptr 指向 vtable 和一个整数数据1。为什么它的大小是 16 而 sizeof(int) 在我的机器上是 4。
  2. 对于派生类,它应该具有从 Base1 类继承的数据和一个额外的整数。它应该有更大的尺寸 比 Base1 类,为什么它们是一样的?
  3. 除了这些大小之外,Derived 类中的 vptr 是什么? Derived 类中的 vptr 是否会覆盖从 Base1 继承的 vptr 上课?

【问题讨论】:

标签: c++ inheritance


【解决方案1】:

对于Base1类的大小,它应该包含一个指向vtable的vptr和一个整数data1。为什么它的大小是 16 而 sizeof(int) 在我的机器上是 4。

Base1s 在一个必须连续的†数组中时,填充以确保 8 字节指针正确对齐。

† C++ 标准要求数组元素在内存中是连续的,并计算索引 i 处元素的内存地址和数组的基地址加上 i 乘以元素大小

对于派生类,它应该具有从 Base1 类继承的数据和一个额外的整数。它的大小应该比 Base1 类大,为什么它们是一样的?

已为额外的 int 成员回收填充。您可以通过输出d&amp;d-&gt;data1&amp;d-&gt;derivedData 来观察这一点。

除了这些大小之外,Derived 类中的 vptr 是什么? Derived 类中的 vptr 会覆盖从 Base1 类继承的 vptr 吗?

在使用指向虚拟调度表的指针的实现中(我知道的每个编译器,但它不是 C++ 标准的强制要求),Derived 类构造函数和析构函数会覆盖“vptr” - 前者写入一个指针到 Derived 的 VDT after 构造函数体运行,后者恢复指向 Base VDT before 析构函数体运行的指针(这确保你不会在对象的正式生命周期之前或之后调用 Derived 成员函数。

【讨论】:

  • 这种情况下指针的大小是多少?为什么是8字节?这取决于编译器还是机器?
  • @CaptainRib 它是 8 个字节,因为您正在编译 64 位,并且每字节 8 位是 8 个字节。是否可以运行 64 位操作系统取决于硬件,如果操作系统和硬件都支持 64 位,您通常可以选择是编译 32 位应用程序(带有 4 字节指针)还是 64 位应用程序。你的编译器应该有命令行参数来在这些目标之间进行选择。请注意,在 64 位应用程序中使用 32 位库可能会受到限制,反之亦然,并检查您的编译器/系统/库文档。
  • @TonyD: offsetof 不能与有问题的类型一起使用(行为未定义,因为类型不是标准布局。类似的测试可以通过打印d&amp;d-&gt;data1&amp;d-&gt;data2
  • @DavidRodríguez-dribeas: true 'tis undefined... 我会相应地编辑我的答案。干杯。
  • @TonyD 您确定将为派生类对象中的 etrax 数据成员 int 回收填充吗?如果是这样,如果将对象复制到另一个对象,比如另一个派生对象,会有什么问题吗?据另一位朋友说,他说派生的大小应该是 24,因为它是 16+4+padding。
【解决方案2】:

在 C++ 中,有许多因素决定了一个类的对象的大小。

这些因素是:

  • 所有非静态数据成员的大小
  • 数据成员的顺序
  • 字节对齐或字节填充
  • 其直接基类的大小
  • 虚函数的存在(使用虚函数的动态多态性)。
  • 正在使用的编译器
  • 继承方式(虚拟继承)

当我在 MVSC 2010 上编译您的代码时,x64 位。这是内存布局的结果:

class Base1 size(16):
    +---
 0  | {vfptr}
 8  | data1
    | <alignment member> (size=4)
    +---

Base1::$vftable@:
    | &Base1_meta
    |  0
0   | &Base1::virt1

Base1::virt1 这个调节器:0

   class Derived    size(24):
    +---
    | +--- (base class Base1)
0   | | {vfptr}
8   | | data1
    | | <alignment member> (size=4)
    | +---
16  | derivedData
    | <alignment member> (size=4)
    +---

Derived::$vftable@:
    | &Derived_meta
    |  0
 0  | &Derived::virt1

您应该阅读有关 vTable、虚函数、类/结构的字节对齐的更多信息。 在这里,一些链接可以帮助您。希望对你有帮助:

确定类对象的大小:http://www.cprogramming.com/tutorial/size_of_class_object.html

内存布局:http://www.phpcompiler.org/articles/virtualinheritance.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多