c++对象内存布局

c++中,有两种数据成员:staticnonstatic,三种成员函数:staticnonstaticvirtual。c++对象模型:nonstatic data members被置于类对象之内,而staticdata members被置于类对象之外。static和nonstaticfunction members被放在类对象之外。virtual函数则由如下步骤支持:

1)每个class产生一个vtbl(虚函数表),vtbl中存放其类的类型type_info信息和虚函数指针;

 2)对象内添加一个指向vtbl的指针vptrvptr的设定和重置由类的构造函数、析构函数以及拷贝赋值运算符自动完成。


下面讨论各种情况下c++对象内存布局的情况,以下我做的实验用的都是VS 2013编译器,不同编译器下可能结果有些不同

一、单继承(带成员变量、虚函数、虚函数覆盖)

1、例子

#include<iostream>
using namespace std;

class Parent{
public:
	int iparent;
	Parent() :iparent(10){}
	virtual void f(){ cout << "Parent::f()" << endl; }
	virtual void g(){ cout << "Parent::g()" << endl; }
	virtual void h(){ cout << "Parent::h()" << endl; }
};
class Child:public Parent{
public:
	int ichild;
	Child() :ichild(100){}
	virtual void f(){ cout << "Child::f()" << endl; }
	virtual void g_child(){ cout << "Child::g_child()" << endl; }
	virtual void h_child(){ cout << "Child::h_child()" << endl; }
};
class GrandChild:public Child{
public:
	int igrandchild;
	GrandChild() :igrandchild(1000){}
	virtual void f() { cout << "GrandChild::f()" << endl; }
	virtual void g_child() { cout << "GrandChild::g_child()" << endl; }
	virtual void h_grandchild() { cout << "GrandChild::h_grandchild()" << endl; }
};
int main(){
	typedef void(*pFun)(); //定义一个函数指针,参数列表空,返回值void
	pFun fun = NULL;
	GrandChild gc;

	int** pVtab = (int**)&gc;
	cout << "[0] GrandChild::vptr->" << endl;
	for (int i = 0; (pFun)pVtab[0][i] != NULL; ++i){
		fun = (pFun)pVtab[0][i];
		cout << "    [" << i << "] ";
		fun();
	}
	cout << "[1] Parent.iparent = " << (int)pVtab[1] << endl;
	cout << "[2] Child.ichild = " << (int)pVtab[2] << endl;
	cout << "[3] GrandChild.igrandchild = " << (int)pVtab[3] << endl;

	system("pause");
	return 0;
}
2、运行结果


3、类继承关系

4、GrandChild类对象布局

5、说明

1) 虚表指针在最前面的位置

2) 父类的虚函数在子类的虚函数前面

3)成员变量根据其继承和声明顺序依次放在后面

4)在单一的继承中,被overwrite的虚函数在虚函数表中得到了更新

二、多继承(带成员变量、虚函数、虚函数覆盖)

1、例子

#include<iostream>
using namespace std;

class Base1 {
public:
	int ibase1;
	Base1() :ibase1(10) {}
	virtual void f() { cout << "Base1::f()" << endl; }
	virtual void g() { cout << "Base1::g()" << endl; }
	virtual void h() { cout << "Base1::h()" << endl; }

};
class Base2 {
public:
	int ibase2;
	Base2() :ibase2(20) {}
	virtual void f() { cout << "Base2::f()" << endl; }
	virtual void g2() { cout << "Base2::g()" << endl; }
	virtual void h() { cout << "Base2::h()" << endl; }
};
class Base3 {
public:
	int ibase3;
	Base3() :ibase3(30) {}
	virtual void f() { cout << "Base3::f()" << endl; }
	virtual void g() { cout << "Base3::g()" << endl; }
	virtual void h() { cout << "Base3::h()" << endl; }
};
class Derive : public Base1, public Base2, public Base3 {
public:
	int iderive;
	Derive() :iderive(100) {}
	virtual void f() { cout << "Derive::f()" << endl; }
	virtual void g2() { cout << "Derive::g2()" << endl; }
	virtual void h1(){ cout << "Derive::h1()" << endl; }
};

