【问题标题】:Is it possible to add a private member-variable without increasing the containing object's size?是否可以在不增加包含对象大小的情况下添加私有成员变量?
【发布时间】:2021-06-07 22:17:04
【问题描述】:

我有一个很小的实用程序类ObjectCounter,它没有虚拟方法和成员变量;它只包含一个构造函数和一个析构函数,它们分别递增和递减一个全局变量:

int _objectCount = 0;  // global

class ObjectCounter
{
public:
   ObjectCounter() {printf("DefaultCtor:  count=%i\n", ++_objectCount);}
   ~ObjectCounter() {printf("Dtor:  count=%i\n", --_objectCount);}
};

当我想跟踪我的程序在任何给定时间创建的另一个类的实例数时,我只需添加一个 ObjectCounter 作为该类的私有成员变量:

class SomeUserClass
{
public:
   SomeUserClass() : _userData(0) {/* empty */}
   ~SomeUserClass() {/* empty */}

private:
   ObjectCounter _objectCounter;
   int64_t _userData;
};

(本来我会用那个类的子类ObjectCounter来代替,但是那样做就成了my DOxygen class-graphs unnecessarily complex,所以我改成一个私有成员变量)

今天我注意到将这个“空”私有成员变量添加到我的类对象通常会增加类对象的大小(由 sizeof() 报告)。例如,以下代码显示当我包含 _objectCounter 成员变量时,我的机器上的 sizeof(SomeUserClass) 从 8 增加到 16:

int main(int, char **)
{
   SomeUserClass sc1;
   printf("sizeof(SomeUserClass)=%zu\n", sizeof(SomeUserClass));
   printf("sizeof(ObjectCounter)=%zu\n", sizeof(ObjectCounter));

   return 0;
}

无论是否启用优化,都会增加(通过-O3)。

我相信这样做的原因是编译器正在为_objectCounter 成员变量分配空间,因此如果其他代码需要获取指向ObjectCounter 的指针,则可以提供唯一地址。但是我的程序中的任何代码实际上都没有引用_objectCounter 变量;它的存在只是为了在适当的时候执行自己的默认构造函数和析构函数。

鉴于此,有没有办法鼓励(或者更好的是,强制)编译器不要为这个成员变量分配任何空间?

【问题讨论】:

  • 尝试让int64_t 类成员成为该类的第一个成员。
  • @SamVarshavchik 我试过了,sizeof(SomeUserClass) 还是 16。
  • ObjectCounter 继承,以便可以应用空基优化。在大多数情况下,它会。
  • 切向:将_objectCount 从全局变量更改为属于ObjectCounter 的静态类变量就足够简单了。简单但需要更多输入是通过添加复制和移动构造函数来更改它以遵守 3/5/0 规则,这些构造函数也会酌情增加计数器。
  • 如果你使用的是 C++20 那么属性[[no_unique_address]]

标签: c++ sizeof


【解决方案1】:

如果您可以使用 C++20,则可以使用属性[[no_unique_address]] 来完成此操作。使用

#include <cstdio>
#include <cstdint>

int _objectCount = 0;  // global

class ObjectCounter
{
public:
   ObjectCounter() {printf("DefaultCtor:  count=%i\n", ++_objectCount);}
   ~ObjectCounter() {printf("Dtor:  count=%i\n", --_objectCount);}
};

class SomeUserClass
{
public:
   SomeUserClass() : _userData(0) {/* empty */}
   ~SomeUserClass() {/* empty */}

private:
   [[no_unique_address]] ObjectCounter _objectCounter;
   int64_t _userData;
};

int main(int, char **)
{
   SomeUserClass sc1;
   printf("sizeof(SomeUserClass)=%zu\n", sizeof(SomeUserClass));
   printf("sizeof(ObjectCounter)=%zu\n", sizeof(ObjectCounter));

   return 0;
}

输出:

DefaultCtor:  count=1
sizeof(SomeUserClass)=8
sizeof(ObjectCounter)=1
Dtor:  count=0

【讨论】:

  • 这似乎是一个很好的解决方案,除了当我尝试它时(使用 g++ -O3 temp.cpp -std=c++20,使用 XCode 12.5 中的 Apple clang v12.0.5),sizeof(SomeUserClass) 即使使用 @ 仍然打印为 16 987654328@ 标签已应用。也许是编译器错误?
  • @JeremyFriesner 错误可能,还不能实现。 MacOS 上的 IIRC g++ 实际上是 clang,至少 clang 12.0 给出了正确的结果:godbolt.org/z/W5oaGG8vd
  • 啊,我看到了区别——如果我先声明_userData,我会得到sizeof=16,但如果我先声明_objectCounter,我会得到sizeof=8。
猜你喜欢
  • 1970-01-01
  • 2017-07-22
  • 2019-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-18
  • 1970-01-01
相关资源
最近更新 更多