当前位置: 首页 > news >正文

C++空类的那点事儿

什么是C++的空类

顾名思义,空类就是指哪些不包含成员变量的类。例如以下这个就是一个空类:

class EmptyBase {};

既然如此,那么是不是说空类的内部一定不会其他代码呢?不是的,空类内部也可以包含其他东西,例如:构造函数、析构函数、静态成员变量、静态函数、成员函数、typedef语句等。

例如在以下代码中EmptyBase依然是空类:

class EmptyBase {
public:// 构造函数EmptyBase(){}// 析构函数~EmptyBase(){}// typedef并没有给类增加成员或者函数typedef int INT_NUM;// 不涉及到内部成员变量的内部函数void set(int a){}// 静态函数static void setStr(const std::string& s){}// 静态变量static std::string str;
};

在C++11之后我们可以使用std::is_empty判断一个类是否是空类:

#include <iostream>
class EmptyBase {};int main() {auto aa = std::is_empty<EmptyBase>::value;std::cout << "是否是空类:" << aa << std::endl;return 0;
}

C++空类的大小

有以下计算空类大小的代码,你认为输出结果是多少?

#include <iostream>
class EmptyClass {// 空类
};
int main(int argc, char* argv[]) {std::cout << "sizeof(EmptyClass): " << sizeof(EmptyClass) << std::endl;return 0;
}

即使是空类,其大小也不会为0。在许多平台上,空类的大小为1;而在某些对于对齐(alignment)要求更严格系统上,空类的大小可能是另一个数(通常是4)。

为什么C++空类的大小不是0呢?

C++的设计者们不允许类的大小为0,因为每个对象都必须具有唯一的地址,特别是在涉及到取址和指针计算时,如果一个类的大小是0,那么指针的一切将会失效。 试想一下如果空类的大小为0,那么由空类它们构成的数组,其大小必然也是0,这会导致指针运算中普遍使用的性质失效。

空基类优化

C++标准规定,当空类作为基类时,只要不会与同一类型的另一个对象或子对象分配在同一地址,就不需为其分配任何空间。

#include <iostream>
class EmptyBase {// 空基类
};class EmptyOne: public EmptyBase{// 空类1
};class EmptyTwo: public EmptyOne{// 空类2
};int main(int argc, char* argv[]) {std::cout << "sizeof(EmptyBase): " << sizeof(EmptyBase) << std::endl;std::cout << "sizeof(EmptyOne): " << sizeof(EmptyOne) << std::endl;std::cout << "sizeof(EmptyTwo): " << sizeof(EmptyTwo) << std::endl;return 0;
}

如果编译器支持空基类优化,上述程序所有的输出结果相同(一般是1),但均不为0。

我们修改一下代码,将EmptyTwo改为多继承,那么EmptyTwo还是空类吗?

class EmptyTwo: public EmptyOne,public EmptyBase{};

答案是在多继承状态的EmptyTwo已经不是空类了, 虽然EmptyTwo和它的基类都没有任何成员。不过,EmptyTwo的基类EmptyOne和EmptyBase不能分配到同一地址空间, 否则EmptyTwo的基类EmptyBase会和EmptyOne的基类EmptyBase撞在同一地址空间上。换句话说,两个相同类型的子对象偏移量相同,这是C++对象布局规则不允许的。

对空基类优化进行限制的根本原因在于,我们需要能比较两个指针是否指向同一对象。 由于指针几乎总是用地址作内部表示,所以我们必须保证两个不同的地址(即两个不同的指针值)对应两个不同的对象。 虽然这种约束看起来并不非常重要,但是在实际应用中的许多类都是继承自一组定义公共typedefs的基类,当这些类作为子对象出现在同一对象中时,问题就凸现出来了,此时优化应被禁止。

空类存在的意义是什么

尽管在面向对象编程中,空类看起来可能有些多余,但是它们存确有它们的用途。

空类是一种有着潜在应用价值的编程技巧,例如空类可以被用于多种编程模式和设计模式中,它还可以作为数据类型的标记,用于在编译时实现条件编译。 空类也可以作为接口占位符,用于后续的继承实现或者后续扩展等。空类也在模板编程和元编程等高级编程技术中也发挥重要作用。

例如在C++标准库中,五种迭代器类别都有对应的空类。这些空类用于标识迭代器的类别,并通过模板特化来实现对不同类型迭代器的特殊处理,如图:

相关文章:

  • 虾皮在线定价工具:知虾轻松制定有竞争力的价格策略
  • 一张网页截图,AI帮你写前端代码,前端窃喜,终于不用干体力活了
  • Python技术操作1-高效办公:将文本、图片和表格信息批量写入Word文档
  • QList简单使用
  • Web安全-初识SQL注入(一)
  • 关于 Python 的最全面试题
  • 矩阵的初等变换
  • Maven——Maven发展历程
  • MySQL查看和修改时区
  • 西安安泰——ATA-1220E宽带放大器
  • 【单片机】单片机裸机实现多任务调度
  • 质量小议35 -- SQL注入
  • 第5章 固定通信网
  • 深入了解Java Period类,对时间段的精确控制
  • MX6ULL学习笔记 (七) 中断实验
  • @jsonView过滤属性
  • [js高手之路]搞清楚面向对象,必须要理解对象在创建过程中的内存表示
  • 【面试系列】之二:关于js原型
  • Apache Pulsar 2.1 重磅发布
  • Mysql优化
  • Rancher如何对接Ceph-RBD块存储
  • ucore操作系统实验笔记 - 重新理解中断
  • uni-app项目数字滚动
  • 分类模型——Logistics Regression
  • 给初学者:JavaScript 中数组操作注意点
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 经典排序算法及其 Java 实现
  • 跨域
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 人脸识别最新开发经验demo
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 软件开发学习的5大技巧,你知道吗?
  • 我的面试准备过程--容器(更新中)
  • 写代码的正确姿势
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • k8s使用glusterfs实现动态持久化存储
  • scrapy中间件源码分析及常用中间件大全
  • #HarmonyOS:软件安装window和mac预览Hello World
  • (solr系列:一)使用tomcat部署solr服务
  • (附源码)ssm捐赠救助系统 毕业设计 060945
  • (九)One-Wire总线-DS18B20
  • (十五)使用Nexus创建Maven私服
  • (转)我也是一只IT小小鸟
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .net core MVC 通过 Filters 过滤器拦截请求及响应内容
  • .net core 依赖注入的基本用发
  • .NET Core引入性能分析引导优化
  • .net mvc部分视图
  • .NET MVC之AOP
  • .net Stream篇(六)
  • .Net 访问电子邮箱-LumiSoft.Net,好用
  • .net打印*三角形
  • .net反编译的九款神器
  • .NET开源全面方便的第三方登录组件集合 - MrHuo.OAuth
  • .net下的富文本编辑器FCKeditor的配置方法