C++空类与类的大小

空类的默认成员

如果定义一个空类,没有任何内容,如:

class Empty
{
}

如果你只是声明一个空类,不做任何事情的话,编译器会自动为你生成一个默认构造函数、一个拷贝默认构造函数、一个默认拷贝赋值操作符、一个默认析构函数、取址运算符和一个取址运算符const。这些函数只有在第一次被调用时,才会别编译器创建。所有这些函数都是inline和public的。

class Empty
{
public:
Empty(); // 缺省构造函数//
Empty( const Empty& ); // 拷贝构造函数//
~Empty(); // 析构函数//
Empty& operator=( const Empty& ); // 赋值运算符//
Empty* operator&(); // 取址运算符
const Empty* operator&() const; // 取址运算符 const
};

空类的大小

在C++中空类会占一个字节,这是为了让对象的实例能够相互区别。具体来说,空类同样可以被实例化,并且每个实例在内存中都有独一无二的地址,因此,编译器会给空类隐含加上一个字节,这样空类实例化之后就会拥有独一无二的内存地址。如果没有这一个字节的占位,那么空类就无所谓实例化了,因为实例化的过程就是在内存中分配一块地址。
注意:当该空白类作为基类时,该类的大小就优化为0了,这就是所谓的空白基类最优化。
注意:空白基类最优化无法被施加于多重继承上只适合单一继承。

#include<iostream>
 
using namespace std;
 
class test
{
};
 
int main()
{
	test a, b;
	cout << "sizeof(test): " << sizeof(test) << endl;
	cout << "addr of a: " << &a << endl;
	cout << "addr of b: " << &b << endl;
	system("pause");
	return 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;
}

在这里插入图片描述
在上例中,大部分编译器对于sizeof(derive)的结果是4,而不是8。这就是所谓的空白基类最优化在(empty base optimization-EBO 或 empty base classopimization-EBCO)。在空基类被继承后由于没有任何数据成员,所以子类优化掉基类所占的1 byte。EBO并不是c++标准所规定必须的,但是大部分编译器都会这么做。由于空基类优化技术节省了对象不必要的空间,提高了运行效率,因此成为某些强大技术的基石,基于类型定义类如stl中的binary_function、unary_function、iterator、iterator_traits的实现复用;基于策略类如内存管理、多线程安全同步的实现复用。当某个类存在空类类型的数据成员时,也可考虑借助EBO优化对象布局.
多继承空白基类:

#include<iostream>
 
using namespace std;
 
class test1
{
};
 
class test2
{
};
class derive1 :public test1
{
	private:
		int a;
};
 
class derive2 :public test1,public test2
{
	private:
		int b;
};
 
int main()
{
	cout << "sizeof(test): " << sizeof(test1) << endl;
	cout << "sizeof(derive): " << sizeof(derive1) << endl;
	cout << "sizeof(derive): " << sizeof(derive2) << endl;
 
	system("pause");
	return 0;
}

在这里插入图片描述

类的大小

首先:我们要知道什么是类的实例化,所谓类的实例化就是在内存中分配一块地址.
那我们先看看一个例子:

#include<iostream.h>

class a {};
class b{};
class c:public a{
	virtual void fun()=0;
};
class d:public b,public c{};
int main()
{
	 cout<<"sizeof(a)"<<sizeof(a)<<endl;
	 cout<<"sizeof(b)"<<sizeof(b)<<endl;
	 cout<<"sizeof(c)"<<sizeof(c)<<endl;
	 cout<<"sizeof(d)"<<sizeof(d)<<endl;
	 return  0;
 }

程序执行的输出结果为:

sizeof(a)=1
sizeof(b)=1
sizeof(c)=4
sizeof(d)=8

为什么会出现这种结果呢?初学者肯定会很烦恼是吗?类a,b明明是空类,它的大小应该为为0,为什么 编译器输出的结果为1呢?这就是我们刚才所说的实例化的原因(空类同样可以被实例化),每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址.所以a,b的大小为1.
而类c是由类a派生而来,它里面有一个纯虚函数,由于有虚函数的原因,有一个指向虚函数的指针(vptr),在32位的系统分配给指针的大小为4个字节,所以最后得到c类的大小为4.
类d的大小更让初学者疑惑吧,类d是由类b,c派生迩来的,它的大小应该为二者之和5,为什么却是8呢?这是因为为了提高实例在内存中的存取效率.类的大小往往被调整到系统的整数倍.并采取就近的法则,里哪个最近的倍数,就是该类的大小,所以类d的大小为8个字节.
当然在不同的编译器上得到的结果可能不同,但是这个实验告诉我们初学者,不管类是否为空类,均可被实例化(空类也可被实例化),每个被实例都有一个独一无二的地址.
我所用的编译器为vc++ 6.0.
下面我们再看一个例子.

#include<iostream.h>
class a{
	pivate:
		int data;
};

class b{
private:
		int data;
  		static int data1;
};
 int b::data1=0;
 void mian(){
	 cout<<"sizeof(a)="<<sizeof(a)<<endl;
	 cout<<"sizeof(b)="<<sizeof(b)<<endl;
}

执行结果为:

sizeof(a)=4;
sizeof(b)=4;

为什么类b多了一个数据成员,却大小和类a的大小相同呢?因为:类b的静态数据成员被编译器放在程序的一个global data members中,它是类的一个数据成员.但是它不影响类的大小,不管这个类实际产生 了多少实例,还是派生了多少新的类,静态成员数据在类中永远只有一个实体存在,而类的非静态数据成员只有被实例化的时候,他们才存在.但是类的静态数据成员一旦被声明,无论类是否被实例化,它都已存在.可以这么说,类的静态数据成员是一种特殊的全局变量.
所以a,b的大小相同.

下面我们看一个有构造函数,和析构函数的类的大小,它又是多大呢?

#include<iostream.h>
class A{
public :
	A(int a){
		a=x;
	}
 	void f(int x){
  		cout<<x<<endl;
  	}
 ~A(){}
private:
   int x;
   int g;
};
class B{
	public:
	private:
	int  data; int data2;
	static int xs;
};
int B::xs=0;
void  main(){
	A s(10);
	s.f(10);
	cout<<"sozeof(a)"<<sizeof(A)<<endl;
	cout<<"sizeof(b)"<<sizeof(B)<<endl;
}

程序执行输出结果为:

sizeof(a) 8
sizeof(b) 8

它们的结果均相同,可以看出类的大小与它当中的构造函数,析构函数,以及其他的成员函数无关,只与它当中的成员数据有关.
从以上的几个例子不难发现类的大小:
1.为类的非静态成员数据的类型大小之和.
2.有编译器额外加入的成员变量的大小,用来支持语言的某些特性(如:指向虚函数的指针).
3.为了优化存取效率,进行的边缘调整.
4 与类中的构造函数,析构函数以及其他的成员函数无关.

原文链接:

  1. c++空类实例大小不是0原因
  2. C++中空类占一字节原因详解
  3. C++中的空类默认产生哪些类成员函数?
  • 11
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值