【问题标题】:Why do destructors in C++ increase the size of the object they're in?为什么 C++ 中的析构函数会增加它们所在对象的大小?
【发布时间】:2014-07-11 18:45:36
【问题描述】:

我知道这听起来可能是一个奇怪的问题,但我只是想知道 C++ 中的类是否比具有相同数据字段的结构更重要,而这件事我找不到答案... 考虑一下:

struct SomeStruct {
    int a;
    int b;
};

class SomeClass {
public:
    SomeClass():a(0),b(0){}
private:
    int a;
    int b;
};

int main() {
    std::cout<<sizeof(SomeStruct)<<std::endl; // output is 8
    std::cout<<sizeof(SomeClass)<<std::endl; // output is 8
}

但是现在看看当我向 SomeClass 添加一个析构函数时会发生什么:

struct SomeStruct {
    int a;
    int b;
};

class SomeClass {
public:
    SomeClass():a(0),b(0){}
    virtual ~SomeClass(){}
private:
    int a;
    int b;
};

int main() {
    std::cout<<sizeof(SomeStruct)<<std::endl; // output is 8 bytes
    std::cout<<sizeof(SomeClass)<<std::endl; // output is 16 bytes!
}

为什么 SomeClass 的析构函数需要多 8 个字节?

【问题讨论】:

  • 您添加了一个需要指向虚拟表 (vtable) 的指针的虚拟函数。
  • 已经给出了正确的答案,但我想补充一点,你不必要地使析构函数虚拟化的事实看起来像是某些程序员显然认为的“所有析构函数必须是虚拟的”准则的应用程序10-20 年前是个好主意。这是一个非常糟糕的主意。
  • @Christian - 你能详细说明一下吗?为什么这是一个真的坏主意?
  • @ChristianHackl,Eclipse 自动为我生成了虚拟...
  • 在不需要析构函数的地方创建一个虚拟的析构函数可能很容易使对象的大小翻倍,并且它给程序员一个错误的印象,即该类是要派生的。它会造成混乱并破坏价值类和具有标识的类之间在设计方面的重要区别。这只是一个非常无用的指南,它试图解决经验丰富的程序员不存在的问题,并且只是初学者课程设计中实际问题的症状。

标签: c++ destructor


【解决方案1】:

大小增加是因为virtual。如果不将析构函数设为虚拟,则不会看到大小增加。

所以,不是析构函数让你的类型变大,而是添加了一个虚函数。

有问题的 8 个额外字节是指向您正在使用的类的虚拟表 (vtable) 的指针。如 cmets 所述,这是“一次性”成本。向类中添加一个虚函数会产生这种成本,但您不会看到添加虚函数的成本。

编辑:

类中的额外大小取决于它是编译为 32 位还是 64 位程序。虚拟表的链接在 32 位平台上需要 4 个额外字节,在 64 位平台上需要 8 个额外字节。

【讨论】:

  • 值得注意的是,如果您有任何虚拟功能,您只需支付一次此价格。之后剩下的就不用花钱了。
  • @so.very.tired 添加类的first虚函数会增加大小,因为指向vtable的指针将被添加到每个对象实例。
  • 我明白了... 那么为什么向类添加更多的虚函数并没有进一步增加它的大小呢?你不需要每个虚函数的指针吗?
  • 您认为其他虚函数也需要指针,您是对的。但是,这些指针对于您的类的每个实例都是相同的,因此实现只需要一个指针来链接到列出每个虚函数地址的指针表。总而言之 - 它是一个指向指针列表的指针。
  • @so.very.tired stackoverflow.com/questions/10065088/… 可能会有所帮助。 virtual 方法是类的一个属性,类实例只需要一个指向其类表的指针。
【解决方案2】:

这不是您添加了析构函数的事实。这是您添加了一个虚拟方法的事实(额外的虚拟方法不会产生额外的每个实例成本)。大多数(全部?)C++ 实现使用虚函数指针表来启用动态调度,这需要一个指向要存储在每个对象中的适当表的指针。这个指向虚拟表的指针是您看到的每个对象的额外空间。

【讨论】:

    【解决方案3】:

    当您将析构函数声明为虚拟时,编译器会自动在您的类中添加一个 vtable 指针作为成员。这是必要的,因此它可以找到派生自您的类的所有析构函数的地址。

    这个新成员(你不能轻易访问,你也不应该)被添加到你的类中,因此它的大小增加了一个普通原始指针的大小,它通常与 int 大小相同,但可能取决于你的架构。

    【讨论】:

    • 指针大小可能与 int 大小不同。因此,我认为你可以改进这句话。否则一个很好的答案。
    猜你喜欢
    • 2011-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-14
    • 2011-06-17
    • 1970-01-01
    • 2019-10-11
    相关资源
    最近更新 更多