【重学C++】【类】详细谈谈C++中的空类(面试常问)

大家好,我是 同学小张,持续学习C++进阶知识AI大模型应用实战案例,持续分享,欢迎大家点赞+关注,共同学习和进步。

重学C++系列文章,在会用的基础上深入探讨底层原理和实现,适合有一定C++基础,想在C++方向上持续学习和进阶的同学。争取让你每天用5-10分钟,了解一些以前没有注意到的细节。


实际工作中使用C++时,或多或少都会见过空类,面试时也经常会被问到空类的相关问题。今天我们就来深入学习一下:空类里面有什么?大小是多少?存在的意义是什么?等等。

0. 什么是空类?

用C++定义一个类如下:

class EmptyClass
{

};

类中我们什么都没有定义,那这个类就是空类。

对于空类的定义,简单来说,当类中一个成员变量也没有时,则为空类。注意这里是说的 成员变量

以下几种情况,也为空类:

  • 如果类中有成员函数,但不涉及成员变量。

  • 类中有静态成员变量、静态函数

  • 类中有typedef语句

  • 类中显示写出了默认构造函数等类中默认存在的函数(后面详说)

例如下面这个例子,也算是空类:

class EmptyClass
{
public:
    // 构造函数
    EmptyClass(){

    }
    // 析构函数
    ~EmptyClass(){

    }
    // typedef并没有给类增加成员或者函数
    typedef int INT_NUM;

    // 不涉及到内部成员变量的内部函数
    void set(int a){

    }
    // 静态函数
    static void setStr(const std::string& s){

    }

    // 静态变量
    static std::string str;
};

在C++11之后,可以通过 is_empty来检测一个类是否为空类。

auto is_empty = std::is_empty<EmptyClass>::value;
std::cout << "是否是空类:" << is_empty << std::endl;

// 输出 是否是空类:1

所以,空类里面并不是什么都没有。即使我们不在空类里手动添加任何信息,它里面默认也是有一些东西的。

1. 空类里面默认有什么?

1.1 默认的6个成员函数

任何一个类在我们不写任何东西的情况下,都会生成六个默认成员函数。

(1)默认构造函数

(2)析构函数

(3)拷贝构造函数

(4)赋值运算符

(5)取址运算符

(6)const取址运算符

类似下面这些:

// EmptyClass
EmptyClass();
// 析构函数
~EmptyClass();

EmptyClass(EmptyClass& h);  //拷贝构造函数

EmptyClass& operator=(const EmptyClass& a);  //赋值运算符

EmptyClass* operator&(); // 取址运算符

const EmptyClass* operator&() const; // 取址运算符 const

其中后两个取址运算符跟具体的编译器有关,有的编译器可能没有这两个函数。

1.2 注意事项

  • 这些函数只有在需要调用的时候,编译器才会生成。
  • 函数都是public的。

1.3 拷贝构造函数和赋值操作符的区别

(1)拷贝构造函数是一个对象初始化一块内存区域,这块内存就是新对象的内存区,而赋值运算符是对于一个已经被初始化的对象来进行赋值操作。

(2)拷贝构造函数首先是一个构造函数,它调用时候是通过参数的对象初始化产生一个对象。赋值函数则是把一个新的对象赋值给一个原有的对象,所以如果原来的对象中有内存分配要先把内存释放掉,而且还要检查一下两个对象是不是同一个对象,如果是,不做任何操作,直接返回。

2. 空类的大小

2.1 空类的大小为多少?

用sizeof看一下空类的大小,结果为1。所以,空类也是有大小的,不为0。

std::printf("EmptyClass size: %llu\n", sizeof(EmptyClass)); 

// 输出 EmptyClass size: 1

2.2 为什么空类的大小不为0?

因为编译器需要区分空类的实例,而我们区分两个实例是不是同一个的方法,就是看其在内存中的地址是不是相同的。所以编译器强制给它分配了冗余的空间,这样创建空类实例时返回的地址才会不一样。

EmptyClass a;
EmptyClass b;
std::printf("EmptyClass 地址: a=%p, b=%p\n", &a, &b);

// 输出 EmptyClass 地址: a=00000000005ffe5f, b=00000000005ffe5e

2.3 继承空类,子类的大小是多少?

先看单继承:

class EmptyClassSon1 :public EmptyClass
{

};

class EmptyClassSon2 :public EmptyClass
{

};

// 输出 EmptyClassSon1 size: 1
//     EmptyClassSon2 size: 1

单继承时,子类的大小也为1。

再看下多继承:

class EmptyClassSon3 :public EmptyClassSon1, public EmptyClassSon2
{

};

// 输出 EmptyClassSon3 size: 2

这是因为: EmptyClassSon3 的基类 EmptyClassSon1 和 EmptyClassSon2 不能分配到同一地址空间,否则 EmptyClassSon3 的基类会撞在同一地址空间上,违背了要能够区分不同类型实例地址的原则。

2.4 补充知识 - 类的实例大小计算

大小计算的主要规则:

  • 基本数据类型大小:类的实例大小至少等于其所有非静态成员变量的大小之和。例如,如果一个类有一个 int 成员和一个 double 成员,那么该类的大小至少是 sizeof(int) + sizeof(double)(还没做内存对齐)。这不包括静态成员,因为静态成员不是类的每个实例的一部分,但是在全局范围内只存在一份。

  • 继承:在C++中,如果一个类B继承自另一个类A,那么类B的实例大小至少是 sizeof(A) + sizeof(B中新增的成员)。

  • 对齐规则:C++编译器可能会调整成员的布局以满足硬件的对齐要求,这可能会增加类的大小。例如,如果一个类有一个 char 成员和一个 double 成员,在某些硬件上 double 必须在8字节的边界上对齐,所以编译器可能会在 char 和 double 之间插入7个填充字节。

  • 虚函数:如果一个类有虚函数,那么它的大小至少还包括一个指向虚函数表(vtable)的指针。

3. 空类存在的意义

直接上大模型回复的答案,总结的比很多文章都要全:

在这里插入图片描述

4. 总结

本文主要是对C++中的空类进行了总结,包括:空类的概念、空类的大小,空类中默认存在的成员函数已经空类存在的意义等。希望能够帮大家更加深入地理解C++中的类。

如果觉得本文对你有帮助,麻烦点个赞和关注呗 ~~~


  • 大家好,我是 同学小张,持续学习C++进阶知识AI大模型应用实战案例
  • 欢迎 点赞 + 关注 👏,持续学习持续干货输出
  • +v: jasper_8017 一起交流💬,一起进步💪。
  • 微信公众号也可搜同学小张 🙏

本站文章一览:

在这里插入图片描述

  • 27
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

同学小张

如果觉得有帮助,欢迎给我鼓励!

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

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

打赏作者

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

抵扣说明:

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

余额充值