C++中空类占位问题
在C++中空类会占一个字节,这是为了让对象的实例能够相互区别。具体来说,空类同样可以被实例化,并且每个实例在内存中都有独一无二的地址,因此,编译器会给空类隐含加上一个字节,这样空类实例化之后就会拥有独一无二的内存地址。如果没有这一个字节的占位,那么空类就无所谓实例化了,因为实例化的过程就是在内存中分配一块地址
1、空类,声明时编译器不会生成任何成员函数
对于空类,编译器不会生成任何的成员函数,只会生成1个字节的占位符。
有时可能会以为编译器会为空类生成默认构造函数等,事实上是不会的,编译器只会在需要的时候生成6个成员函数:一个缺省的构造函数、一个拷贝构造函数、一个析构函数、一个赋值运算符、一对取址运算符和一个this指针。
代码:
#include <iostream>
using namespace std;
class A
{
};
class B
{
virtual bool compare(int a,int b) = 0;
};
class C:public A,public B
{
};
class D:public A,public B
{
virtual bool compare(int a, int b) = 0;
};
class E :virtual A, virtual B
{
};
class F :virtual A, virtual B
{
virtual bool compare(int a, int b) = 0;
};
int main()
{
cout << "A zize:" << sizeof(A) << endl;
cout << "B zize:" << sizeof(B) << endl;
cout << "C zize:" << sizeof(C) << endl;
cout << "D zize:" << sizeof(D) << endl;
cout << "E zize:" << sizeof(E) << endl;
cout << "F zize:" << sizeof(F) << endl;
system("pause");
return 0;
}
分析:
类A是空类,但空类同样可以被实例化,而每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址,所以sizeof(A)的大小为1。
类B里面因有一个纯虚函数,故有一个指向虚函数的指针(vptr),32位系统分配给指针的大小为4个字节,所以sizeof(B)的大小为4。类C继承于A和B,编译器取消A的占位符,保留一虚函数表,故大小为4。类D继承于A和B,派生类基类共享一个虚表,故大小为4。类E虚继承A和B,含有一个指向基类的指针(vftr)和一个指向虚函数的指针。类F虚继承A和B,含有一个指向基类的指针(vftr)和一个指向虚函数的指针。
2、空类,定义时会生成6个成员函数
class Empty
{
};
等价于
class Empty
{
public:
Empty(); //缺省构造函数
Empty(const Empty &rhs); //拷贝构造函数
~Empty(); //析构函数
Empty& operator=(const Empty &rhs); //赋值运算符
Empty* operator&(); //取址运算符
const Empty* operator&() const; //取址运算符(const版本)
};
函数实现
inline Empty::Empty() //缺省构造函数
{
}
inline Empty::~Empty() //析构函数
{
}
inline Empty *Empty::operator&() //取址运算符(非const)
{
return this;
}
inline const Empty *Empty::operator&() const //取址运算符(const)
{
return this;
}
inline Empty::Empty(const Empty &rhs) //拷贝构造函数
{
//对类的非静态数据成员进行以"成员为单位"逐一拷贝构造
//固定类型的对象拷贝构造是从源对象到目标对象的"逐位"拷贝
}
inline Empty& Empty::operator=(const Empty &rhs) //赋值运算符
{
//对类的非静态数据成员进行以"成员为单位"逐一赋值
//固定类型的对象赋值是从源对象到目标对象的"逐位"赋值。
}
注意:当该空白类作为基类时,该类的大小就优化为0了,这就是所谓的空白基类最优化。
#include<iostream>
using namespace std;
class test
{
};
class derive :public test
{
private:
int a;
};
int main()
{
test a, b;
cout << "sizeof(test): " << sizeof(test) << endl;
cout << "sizeof(derive): " << sizeof(derive) << endl;
cout << "addr of a: " << &a << endl;
cout << "addr of b: " << &b << endl;
system("pause");
return 0;
}