是否可以在不增加包含对象的大小的情况下添加私有成员变量?

问题描述

我有一个名为 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() 所报告的)。例如,以下代码显示当我包含 sizeof(SomeUserClass) 成员变量时,_objectCounter 在我的机器上从 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 变量;它的存在只是为了在适当的时候执行自己的认构造函数和析构函数

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

解决方法

如果您可以使用 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