写点什么

C++ 空类的那点事儿

  • 2023-12-04
    福建
  • 本文字数:1710 字

    阅读完需:约 6 分钟

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



发布于: 刚刚阅读数: 4
用户头像

IT领域从业者 分享见解 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
C++空类的那点事儿_C++_树上有只程序猿_InfoQ写作社区