int main(){
	typedef void(*pFun)(); //定义一个函数指针,参数列表空,返回值void
	pFun fun = NULL;
	Derive d;

	int** pVtab = (int**)&d;
	cout << "[0] Base1::vptr->" << endl;
	for (int i = 0; (pFun)pVtab[0][i] != NULL; ++i){
		fun = (pFun)pVtab[0][i];
		cout << "    [" << i << "] ";
		fun();
	}
	cout << "[1] Base1.ibase1 = " << (int)pVtab[1] << endl;

	int s = sizeof(Base1) / 4; //偏移距离
	cout << "[" << s << "] Base2::vptr->" << endl;
	for (int i = 0; (pFun)pVtab[s][i] != NULL; ++i){
		fun = (pFun)pVtab[s][i];
		cout << "    [" << i << "] ";
		fun();
	}
	cout << "[" << s + 1 << "] Base2.ibase2 = " << (int)pVtab[s + 1] << endl;

	s = s + sizeof(Base2) / 4;
	cout << "[" << s << "] Base3::vptr->" << endl;
	for (int i = 0; (pFun)pVtab[s][i] != NULL; ++i){
		fun = (pFun)pVtab[s][i];
		cout << "    [" << i << "] ";
		fun();
	}
	cout << "[" << s << "] Base3.ibase3 = " << (int)pVtab[++s] << endl;
	cout << "[" << s << "] Derive.iderive = " << (int)pVtab[++s] << endl;

	system("pause");
	return 0;
}
2、运行结果


3、类继承关系


4、Derive类对象内存布局


5、说明

1)每个父类都有自己的虚表

2)子类的成员函数被放到了第一个父类的表中

3)内存布局中,其父类布局依次按声明顺序排列

4)每个父类的虚表中的f()函数都被overwrite成了子类的f()。Base2父类的g2()函数被overwrite成了子类的g2()。

三、重复多重继承(带成员变量、虚函数、虚函数覆盖)

1、例子

#include<iostream>
using namespace std;

class B{
public:
	int ib;
	char cb;
public:
	B() :ib(0), cb('B') {}
	virtual void f() { cout << "B::f()" << endl; }
	virtual void Bf() { cout << "B::Bf()" << endl; }
};
class B1 : public B{
public:
	int ib1;
	char cb1;
public:
	B1() :ib1(11), cb1('1') {}
	virtual void f() { cout << "B1::f()" << endl; }
	virtual void f1() { cout << "B1::f1()" << endl; }
	virtual void Bf1() { cout << "B1::Bf1()" << endl; }
};
class B2 : public B{
public:
	int ib2;
	char cb2;
public:
	B2() :ib2(12), cb2('2') {}
	virtual void f() { cout << "B2::f()" << endl; }
	virtual void f2() { cout << "B2::f2()" << endl; }
	virtual void Bf2() { cout << "B2::Bf2()" << endl; }
};
class D : public B1, public B2{
public:
	int id;
	char cd;
public:
	D() :id(100), cd('D') {}
	virtual void f() { cout << "D::f()" << endl; }
	virtual void f1() { cout << "D::f1()" << endl; }
	virtual void f2() { cout << "D::f2()" << endl; }
	virtual void Df() { cout << "D::Df()" << endl; }
};

