【C++】空类为何占一个字节的思考?

01、文章目录

02、一个简单的空类示例

当初学习C++的时候,老师提过一下,空类占一个字节,但是,并没有深究,最近看公司源码的时候老是看见空类作为基类,然后向下继承,越看越迷惑,故,了解了一下原理,这里记录一下。

Test Code:

#include <iostream>
using namespace std;

//声明一个空类,类名自取,这里我以自己名字为例
class Cain
{
	//什么都不做
};

int main()
{
	//实例化两个对象
	Cain a1;
	Cain a2;

	//查看空类对象大小
	cout << "sizeof(a1):" << sizeof(a1) << endl;
	cout << "sizeof(a2):" << sizeof(a2) << endl; 
	
	cout << "a1的地址:" << &a1 << endl;
	cout << "a2的地址:" << &a2 << endl;

	system("pause");
	return 0;
}

Result:
结果我已经测试过了,图片省略,不方便截图

  • sizeof(a1) == 1
  • sizeof(a2) == 1
  • &a1,&a1 为随机地址,这里不写,每个人运行出来都不一样.

值得一提的是为啥a1的地址比a2的地址小1,这是超级基础的问题,如果不清楚,请查看我另一篇博客:https://blog.csdn.net/m0_43458204/article/details/107158200,里面的内容想必而已解答你心中的疑问。

Conclusion:

  1. 其实这是C++中空类占位问题。
  2. 在C++中空类会占一个字节,这是为了让对象的实例能够相互区别。具体来说,空类同样可以被实例化,并且每个实例在内存中都有独一无二的地址,因此,编译器会给空类隐含加上一个字节,这样空类实例化之后就会拥有独一无二的内存地址。如果没有这一个字节的占位,那么空类就无所谓实例化了,因为实例化的过程就是在内存中分配一块地址。

注意:当该空白类作为基类时,该类的大小就优化为0了,这就是所谓的空白基类最优化。

03、单继承空白基类最优化问题

测试代码:

#include <iostream>

using namespace std;

//声明一个空类 基类
class A{

};

//公有继承
class B : public A
{
public:
    int a;
};

int main(void)
{

    //使用空类定义两个对象
    A a1;
    B b1;

    cout << "sizeof(a1): " << sizeof(a1) << endl;
    cout << "sizeof(b1): " << sizeof(b1) << endl;

    cout << "&a1: " << &a1 << endl;
    cout << "&b1: " << &b1 << endl;

    return 0;
}

Result:

  • sizeof(a1):1
  • sizeof(b1):4
  • 内存同样随机,方式同上描述。

Conclusion:
先公布结论之前,了解一下EBO优化对象布局,这是一位前辈写的关于EBO的深入探究,值得一看。

在上例中,大部分编译器对于sizeof(b1)的结果是4,而不是8。这就是所谓的空白基类最优化在(empty base optimization-EBO 或 empty base classopimization-EBCO)。在空基类被继承后由于没有任何数据成员,所以子类优化掉基类所占的1 byte。EBO并不是c++标准所规定必须的,但是大部分编译器都会这么做。由于空基类优化技术节省了对象不必要的空间,提高了运行效率,因此成为某些强大技术的基石,基于类型定义类如stl中的binary_function、unary_function、iterator、iterator_traits的实现复用;基于策略类如内存管理、多线程安全同步的实现复用。当某个类存在空类类型的数据成员时,也可考虑借助EBO优化对象布局.

注意:空白基类最优化无法被施加于多重继承上只适合单一继承。

04、多继承空白基类最优化问题

测试程序:

#include <iostream>

using namespace std;

//声明一个空类 基类
class A{

};

//声明另外一个空类 基类
class B{

};

//公有继承
class C : public A
{
public:
    int a;
};

class D : public A, public B
{
public:
    int a;
};

int main(void)
{

    //使用空类定义两个对象
    A a1;
    C c1;
    D d1;

    cout << "sizeof(a1): " << sizeof(a1) << endl;
    cout << "sizeof(c1): " << sizeof(c1) << endl;
    cout << "sizeof(d1): " << sizeof(d1) << endl;

    cout << "&a1: " << &a1 << endl;
    cout << "&c1: " << &c1 << endl;
    cout << "&d1: " << &d1 << endl;

    return 0;
}

Result:
这里有意思的是,不同的编译器或者说平台,结果不太一样。

  1. Linux 64

    sizeof: 1 4 4
    内存地址:同上所述.

  2. Windows Vs 2015

    sizeof:1 4 8
    内存地址:同上所述

Conclusion:
关于为什么产生这种现象,我也不太清楚,肯定是跟编译器有关,为何这么优化,可能得看汇编。
如果有谁清楚,希望不吝指教。

05、小结

关于空类的思考,暂时就到这里,至少了解了这些,知道为啥公司源码里面会频繁使用空类来做基类。都是出于优化的考量。

版权声明:转载请注明出处,谢谢。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cain Xcy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值