01
空类
1. 空类
所谓空类,意味着类中不携带任何数据。即该类中没有非静态成员函数、虚函数、虚基类等。理论上说,一个不携带任何数据的空类其所占用的内存空间大小应该是零,因为它不需要存储属于任何附加对象的数据。
实际情况果真如我们猜想的那样空类的大小为零吗?让我们拭目以待。
//test.cpp
#include <iostream>
using namespace std;
class Base{
};
int main()
{
cout<<"sizeof(Base): "<<sizeof(Base)<<endl;
return 0;
}
复制代码
类Base是一个名副其实的空类,因为类内部不包含任何的成员和虚函数等。g++编译该test.cpp程序(g++ test.cpp -o test),然后执行,得到的结果是:1。
对于空类,它有一个虚拟占位符成员,该成员大小为1字节,这个占位符成员保证了每个对象应具有唯一的地址。因此,Base类的不同对象其地址空间是不相同的。如下:
#include <iostream>
using namespace std;
class Base{
};
int main()
{
cout<<"sizeof(Base): "<<sizeof(Base)<<endl;
Base a, b;
if(&a == &b)
cout<<"The address is the same."<<endl;
else
cout<<"The address is different."<<endl;
cout<<"&a: "<<&a<<", "<<"&b: "<<&b<<endl;
return 0;
}
复制代码
现定义两个Base类对象,分别是a和b。然后分别对两个对象的地址进行判断,若相等,则打印“The address is the same.”反之,则打印“The address is different.” 编译运行,得到的打印结果如下:
可看到,两个对象所占用的内存地址空间是不一样的。
若对于一个空类,其所有对象占用的内存地址空间一样,会出现什么问题?如下示例所示,分别定义两个指向Base类类型的指针变量,然后分别初始化。
Base *p1 = new Base;
Base *p2 = new Base;
delete p1;
delete p2;
复制代码
若各对象占用的内存空间相同,那么这里delete p1、p2时候,会对同一内存空间释放两次,这是一个很严重的问题。
02
C++标准不允许空类大小为0
C++标准保证任何类的大小至少为一字节。并且标准规定,任何对象都不应该与另外一个对象具有相同的内存地址。这样做的理由是:
-
保证了new始终会返回指向不同内存地址的指针。
-
避免被零除。例如,指针算术运算(其中许多有编译器自动完成)涉及除以sizeof(类名)。
如下示例所示,若空类Base大小为0,那么下面demo将会导致段错误。由于C++标准规定空类不为0,所以下面的demo将正常运行,sizeof(b)结果是10。
#define ARRAY_SIZE(arr) ( sizeof(arr) / sizeof(arr[0]) )
class Base{
};
Base b[10];
for(auto i = 0; i < ARRAY_SIZE(b); i++){
do something;
}
复制代码
03
空的基类不会为派生类大小增1
对于空类,其大小为1,那我现在声明一个派生类,继承于基类。派生类对象的大小是不是等于派生类大小+(空)基类大小(一字节)呢?其实不然,空基类不会为派生类的大小加一。如下示例:
#include <iostream>
using namespace std;
class Base{
};
class SubBase : public Base
{
char m_c;
};
int main()
{
cout<<"sizeof(SubBase): "<<sizeof(SubBase)<<endl;
return 0;
}
复制代码
从打印结果可看到,对于之类SubBase的大小仍然为1字节,这里的1字节是子类的成员变量m_c的大小。所以空基类不会为派生类的大小增加一。
往期推荐