int main(){
	typedef void(*pFun)(); //定义一个函数指针,参数列表空,返回值void
	pFun fun = NULL;
	D d;

	int** pVtab = (int**)&d;
	int s = 0;//偏移地址
	cout << "[" << s << "] B1::vptr->" << endl;
	for (int i = 0; (pFun)pVtab[s][i] != NULL; ++i){
		fun = (pFun)pVtab[s][i];
		cout << "    [" << i << "] ";
		fun();
	}
	cout << "[" << ++s << "] B::ib = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] B::cb = " << (char)pVtab[s] << endl;
	cout << "[" << ++s << "] B1::ib1 = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] B1::cb1 = " << (char)pVtab[s] << endl;

	cout << "[" << ++s << "] B2::vptr->" << endl;
	for (int i = 0; (pFun)pVtab[s][i] != NULL; ++i){
		fun = (pFun)pVtab[s][i];
		cout << "    [" << i << "] ";
		fun();
	}
	cout << "[" << ++s << "] B::ib = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] B::cb = " << (char)pVtab[s] << endl;
	cout << "[" << ++s << "] B2::ib2 = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] B2::cb2 = " << (char)pVtab[s] << endl;

	cout << "[" << ++s << "] D::id = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] D::cd = " << (char)pVtab[s] << endl;

	system("pause");
	return 0;
}
2、运行结果


3、类继承关系


4、D类对象内存布局


5、说明

D d;
d.ib = 0;               //二义性错误
d.B1::ib = 1;           //正确
d.B2::ib = 2;           //正确

上面例子中的最后两条语句存取的是两个变量。虽然我们消除了二义性的编译错误,但B类在D中还是有两个实例,这种继承造成了数据的重复,称为重复继承。重复的基类数据成员可能并不是我们想要的。所以,C++引入了虚基类的概念。

四、单一虚拟单继承(带成员变量、虚函数、虚函数覆盖)

1、例子

#include<iostream>
using namespace std;

class Parent{
public:
	int iparent;
	char cparent;
public:
	Parent() :iparent(0), cparent('P') {}
	virtual void f() { cout << "Parent::f()" << endl; }
	virtual void Pf() { cout << "Parent::Pf()" << endl; }
};
class Child : virtual public Parent{
public:
	int ichild;
	char cchild;
public:
	Child() :ichild(10), cchild('C') {}
	virtual void f() { cout << "Child::f()" << endl; }
	virtual void f1() { cout << "Child::f1()" << endl; }
	virtual void Cf1() { cout << "Child::Cf1()" << endl; }
};
class GrandChild :public Child{
public:
	int igrandchild;
	char cgrandchild;
	GrandChild() :igrandchild(100),cgrandchild('G') {}
	virtual void f() { cout << "GrandChild::f()" << endl; }
	virtual void f1() { cout << "GrandChild::f1()" << endl; }
	virtual void g() { cout << "GrandChild::g()" << endl; }
};
int main(){
	typedef void(*pFun)(); //定义一个函数指针,参数列表空,返回值void
	GrandChild grandchild;;

	int** pVtab = (int**)&grandchild;
	int s = 0;//偏移地址
	cout << "[" << s << "] grandchild::vptr->" << endl;
	for (int i = 0; (pFun)pVtab[s][i] != NULL; ++i){
		cout << "    [" << i << "] ";
		((pFun)pVtab[s][i])();
	}
	//虚继承时,除了有虚函数表外,还有虚基类表
	cout << "[" << ++s << "] grandchild;::vbptr->" << endl;//grandchild虚基类表中的第一项为grandchild的地址与grandchild的虚基类表指针的地址的偏移量
	for (int i = 0; (pFun)pVtab[s][i] != NULL; ++i){  //grandchild虚基类表中的第二项为Parent的地址相对于grandchild虚基类表地址的偏移量,这里应该为24
		cout << "    [" << i << "] ";
		cout << pVtab[s][i] << endl;
	}
	cout << "[" << ++s << "] Child::ichild = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] Child::cchild = " << (char)pVtab[s] << endl;
	cout << "[" << ++s << "] GrandChild::igrandchild = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] GrandChild::cgrandchild = " << (char)pVtab[s] << endl;
	cout << "[" << ++s << "] " << (pFun)pVtab[s] << endl; //NULL

	cout << "[" << ++s << "] Parent::vptr->" << endl;
	for (int i = 0; /*(pFun)pVtab[s][i] != NULL*/i<2; ++i){//这里不能用(pFun)pVtab[s][i] != NULL,原因不清楚,可能是VS2013编译器的一个bug
		cout << "    [" << i << "] ";
		((pFun)pVtab[s][i])();
	}
	cout << "[" << ++s << "] Parent::iparent = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] Parent::cparent = " << (char)pVtab[s] << endl;

	system("pause");
	return 0;
}

