1.sizeof(没有任何成员变量和成员函数的空类)是几,为什么?
- 是1B。空类型的实例中不包含任何信息,本来求sizeof应该是0,但是当我们声明该类型的实例的时候,它必须在内存中占有一定的空间,否则无法使用这些实例。至于占用多少内存,由编译器决定。例如:在Code::Blocks和Visual Studio中每个空类型的实例占1B。
注意:一旦类中有其他的占用空间成员,则这1个字节就不在计算之内。
2.sizeof(没有任何成员变量但有一个构造函数和析构函数的类)是几,为什么?
- 是1B。调用构造函数和析构函数只需要直到函数的地址即可,而这些函数的地址只与类型相关,而与类型的实例无关,编译器也不会因为这两个函数而在实例内添加任何额外的信息。
注意:如果有其他成员函数(非虚函数),则还是只占用1个字节。
3.sizeof(没有任何成员变量但有一个构造函数和虚析构函数的类)是几,为什么?
- 是4B。C++的编译器一旦发现一个类型中有虚函数,就会为该类型生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针。在32位的机器上,一个指针占4字节的空间,如果在64位的机器上,一个指针占8字节的空间。
注意:虚函数表是C++实现多态的一种机制。
总结1:
- C++标准规定类的大小不为0,空类的大小为1,当类不包含虚函数和非静态数据成员时,其对象大小也为1。
- 如果在类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针指向虚函数表VTable;在32位机器上,一个对象会增加4个字节来存储此指针,它是实现面向对象中多态的关键。
- 虚函数本身和其他成员函数一样,是不占用对象的空间的。虚函数本身和其他成员函数一样,是不占用对象的空间的。
相关知识点:
- 由于类只是一个类型定义,没有大小可言,因此, 用sizeof运算符对一个类型名操作时,得到的实际上是该类型实例的大小。
- 一个C++的空类,即使没有任何成员变量,编译器也会自动生成默认构造函数、默认拷贝构造函数、默认析构函数、默认赋值函数、默认取值函数。
- 构造函数是一种特殊的成员函数, 主要用于为对象分配空间,进行初始化。
- 构造函数没有返回值(不能说明为void类型),可以被重载,不能为虚函数。
- 默认构造函数不带任何参数,函数体是空的,它只能为对象开辟数据成员存储空间,而不能给对象中的数据成员赋初值。
- 拷贝构造函数是一种特殊的构造函数,其形参是本类对象的引用。用于在建立一个新对象时,使用一个已经存在的对象去初始化这个新对象。
- 默认拷贝构造函数用于复制出数据成员值完全相同的新对象。
- 析构函数是一种特殊的成员函数,它执行与构造函数相反的操作,通常用于执行一些清理任务,如释放分配给对象的内存空间等。
- 析构函数没有返回值(不能说明为void类型),不能被重载,可以为虚函数。
- 采用默认赋值函数实现的数据成员逐域赋值的方法是一种浅层复制方法。通常,默认赋值函数是能够胜任工作的。但是,对于类似指针悬挂的问题来说,还需要用户根据实际自己对赋值运算符进行重载(进行深层复制)。
- 浅拷贝:源对象的指针和拷贝对象的指针都指向同一个空间;
深拷贝:源对象的指针指向一个空间,拷贝对象的指针指向另一个新的空间(两个空间的数据成员相同)。 - 虚函数允许函数调用与函数体之间的联系在运行时才建立,也就是在运行时才决定如何动作,即所谓的动态联编。
- 虚函数的作用是允许在派生类中定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。其定义是在基类中进行的。
扩展:
下列sizeof()的大小分别是?
class Base{
}; // sizeof(Base)?
class Derived:public Base{
private:
int a;
} // sizeof(Derived)?
sizeof(Base) = 1;
sizeof(Derived) = 4;
- 在空基类被继承后,子类会优化掉空基类的1字节大小,从而节省空间大小,提高运行效率。
class Base1{
private:
char a;
int b;
char c;
}; // sizeof(Base1)?
class Base2{
private:
char a;