C++ 空基类优化

1. 继承体系中的内存模型

我们都知道,在C++中,不存在大小是零的类。即便是空类,也要占据一个字节,否则无法比较两个空类对象是否是同一个对象(在C/C++中,默认使用地址来判断两个变量是否是同一个)。

class BaseEmpty { 
public:
  	BaseEmpty() { std::cout<<"Base address:    "<< this << std::endl;}
};

int main(int argc, char const *argv[]) {

  	BaseEmpty empty_1{};
  	BaseEmpty empty_2{};
 
  	assert(&empty_1 != &empty_2); // 两个空类对象的地址肯定不同

  	std::cout<<sizeof(BaseEmpty{})<<std::endl; // 输出 1
}

子类继承父类,可以等效地看作子类将父类的成员变量复制到自己内存模型中。比如在下面的demo中,Dervied_1继承了父类BaseDerived_1的内存模型等效于Derived_2

class Base { 
public:
  	Base() = default;
private:
  	int num_{0};
  	bool state_{false};
};

class Derived_1 : public Base { 
public:
  	Derived_1() = default;
private:
  	std::string name_{"CPP"};
};

class Derived_2 { 
public:
  	Derived_2() = default;
private:
  	int num_{0};
  	bool state_{false};
  	// 子类的成员变量
  	std::string name_{"CPP"};
};

2. 空基类优化

对于空类BaseEmpty ,假设有另一个空类DerivedEmpty继承自BaseEmpty,空类DerivedDeeperEmpty继承自DerivedEmpty,那么DerivedDeeperEmpty对象的大小sizeof(DerivedDeeperEmpty{})会是几个字节???

class DerivedEmpty: public BaseEmpty { 
public:
  DerivedEmpty() { 
    std::cout<<"Derived address: "<< this << std::endl;
  }
};

class DerivedDeeperEmpty: public DerivedEmpty {
public:
  DerivedDeeperEmpty() { 
    std::cout<<"deeper address:  "<< this << std::endl;
  }
};

如果仍然像之前说的那种内存等效模型,那么编译器会每个空基类对象都分配内存,因此即便DerivedDeeperEmpty是个空类,也要占用两个字节,相当于内部分别包含了BaseEmptyDerivedEmpty对象,即等效为类DerivedDeeperEmpty_eq

class DerivedDeeperEmpty_eq {
public:
  	DerivedDeeperEmpty_eq() = default;
private:
  	BaseEmpty    base_1_;
  	DerivedEmpty base_2_;
};

sizeof(DerivedDeeperEmpty_eq); // 2

如果编译器真的是这么实现,是否很不符合直觉。因为DerivedDeeperEmpty明明只是个空类啊!!!大小却是2个字节???如果情况再极端点,DerivedDeeperEmpty还有更深的空子类,那么空子类的大小会不断膨胀???

是的,这种情况太不符合直觉,严重浪费内存。因此,C++标准如下规定:在空类被用作基类时,如果不给它分配内存并不会导致它被存储到与同类型对象(包括子类对象)相同的地址上,那么就可以不给它分配内存。换句话说,BaseEmpty作为空基类时,下面两种情况,编译器不会为Basement对象在子类中分配内存:

  • 子类单继承,比如类DerivedDeeperEmpty。

  • 子类在多继承、选择第二个基类时,没有继续选择BaseEmptyBaseEmpty的子类作为父类,那么BaseEmpty是不会被分配内存的。

posted @   小熊酱  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示