2、运行结果


3、类继承关系


4、GrandChild类对象内存布局


5、说明

1)虚基类的信息是独立的,虚基类与子类的信息用NULL隔离开来

2)虚继承时,还有一张虚基类表,对象中有一个虚基类表指针指向虚基类表

五、钻石型的虚拟多重继承(带成员变量、虚函数、虚函数覆盖)

1、例子

#include<iostream>
using namespace std;

class B{
public:
	int ib;
	char cb;
public:
	B() :ib(0), cb('B') {}
	virtual void f() { cout << "B::f()" << endl; }
	virtual void Bf() { cout << "B::Bf()" << endl; }
};
class B1 : virtual public B{
public:
	int ib1;
	char cb1;
public:
	B1() :ib1(11), cb1('1') {}
	virtual void f() { cout << "B1::f()" << endl; }
	virtual void f1() { cout << "B1::f1()" << endl; }
	virtual void Bf1() { cout << "B1::Bf1()" << endl; }

};
class B2 : virtual public B{
public:
	int ib2;
	char cb2;
public:
	B2() :ib2(12), cb2('2') {}
	virtual void f() { cout << "B2::f()" << endl; }
	virtual void f2() { cout << "B2::f2()" << endl; }
	virtual void Bf2() { cout << "B2::Bf2()" << endl; }
};
class D : public B1, public B2{
public:
	int id;
	char cd;
public:
	D() :id(100), cd('D') {}
	virtual void f() { cout << "D::f()" << endl; }
	virtual void f1() { cout << "D::f1()" << endl; }
	virtual void f2() { cout << "D::f2()" << endl; }
	virtual void Df() { cout << "D::Df()" << endl; }
};

int main(){
	typedef void(*pFun)(); //定义一个函数指针,参数列表空,返回值void
	pFun fun = NULL;
	D d;

	int** pVtab = (int**)&d;
	int s = 0;//偏移地址
	cout << "[" << s << "] B1::vptr->" << endl;
	for (int i = 0; (pFun)pVtab[s][i] != NULL; ++i){
		cout << "    [" << i << "] ";
		fun = (pFun)pVtab[s][i]; //用函数指针
		fun();
	}
	cout << "[" << ++s << "] B1::vbptr->" << endl;//Base1虚基类表中的第一项为虚基类Base1的地址与Base1的虚基类表指针的地址的偏移量
	for (int i = 0; i<2; ++i){  //Base1虚基类表中的第二项为Base1的虚基类Base的地址相对于Base1虚基类表地址的偏移量,这里应该为40
		cout << "    [" << i << "] ";
		cout << pVtab[s][i] << endl;
	}
	cout << "[" << ++s << "] B1::ib1 = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] B1::cb1 = " << (char)pVtab[s] << endl;

	cout << "[" << ++s << "] B2::vptr->" << endl;
	for (int i = 0; (pFun)pVtab[s][i] != NULL; ++i){
		cout << "    [" << i << "] ";
		((pFun)pVtab[s][i])(); //与fun = (pFun)pVtab[0][i]; //用函数指针 fun();相同
	}
	cout << "[" << ++s << "] B2::vbptr->" << endl;//Base2虚基类表中的第一项为虚基类Base2的地址与Base2的虚基类表指针的地址的偏移量
	for (int i = 0; i<2; ++i){//Base2虚基类表中的第二项为Base2的虚基类Base的地址相对于Base2虚基类表地址的偏移量,这里应该为24
		cout << "    [" << i << "] ";
		cout << pVtab[s][i] << endl;
	}
	cout << "[" << ++s << "] B2::ib2 = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] B2::cb2 = " << (char)pVtab[s] << endl;
	cout << "[" << ++s << "] D::id = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] D::cd = " << (char)pVtab[s] << endl;
	cout << "[" << ++s << "] " << pVtab[s] << endl;

	cout << "[" << ++s << "] B::vptr->" << endl;
	for (int i = 0; (pFun)pVtab[s][i] != NULL; ++i){
		cout << "    [" << i << "] ";
		((pFun)pVtab[s][i])();
	}
	cout << "[" << ++s << "] b::ib = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] b::cb = " << (char)pVtab[s] << endl;
	system("pause");
	return 0;
}
2、运行结果


