有两个概念能够解释C++对象模型:html
语言中直接支持面向对象程序设计的部分。
对于各类支持的底层实现机制。前端
数据成员分为静态和非静态,成员函数有静态非静态以及虚函数ios
class data members:static和nonstatic函数
class data functions:static、nonstatic和virtual布局
好比:spa
class Base { public: Base(int i) :baseI(i){}; int getI(){ return baseI; } static void countI(){}; virtual void print(void){ cout << "Base::print()"; } virtual ~Base(){} private: int baseI; static int baseS; };
简单对象模型:这个模型很是地简单粗暴。在该模型下,对象由一系列的指针组成,每个指针都指向一个数据成员或成员函数,也便是说,每一个数据成员和成员函数在类中所占的大小是相同的,都为一个指针的大小。这样有个好处——很容易算出对象的大小,不过赔上的是空间和执行期效率。因此这种对象模型并无被用于实际产品上。设计
表格驱动对象模型:把类中的数据分红了两个部分:数据部分与函数部分,并使用两张表格,一张存放数据自己,一张存放函数的地址(也即函数比成员多一次寻址),而类对象仅仅含有两个指针,分别指向上面这两个表。这样看来,对象的大小是固定为两个指针大小。这个模型也没有用于实际应用于真正的C++编译器上。3d
C++对象模型:正在使用的指针
在此模型下,nonstatic 数据成员被置于每个类对象中,而static数据成员被置于类对象以外。static与nonstatic函数也都放在类对象以外,而对于virtual 函数,则经过虚函数表+虚指针来支持:调试
原则:
对普通单继承而言
1 #include <iostream> 2 using namespace std; 3 4 class Base 5 { 6 public: 7 virtual void fun1(){ cout << "Base fun1" << endl; } 8 virtual void fun2(){ cout << "Base fun2" << endl; } 9 private: 10 int a; 11 }; 12 13 class Derive : public Base 14 { 15 public: 16 void fun2(){ cout << "Derive fun2" << endl; } 17 virtual void fun3(){} 18 private: 19 int b; 20 }; 21 22 int main() 23 { 24 Base b; 25 Derive d; 26 Base *p = &d; 27 p->fun1(); 28 p->fun2(); 29 30 system("pause"); 31 return 0; 32 }
输出:
调试:
对象模型:
事实上vs调试并不能看到完整信息(好比virtual fun3以及以后提到到虚基类指针),正确的应该是
这里讲的是不考虑菱形继承的多继承,由于菱形继承须要用到虚继承,放到以后考虑
原则:
1 #include <iostream> 2 using namespace std; 3 4 class Base1 5 { 6 public: 7 virtual void fun1(){} 8 private: 9 int m_base1; 10 }; 11 12 class Base2 13 { 14 public: 15 virtual void fun1(){} 16 virtual void fun2(){} 17 private: 18 int m_base2; 19 }; 20 21 class Derive : public Base1,public Base2 22 { 23 public: 24 void fun1(){} 25 virtual void fun3(){} 26 private: 27 int m_derive; 28 }; 29 30 int main() 31 { 32 Base1 b1; 33 Base2 b2; 34 Derive d; 35 36 cout <<"b1:" <<sizeof(b1) << endl; 37 cout << "b2:" << sizeof(b2) << endl; 38 cout <<"d:" << sizeof(d) << endl; 39 system("pause"); 40 return 0; 41 }
输出:
各个类对象的大小
调试:注意观察fun1
对象模型:
原则:
虚继承解决了菱形继承中最派生类拥有多个间接父类实例的状况
在C++对象模型中,虚继承而来的子类会生成一个隐藏的虚基类指针(vbptr),在Microsoft Visual C++中,虚基类表指针老是在虚函数表指针以后,于是,对某个类实例来讲,若是它有虚基类指针,那么虚基类指针可能在实例的0字节偏移处(该类没有vptr时,vbptr就处于类实例内存布局的最前面,不然vptr处于类实例内存布局的最前面),也可能在类实例的4字节偏移处。
虚基类表也由多个条目组成,条目中存放的是偏移值。
第一个条目存放虚基类表指针(vbptr)所在地址到该类内存首地址的偏移值
第2、第三...个条目依次为该类的最左虚继承父类、次左虚继承父类...的内存地址相对于虚基类表指针的偏移值。
1 #include <iostream> 2 using namespace std; 3 4 class Base 5 { 6 public: 7 virtual void fun1(){} 8 virtual void fun2(){} 9 private: 10 int m_base; 11 }; 12 13 class Derive : virtual public Base 14 { 15 public: 16 void fun1(){} 17 virtual void fun3(){} 18 private: 19 int m_derive; 20 }; 21 22 int main() 23 { 24 Base b; 25 Derive d; 26 27 system("pause"); 28 return 0; 29 }
对象模型:
菱形虚继承是多继承和虚继承的复合,直接画一个对象模型吧:
笔记原图: