C++深入理解(二)——空基类优化 Empty Base Optimization(EBO)的理解

版权声明:原创博客未经允许请勿转载! https://blog.csdn.net/holmes_MX/article/details/87919608

0.写作目的

好记性不如烂笔头。

总结:

在VS2012中(win32)

1) 在多继承中,只存在一个“空类”的继承,这里的空类只是不包括non-static成员变量,空类中可以包括enums,typedef,static,non-virtual函数[1]。

在派生类中,如果只存在继承一个空基类,而没有在派生类中定义该空基类或者其他空基类的对象作为成员数据,则虽然sizeof(空基类) = 1,但是由于空基类优化EBO,因此在计算派生类的sizeof时,不会计算继承的空基类的大小。

在派生类中,只继承一个空基类,但是在派生类中定义了该空基类或者其他空基类的对象作为成员数据,如果只定义一个空基类对象,则占用一个字节,但是由于EBO,在计算派生类的大小时,采样对其的方式,即32位对齐,则派生类的大小会多出3个字节。如果定义多个空基类数据成员,则4个空基类成员构成一个32位,对于剩下的空基类向上取整,凑出一个32位。且继承基类的地址,按照继承声明的顺序(如下面的代码中,地址在b2, b1的地址之后)。

2) 在多继承中,存在多个空基类的继承时,只有最后一个空基类的继承不分配空间,其余继承的空基类均分配一个字节。对于在派生类中定义的空基类的对象,每个对象分配一个字节。然后进行EBO,不足4个字节的向上取整。

在Ubuntu16 g++5.4.0中

无论多继承中,存在多少个空基类的继承,空基类的继承均不分配空间,而且空间类的地址与派生类的对象首地址相同,这与VS2012(win32)中不同。然后对派生类中的空基类对象,每个空基类对象分配一个字节,然后进行EBO,不足4个字节的向上取整。

(注意: 多继承中,先对继承的对象进行构造,然后对派生类中基类的对象进行构造,最后执行派生类的构造函数体中的代码。便于理解下面构造函数的结果。)

1. 多继承中,存在一个空基类,派生类的大小计算

#include<iostream>

using namespace std;

class B1
{
protected:
	int b1;
  public:
     B1(int i)
	{ b1 = i;
	 cout<<"this = "<<this<<" Constructing B1"
              <<" &this->b1="<<&b1<<" this->b1="<<b1<<endl;}
	 ~B1(){
		 cout<<"this = "<<this<<" Deconstructor B1"<<endl;
	 }
};
class B2
{
protected:
	int b2;
 public:
	B2(int j)
	{b2 = j;
	cout<<"this = "<<this<<" Constructing B2"
              <<" &this->b2="<<&b2<<" this->b2="<<b2<<endl;}
	~B2(){
		cout<<"this = "<<this<<" Deconstructor B2"<<endl;
	}
};
class B3
{
//int b3;
public:
	B3()
	{ //b3 = 0; 
	cout<<"this = "<<this<<" Constructing B3"<<" this="<<this
              <<endl;}
	~B3(){
		cout<<"this = "<<this<<" Deconstructor B3"<<endl;
	}
};

class B4{
public:
	B4(){ 
	cout<<"this = "<<this<<" Constructing B4"<<" this="<<this
              <<endl;
	}
	~B4(){
		cout<<"this = "<<this<<" Deconstructor B4"<<endl;
	}
};
class D : public B2,public B1,public B3
{ 
  public: 
	  /*C(int a, int b, int c, int d)
	     :B1(a), memB2(d), memB1(c),    B2(b) {  
			 cout<<"this = "<<this << " Constructor C"<<endl;
	  }*/
	  D(int a, int b, int c, int d)
	     :B1(a),    B2(b) {  
			 cout<<"this = "<<this << " Constructor D"<<endl;
	  }
	  ~D(){
		  cout<<"this = "<<this<<" DeConstructor D"<<endl;
	  }
	  void print(){
		  cout<<"***************************************"<<endl;
		  cout<<"\tD: this="<<this<<endl;
		  cout<<"\tD: &this->b1" <<&b1<<" this->b1="<<b1<<endl;
		  cout<<"\tD: &this->b2" <<&b2<<" this->b2="<<b2<<endl;
		  //cout<<"\tC: &this->memB1=" <<&memB1<<endl;
		  //cout<<"\tC: &this->memB2=" <<&memB2<<endl;
		  cout<<"\tD: &this->memB3="<<&memB3<<endl;
		  cout<<"\tD: &this->memB3_2="<<&memB3_2<<endl;
		  cout<<"\tD: &this->memB4_1="<<&memB4_1<<endl;
		  cout<<"\tD: &this->memB4_2="<<&memB4_2<<endl;
		  cout<<"\tD: &this->memB3_5="<<&memB3_5<<endl;
		  cout<<"\tD: &this->dd="<<&dd<<endl;
		  cout<<"\tD: sizeof(D)="<<sizeof(*this)<<endl;
		  //cout<<"\tC: sizeof(B1)="<<sizeof(memB1)<<endl;
		  //cout<<"\tC: sizeof(B2)="<<sizeof(memB2)<<endl;
		  cout<<"\tD: sizeof(B3)="<<sizeof(memB3)<<endl;
	  }
  private: 
	 //B1 memB1;
	 //B2 memB2;
	 B3 memB3;
	 B3 memB3_2;
	 B4 memB4_1;
	 B4 memB4_2;
	 B3 memB3_5;
	 int dd;
};

int main()
{

	{
		D d(5, 6, 7, 8);
		d.print();
		cout<<"sizeof(d)="<<sizeof(d)<<endl;
	}
	system("pause");
	return 0;
}

VS2012(win32)结果:

由上图得到,派生类D的对象d的内存分配情况:

在Ubuntu16 g++ 5.4.0中,继承的空基类的地址与派生类对象的首地址相同。

 

2. 多继承中,存在两个空基类,派生类的大小计算

#include<iostream>

using namespace std;

class B1
{
protected:
	int b1;
  public:
     B1(int i)
	{ b1 = i;
	 cout<<"this = "<<this<<" Constructing B1"
              <<" &this->b1="<<&b1<<" this->b1="<<b1<<endl;}
	 ~B1(){
		 cout<<"this = "<<this<<" Deconstructor B1"<<endl;
	 }
};
class B2
{
protected:
	int b2;
 public:
	B2(int j)
	{b2 = j;
	cout<<"this = "<<this<<" Constructing B2"
              <<" &this->b2="<<&b2<<" this->b2="<<b2<<endl;}
	~B2(){
		cout<<"this = "<<this<<" Deconstructor B2"<<endl;
	}
};
class B3
{
//int b3;
public:
	B3()
	{ //b3 = 0; 
	cout<<"this = "<<this<<" Constructing B3"<<" this="<<this
              <<endl;}
	~B3(){
		cout<<"this = "<<this<<" Deconstructor B3"<<endl;
	}
};

class B4{
public:
	B4(){ 
	cout<<"this = "<<this<<" Constructing B4"<<" this="<<this
              <<endl;
	}
	~B4(){
		cout<<"this = "<<this<<" Deconstructor B4"<<endl;
	}
};

class C : public B2,public B1,public B3, public B4
{ 
  public: 
	  /*C(int a, int b, int c, int d)
	     :B1(a), memB2(d), memB1(c),    B2(b) {  
			 cout<<"this = "<<this << " Constructor C"<<endl;
	  }*/
	  C(int a, int b, int c, int d)
	     :B1(a),    B2(b) {  
			 cout<<"this = "<<this << " Constructor C"<<endl;
	  }
	  ~C(){
		  cout<<"this = "<<this<<" DeConstructor C"<<endl;
	  }
	  void print(){
		  cout<<"***************************************"<<endl;
		  cout<<"\tC: this="<<this<<endl;
		  cout<<"\tC: &this->b1" <<&b1<<" this->b1="<<b1<<endl;
		  cout<<"\tC: &this->b2" <<&b2<<" this->b2="<<b2<<endl;
		  //cout<<"\tC: &this->memB1=" <<&memB1<<endl;
		  //cout<<"\tC: &this->memB2=" <<&memB2<<endl;
		  cout<<"\tC: &this->memB3="<<&memB3<<endl;
		  cout<<"\tC:: &this->memB3_2="<<&memB3_2<<endl;
		  cout<<"\tC: &this->memB4="<<&memB4<<endl;
		  cout<<"\tC: &this->memB4_2="<<&memB4_2<<endl;
		  cout<<"\tC: &this->cc="<<&cc<<endl;
		  cout<<"\tC: sizeof(C)="<<sizeof(*this)<<endl;
		  //cout<<"\tC: sizeof(B1)="<<sizeof(memB1)<<endl;
		  //cout<<"\tC: sizeof(B2)="<<sizeof(memB2)<<endl;
		  cout<<"\tC: sizeof(B3)="<<sizeof(memB3)<<endl;
		  cout<<"\tC: sizeof(B3)="<<sizeof(B3)<<endl;
		  cout<<"\tC: sizeof(B4)="<<sizeof(B4)<<endl;
	  }
  private: 
	 //B1 memB1;
	 //B2 memB2;
	 B3 memB3;
	 B3 memB3_2;
	 B4 memB4;
	 B4 memB4_2;
	 int cc;
};
int main()
{
	{ 
		C c(1, 2, 3, 4);
	    c.print();
		cout<<"sizoef(c)="<<sizeof(c)<<endl;
	}
	system("pause");
	return 0;
}

VS2012(win32)运行结果(ubuntu g++ 5.4.0的运行结果不同):

VS2012(win32)和ubuntu g++ 5.4.0派生类C的对象c的内存分布:

3. 多继承中,存在三个空基类,派生类的大小计算

直接给出结果(VS2012(win32)):(前两个继承的空基类各分配了一个字节,第3个继承空基类没有分配空间,派生类中定义的空基类的对象,均分配了一个字节,然后在计算派生类大小时,不足4个字节的进行上取整补全。)

ubuntu16 g++5.4.0中,派生类中的空基类均不分配空间,且继承的空基类的地址与派生类对象的首地址相同。

There may be some mistakes in this blog. So, any suggestions and comments are welcome!

[Reference]

[1] https://blog.csdn.net/zhangxiao93/article/details/76038001

猜你喜欢

转载自blog.csdn.net/holmes_MX/article/details/87919608