3、类继承关系


4、D类对象内存布局


5、说明

1)虚基类的信息是独立的,虚基类与子类的信息用NULL隔离开来

2)虚继承时,每一个直接子类都有一张虚基类表,每一个直接子类对象中有一个虚基类表指针指向虚基类表

以上就是c++对象内存布局的各种情况,但是RTTI信息在上面都没有体现。

简单来说RTTI(Run-Time Type Information,通过运行时类型信息)程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。在C++中存在虚函数,也就存在了多态性,对于多态性的对象,在程序编译时可能会出现无法确定对象的类型的情况。当类中含有虚函数时,其基类的指针就可以指向任何派生类的对象,这时就有可能不知道基类指针到底指向的是哪个对象的情况,类型的确定要在运行时利用运行时类型标识做出。

下面的一个例子就简单说明下VS 2013中RTTI信息是保存在哪里的。

#include<iostream>
#include<typeinfo>
#include<Windows.h>
#include<string>
using namespace std;

struct PMD
{
	int mdisp;  //member displacement
	int pdisp;  //vbtable displacement
	int vdisp;  //displacement inside vbtable
};
struct RTTIBaseClassDescriptor
{
	struct TypeDescriptor* pTypeDescriptor; //type descriptor of the class
	DWORD numContainedBases; //number of nested classes following in the Base Class Array
	struct PMD where;        //pointer-to-member displacement info
	DWORD attributes;        //flags, usually 0
};
struct RTTIClassHierarchyDescriptor
{
	DWORD signature;      //always zero?
	DWORD attributes;     //bit 0 set = multiple inheritance, bit 1 set = virtual inheritance
	DWORD numBaseClasses; //number of classes in pBaseClassArray
	struct RTTIBaseClassArray* pBaseClassArray;
};
struct TypeDescriptor
{
	DWORD ptrToVTable;
	DWORD spare;
	char name[];
};
struct RTTICompleteObjectLocator
{
	DWORD signature; //always zero ?
	DWORD offset;    //offset of this vtable in the complete class
	DWORD cdOffset;  //constructor displacement offset
	struct TypeDescriptor* pTypeDescriptor; //TypeDescriptor of the complete class
	struct RTTIClassHierarchyDescriptor* pClassDescriptor; //describes inheritance hierarchy
};

class B{
public:
	int ib;
	char cb;
public:
	B() :ib(0), cb('B') {}
	virtual void f() { cout << "B::f()" << endl; }
	virtual void Bf() { cout << "B::Bf()" << endl; }
};
class B1 : virtual public B{
public:
	int ib1;
	char cb1;
public:
	B1() :ib1(11), cb1('1') {}
	virtual void f() { cout << "B1::f()" << endl; }
	virtual void f1() { cout << "B1::f1()" << endl; }
	virtual void Bf1() { cout << "B1::Bf1()" << endl; }

};
class B2 : virtual public B{
public:
	int ib2;
	char cb2;
public:
	B2() :ib2(12), cb2('2') {}
	virtual void f() { cout << "B2::f()" << endl; }
	virtual void f2() { cout << "B2::f2()" << endl; }
	virtual void Bf2() { cout << "B2::Bf2()" << endl; }
};
class D : public B1, public B2{
public:
	int id;
	char cd;
public:
	D() :id(100), cd('D') {}
	virtual void f() { cout << "D::f()" << endl; }
	virtual void f1() { cout << "D::f1()" << endl; }
	virtual void f2() { cout << "D::f2()" << endl; }
	virtual void Df() { cout << "D::Df()" << endl; }
};

