C++对象模型
一、C++对象内存模型认知模式
二、C++类数据成员内存模型
<1>无继承情况
<2>单继承与多重继承的情况
<3>虚继承
观点:
<a>非静态数据成员在类实例内存中在内存中连续存储;
<b>静态数据成员不占类实例内存;
<c>一个子类的内存模型可以看成就是父类的各数据成员与自己新添加的数据成员的总和;
<d>虚拟继承中,关于对象的数据成员内存布局问题有多种策略,自己注意测试;
三、C++类函数成员的内存模型
相关概念:C++的指针类型
指针类型中包含了一个类似于 sizeof 的信息,或者其他的辅助信息,个sizeof的信息就告诉了系统你应该拿几个(连续)地址上的字节返回给我(猜想)
<1>静态函数成员
静态函数成员从实现的角度上讲,最大的特点就是编译器在处理静态函数成员的时候不会讲一个this指针压入其参数列表
特点:
<a>它不能直接读写class内的非静态成员,无论是数据成员还是函数成员;
<b>它不能声明为const或是virtual;
<c>它不是由类的实例来调用的,而是类作用域界定符;
<2>非静态函数成员
非静态函数的实现机制:
<a>改写非静态成员函数的函数原型,压入一个额外的this指针到成员函数的参数列表中,目的就是提供一个访问类的实例的非静态数据/函数成员的渠道;
<b>将每一个对非静态数据/函数成员的读写操作改为经由this指针来读写;
<c>最惊讶的一步是,将成员函数改写为一个外部函数——Gotcha!这就是为什么sizeof(Class)的时候不会将非虚函数地址指针计算进去的原因,因为(非静态)成员函数都被搬到类的外面去了,并借助Name Mangling算法将函数名转化为一个全局唯一的名字。
<3>虚拟成员函数
<3.1>单继承下的虚拟成员函数
class Parent {
public:
Parent():nParent(888) {}
virtual void sayhello() { cout << "Perent()::sayhello()" << endl; }
virtual void walk() { cout << "Parent::walk()" << endl; }
virtual void sleep() { cout << "Parent::sleep()" << endl; }
protected:
int nParent;
};
class Child : public Parent {
public:
Child():nChild(88) {}
virtual void sayhello() { cout << "Child::sayhello()" << endl; }
virtual void walk_child() { cout << "Child::walk_child()" << endl; }
virtual void sleep_child() { cout << "Child::sleep_child()" << endl; }
protected:
int nChild;
};
class GrandChild : public Child{
public:
GrandChild():nGrandchild(8) {}
virtual void sayhello() { cout << "GrandChild::sayhello()" << endl; }
virtual void walk_child() { cout << "GrandChild::walk_child()" << endl; }
virtual void sleep_grandchild() { cout << "GrandChild::sleep_grandchild()" << endl; }
protected:
int nGrandchild;
};
现在,我们使用一个int** pVtbl 来作为遍历对象内存布局的指针,这样可以方便地像使用数组一样来遍历所有的成员包括其虚函数表:
typedef void(*Fun)(void);
GrandChild gc;
int** pVtbl = (int**)&gc;
cout << "[0] GrandChild::_vptr->" << endl;
for(int i=0; (Fun) pVtbl[0][i]!=NULL; i++){
pFun = (Fun) pVtbl[0][i];
cout << " ["<<i<<"] ";
pFun();
}
cout << "[1] Parent.nParent = " << (int)pVtbl[1] << endl;
cout << "[2] Child.nChild = " << (int) pVtbl[2] << endl;
cout << "[3] GrandChild.nGrandchild = " << (int) pVtbl[3] << endl;
运行结果如下:
三个类的内存布局图
<3.2>多重继承下的虚拟函数
多重继承下的虚拟函数主要有一下几个麻烦:
<a>几个父类都声明了相同原型的virtual函数;
<b>有不止一个父类将其析构函数声明为虚拟;
<c>一般的虚拟函数问题;
class Parent1{
public:
Parent1() : data_parent1(0.0){}
virtual ~Parent1(){cout<<"Parent1::~Parent1()"<<endl;}
virtual void speakClearly(){cout<<"Parent1::speakClearly()"<<endl;}
virtual Parent1* clone() const{cout<<"Parent1::clone()"<<endl; return null;}
protected:
int data_parent1;
};
class Parent2{
public:
Parent2() : data_parent2(1.0){}
virtual ~Parent2(){cout<<"Parent2::~Parent2()"<<endl;}
virtual void mumble(){cout<<"Parent2::mumble()"<<endl;}
virtual Parent2* clone() const{cout<<"Parent2::clone()"<<endl; return null;}
protected:
int data_parent2;
};
class Child : public Parent1, public Parent2
{
public:
Child() : data_child(2.0){}
virtual ~Child(){cout<<"Child::~Child()"<<endl;}
virtual Child* clone() const{cout<<"Child::clone()"<<endl; return null;}
protected:
int data_child;
};
就内
内存布局图:
代码验证:
typedef void(*Fun)(void);
int main()
{
Child c;
Fun pFun;
int** pVtbl = (int**)&c;
cout << "[0] Parent1::_vptr->" << endl;
pFun = (Fun)pVtbl[0][0];
cout << " [0] ";
pFun();
pFun = (Fun)pVtbl[0][1];
cout << " [1] ";
pFun();
cout << " Parent1.data_parent1 = " << (int)pVtbl[1] << endl;
int s = sizeof(Parent1)/4;
cout << "[" << s << "] Parent2::_vptr->"<<endl;
pFun = (Fun)pVtbl[s][0];
cout << " [0] "; pFun();
pFun = (Fun)pVtbl[s][1];
cout << " [1] "; pFun();
s++;
cout << " Parent2.data_parent2 = " << (int)pVtbl[s] << endl;
s++;
cout << "[3] Child.data_child = " << (int)pVtbl[s] << endl;
}
运行结果:
备注:虚拟继承的出现就是为了解决重复继承中多个间接父类的问题的,
四、C++对象模型总结
<1>非静态数据成员都存放在对象所跨有的地址空间中,静态数据成员则存放于对象所跨有的地址空间之外;
<2>非虚拟成员函数(静态和非静态)也存放于对象所跨有的地址空间之外,且编译器将其改写为普通的非成员函数的形式(以求降低调用开销);
<3>对于虚拟成员函数,则借助vtbl和vptr支持。
<4>对于继承关系,子类对象跨有的地址空间中包含了父类对象的实体,通过嵌入type-info信息进行识别和虚函数调用。
内容参考:http://blog.csdn.net/outmanlee/article/details/6396621
相关阅读:http://www.cnblogs.com/archy_yu/archive/2012/12/18/2822911.html