【问题标题】:Why in C++ the size of a class must be always known by its users?为什么在 C++ 中,类的大小必须始终为用户所知?
【发布时间】:2016-02-07 11:19:09
【问题描述】:

假设一个类完全定义在它的.cpp文件中,所以在源文件中你可以找到:

  • 构造函数定义
  • 析构函数定义
  • 定义的每个方法

那为什么它的私有成员变量还必须在header文件中呢? 为什么我们仍然需要 PIMPL 来摆脱它们?

如果对于这个类我也在源文件中定义了自己的new operator,为什么我还需要从外部代码中知道大小?

是因为类仍然可以堆栈分配吗? 如果是这样,那么为什么在堆栈上分配的“函数”不是 .cpp 文件内的构造函数调用的一部分?

【问题讨论】:

  • 如果您不知道需要多少位,并且无法找出答案,您将如何从一组位中创建一个对象?
  • 顺便说一句:您并不总是需要知道对象的大小。例如。每当您使用前向声明时,大小仍然未知,但您仍然可以使用该类的对象。
  • @Peter 使用在其他源文件中定义的链接函数,它只是返回给我一个指针
  • 这还不够,nyarlathotep。要返回指向对象的指针,该“链接函数”还需要从一组位中创建一个对象。
  • tobi - 前向声明只允许创建指针和引用。仅使用指向(如果有)或引用的对象是不够的。例如,some_pointer->some_member_function() 将生成编译错误,除非实际类型定义(不仅仅是前向声明)对编译器可见。

标签: c++ constructor destructor new-operator pimpl-idiom


【解决方案1】:

那为什么它的私有成员变量还必须在头文件中呢?为什么我们仍然需要 PIMPL 来摆脱它们?

因为对于许多操作(在看到定义后允许的操作),编译器需要知道对象实例的大小。详情如下。

如果对于这个类,我也在源文件中定义了自己的新操作符,为什么我还需要从外部代码中知道大小?

是因为类仍然可以堆栈分配吗?如果是这样,那么为什么在堆栈上分配的“函数”不是 .cpp 文件内的构造函数调用的一部分?

部分。编译器在函数调用开始时将堆栈指针移动局部变量的总大小是最简单和最有效的,然后在返回时将其移回。该大小通常可以在编译时计算。如果您有返回单个对象大小的运行时函数,那么编译器将需要处理点点滴滴的堆栈指针增量,并在运行时重复计算特定对象的地址作为早期分配的累积总数,或者使用内存/寄存器来维护一组指向它们最终位置的指针或偏移量。 (这是大多数 C++ 编译器不支持数组维度的运行时规范的主要原因之一。)

我说“部分”是因为它不仅仅与堆栈有关:类似的问题也适用于静态/全局和线程本地对象。

【讨论】:

  • 我明白了......基本上它只是关于性能与信息隐藏(所以如果我真的想隐藏私有成员变量,我必须使用 PIMPL 习惯用法和堆分配手动完成)?跨度>
  • 另一个原因是继承。如果B 继承自A,则B 必须知道它必须为继承的A 保留多少空间。
  • @nyarlathotep108:差不多大小,是的。还有其他选择——比如在某个地方管理你自己的静态对象池而不是使用堆——但它们不会随意扩展,是浪费的,和/或变得比它们通常的价值更复杂。或者偶尔你会看到有人在对象中保留了“不透明”空间,例如 private: double impl_[20];,他们会做一些 hacky 的强制转换或新的放置以在该内存中创建他们真正想要的任何内容,以及静态断言以确保 impl_ 足够大。 (double 确保 8 字节对齐,如果您想知道为什么不char)。
  • @nyarlathotep108:如果您的直觉是“double impl_[];?哦,是的!”,让我说除非您了解“混叠”和未定义行为的相关风险,否则不要尝试。
  • @el.pescado: 好点...!周围有很多微妙的问题,例如如果在定义中不需要私有细节,他们想要指向他们不会知道存在的私有基础的指针。
猜你喜欢
  • 2019-01-16
  • 2015-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-08
  • 2022-10-24
  • 2021-12-20
相关资源
最近更新 更多