int main(){
	typedef void(*pFun)(); //定义一个函数指针,参数列表空,返回值void
	pFun fun = NULL;
	D d;

	cout << "对象d的起始内存地址:" <<(int*)(&d) << endl;
	int** pVtab = (int**)&d;
	int s = 0;//偏移地址
	cout << "type_info信息:" << ((int*)*(int*)(&d) - 1) << endl;
	RTTICompleteObjectLocator str =
		*((RTTICompleteObjectLocator*)*((int*)*(int*)(&d) - 1));
	//abstract class name from RTTI
	string classname(str.pTypeDescriptor->name);
	classname = classname.substr(4, classname.find("@@") - 4);
	cout << classname << endl;
	cout << "[" << s << "] B1::vptr->" << endl;
	for (int i = 0; (pFun)pVtab[s][i] != NULL; ++i){
		cout << "    [" << i << "] ";
		fun = (pFun)pVtab[s][i]; //用函数指针
		fun();
	}
	cout << "[" << ++s << "] B1::vbptr->" << endl;//B1虚基类表中的第一项为虚基类B1的地址与B1的虚基类表指针的地址的偏移量
	for (int i = 0; i<2; ++i){  //Base1虚基类表中的第二项为B1的虚基类B的地址相对于B1虚基类表地址的偏移量,这里应该为40
		cout << "    [" << i << "] ";
		cout << pVtab[s][i] << endl;
	}
	cout << "[" << ++s << "] B1::ib1 = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] B1::cb1 = " << (char)pVtab[s] << endl;

	cout << "[" << ++s << "] B2::vptr->" << endl;
	for (int i = 0; (pFun)pVtab[s][i] != NULL; ++i){
		cout << "    [" << i << "] ";
		((pFun)pVtab[s][i])(); //与fun = (pFun)pVtab[0][i]; //用函数指针 fun();相同
	}
	cout << "[" << ++s << "] B2::vbptr->" << endl;//Base2虚基类表中的第一项为虚基类Base2的地址与Base2的虚基类表指针的地址的偏移量
	for (int i = 0; i<2; ++i){//Base2虚基类表中的第二项为Base2的虚基类Base的地址相对于Base2虚基类表地址的偏移量,这里应该为24
		cout << "    [" << i << "] ";
		cout << pVtab[s][i] << endl;
	}
	cout << "[" << ++s << "] B2::ib2 = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] B2::cb2 = " << (char)pVtab[s] << endl;
	cout << "[" << ++s << "] D::id = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] D::cd = " << (char)pVtab[s] << endl;
	cout << "[" << ++s << "] " << pVtab[s] << endl;

	cout << "[" << ++s << "] B::vptr->" << endl;
	for (int i = 0; (pFun)pVtab[s][i] != NULL; ++i){
		cout << "    [" << i << "] ";
		((pFun)pVtab[s][i])();
	}
	cout << "[" << ++s << "] b::ib = " << (int)pVtab[s] << endl;
	cout << "[" << ++s << "] b::cb = " << (char)pVtab[s] << endl;
	system("pause");
	return 0;
}
运行结果


从例子程序和运行结果来看,VS 2013编译器把RTTI信息放在了虚函数表首地址的前面4个字节。RTTI我就简单的看了一下,所以也就不深入讨论了。


参考:

http://blog.csdn.net/haoel/article/details/3081328

http://blog.csdn.net/haoel/article/details/3081385

http://blog.jobbole.com/86843/

RTTI:http://www.cnblogs.com/zhyg6516/archive/2011/03/07/1971898.html

版权声明:本文为博主原创文章,未经博主允许不得转载。

原文地址:https://www.cnblogs.com/ruan875417/p/4785456.html