c++类的内存结构(单继承)
来源:互联网 发布:数据加密的目的是 编辑:程序博客网 时间:2024/06/05 04:04
关于c++类的内存的文章在网上有许多,但是,大多是一段段代码和说明,偶尔有图文并茂的,总觉不是很详细,没有一目了然之感觉
并且大部分也没有涉及虚析构,也没有说明访问标号的影响(虽然事实是没有影响)。而这块内容对于理解多态、重写、继承的构造和析构又是足够重要的。
终于下定决心,自己动手,在参考多方资料后,终有此文。
一、基类内存结构
1.1 不带虚函数、虚析构的基类:
class Base{public:Base():i(3),j(4){}int i;private:int j;};这是最简单的情况,直接给图:
数据成员的排列将基于声明顺序,尽管数据成员j是私有的,这并没有什么影响。
1.2 不带虚函数、带虚析构的基类:
class Base{public:Base():i(3),j(4){}virtual ~Base(){}int i;private:int j;};稍复杂一点,像是这样:
多了一个虚函数表__vfptr[],它可以看做是一个指向函数的指针数组(void**),其成员都是指向函数的指针(void*),更确切的说,都指向此类的虚函数,除了最后一个结束标记(值为0,这里编译器是vs,其他编译器可能有所不同),大小就是此类中含虚函数的个数(含继承的)+1(结束标记),测试程序将在后面给出。
图中&bObj是Base类的一个对象bObj的地址,图中可以看到它的值是虚表的地址,那么&bObj的类型(void***),是指向虚表__vfptr的指针。
因为我们显式声明了虚析构,所以虚表的大小是2,但是__vfptr[0]并非像预想中的直接指向~Base();而是指向“析构代理函数”。
所谓析构代理函数,是编译器替我们生成的虚成员函数,它会动态的调用类的对应析构函数,并做析构相关的额外一些其他事情。
其类似实现原理参看这里:http://blog.csdn.net/jiangdf/article/details/8917020
1.3 带虚函数、不带虚析构的基类:
class Base{public:Base():i(3),j(4){} virtual void f() { std::cout << "Base::f" << std::endl; } virtual void g() { std::cout << "Base::g" << std::endl; }int i;private:virtual void h() { std::cout << "Base::h" << std::endl; }int j;};这个基类有些怪异,含一个私有的虚函数,这比较少见,虽然他将被派生类继承,但不能被直接访问。更重要的是它没有虚析构函数,这是一种错误,为了全面起见,
罗列在此。
内存图将是这样的:
也是按照声明的顺序,虚函数表依次指向各虚函数,虽然h是私有的,这也没什么影响
1.4 含虚函数与虚析构的基类
class Base{public:Base():i(3),j(4){}virtual ~Base(){} virtual void f() { std::cout << "Base::f" << std::endl; } virtual void g() { std::cout << "Base::g" << std::endl; } int i;private: virtual void h() { std::cout << "Base::h" << std::endl; }int j;};这是最复杂、最正常的基类,图如下:
为了清晰起见,图中给出某次运行时的实际地址,这里相邻地址总是正好4字节,因为我的系统int与指针类型是4字节,
如果是其他类型,未必是4,还可能要考虑内存对齐的问题,不属于这篇文章的范畴……
正常基类的虚表总是以虚析构代理函数开头、中间接虚函数、以结束标记结尾,就算f比~Base()先声明,结果也是一样。
1.5 含纯虚函数的抽象基类
class Base{public:Base():i(3),j(4){}virtual ~Base(){} virtual void f()=0; virtual void g(){ std::cout << "Base::g" << std::endl; }int i;private:virtual void h(){ std::cout << "Base::h" << std::endl; }int j;};
由于抽象基类的对象没有办法构造,其指针类型也只能指向派生类实例,所以似乎没有办法测试基类的内存结构,但可以从派生类对象上大体推断:
其内存结构与1.4的图并无区别
二、派生类内存结构
2.1 继承自抽象类的(1.5)、重写纯虚函数的派生类
class Derived: public Base{public:virtual void f() { std::cout << "Derived::f" << std::endl; }};这里是为了承接1.5的"推断",给个最为简单的测试代码:
Derived dObj;Base* pb = &dObj;调试,展开pb之+,可以看到:
这里虽然是dObj的虚表, 但基本可以证明Base的虚表内存结构(将图中Derived换成Base即可)
2.2 继承自1.4,重写f、添加自己的虚函数和数据成员的派生类
class Derived: public Base{public:Derived():k(5){}virtual void f() { std::cout << "Derived::f" << std::endl; }virtual void f1() { std::cout << "Derived::f1" << std::endl; }private:virtual void g1() { std::cout << "Derived::g1" << std::endl; }int k;};这个是最正常用法,先上图
是时候给出测试代码了:
void Base::test(){typedef void (*Destructor)(unsigned int); // 函数指针typedef void(*Fun)(void); // 函数指针Destructor pfna, pfn0;Fun pfnb, pfnc, pfnd, pfn1, pfn2, pfn3, pfn4, pfn5;std::cout << "--------------------Base-------------------" << std::endl;Base bObj;// Base data member:// __vfptr指针的地址(void ***):__vfptr(void**)是指针数组(void *[]),成员是指向虚函数的指针(void *)std::cout << "指向Base虚函数表地址:__vfptr地址" << (int*)(&bObj) << std::endl; std::cout << "Base数据成员1(i)地址:" << (int*)(&bObj)+1 << std::endl;std::cout << "Base数据成员2(j)地址:" << (int*)(&bObj)+2 << std::endl;std::cout << "----------对应的值-----------" << std::endl;std::cout << "__vfptr值(vfptr[0]地址):" << (int*)*(int*)(&bObj) << std::endl; // void **std::cout << "i:" << *((int*)(&bObj)+1) << std::endl;std::cout << "j:" << *((int*)(&bObj)+2) << std::endl;std::cout << std::endl;// Base virtual function member:std::cout << "----------Base虚函数表-----------" << std::endl; // void **类型(void*[])std::cout << "__vfptr[0]地址:" << (int*)*(int*)(&bObj) << std::endl; // void **, __vfptr[0](void *)std::cout << "__vfptr[1]地址:" << (int*)*(int*)(&bObj)+1 << std::endl; // 指向函数的指针所在地址std::cout << "__vfptr[2]地址:" << (int*)*(int*)(&bObj)+2 << std::endl;std::cout << "__vfptr[3]地址:" << (int*)*(int*)(&bObj)+3 << std::endl;std::cout << "__vfptr[4]地址:" << (int*)*(int*)(&bObj)+4 << std::endl;std::cout << "----------对应的值-----------" << std::endl; // void *类型 // 编译器自动生成的虚函数:void vector_deleting_destructor(unsigned int),动态处理对象的析构,调用~Base()std::cout << "__vfptr[0]值(析构的代理虚函数地址):" << (int*)*(int*)*(int*)(&bObj) << std::endl;std::cout << "__vfptr[1]值(第一个虚函数地址):" << (int*)*((int*)*(int*)(&bObj)+1) << std::endl; // void *:“指向虚函数的指针”,虚函数入口所在地址std::cout << "__vfptr[2]值(第二个虚函数地址):" << (int*)*((int*)*(int*)(&bObj)+2) << std::endl;std::cout << "__vfptr[3]值(第三个虚函数地址):" << (int*)*((int*)*(int*)(&bObj)+3) << std::endl;std::cout << "__vfptr[4]值(结束标记):" << *((int*)*(int*)(&bObj)+4) << std::endl; // 0std::cout << "------------调用虚函数-------------" << std::endl;pfna = (Destructor)*((int*)*(int*)(&bObj)); // Base::`vector_deleting_destructor(unsigned int)pfnb = (Fun)*((int*)*(int*)(&bObj)+1); // 指针解引用*(void**)-->指向函数入口指针(void*)-->(Fun)强制转换为函数指针pfnc = (Fun)*((int*)*(int*)(&bObj)+2);pfnd = (Fun)*((int*)*(int*)(&bObj)+3);pfnb(); // Base::fpfnc(); // Base::gpfnd(); // Base::hstd::cout << std::endl << std::endl;std::cout << "--------------------Derived-------------------" << std::endl;Derived dObj;Base *pb = &dObj;// Derived data member:std::cout << "指向Derived虚函数表地址:__vfptr地址" << (int*)pb << std::endl; // (int*)pb:(int*)(&dObj)std::cout << "Derived中Base数据成员1(i)地址:" << (int*)pb+1 << std::endl;std::cout << "Derived中Base数据成员2(j)地址:" << (int*)pb+2 << std::endl;std::cout << "Derived数据成员(k)地址:" << (int*)pb+3 << std::endl;std::cout << "----------对应的值-----------" << std::endl;std::cout << "__vfptr值(vfptr[0]地址):" << (int*)*(int*)pb << std::endl;std::cout << "i:" << *((int*)pb+1) << std::endl;std::cout << "j:" << *((int*)pb+2) << std::endl;std::cout << "k:" << *((int*)pb+3) << std::endl;std::cout << std::endl;// Derived virtual function member:std::cout << "----------Derived虚函数表------------" << std::endl;std::cout << "__vfptr[0]地址:" << (int*)*(int*)pb << std::endl;std::cout << "__vfptr[1]地址:" << (int*)*(int*)pb+1 << std::endl;std::cout << "__vfptr[2]地址:" << (int*)*(int*)pb+2 << std::endl;std::cout << "__vfptr[3]地址:" << (int*)*(int*)pb+3 << std::endl;std::cout << "__vfptr[4]地址:" << (int*)*(int*)pb+4 << std::endl;std::cout << "__vfptr[5]地址:" << (int*)*(int*)pb+5 << std::endl;std::cout << "__vfptr[6]地址:" << (int*)*(int*)pb+6 << std::endl;std::cout << "----------对应的值-----------" << std::endl;std::cout << "__vfptr[0]值(析构的代理虚函数地址):" << (int*)*(int*)*(int*)pb << std::endl;std::cout << "__vfptr[1]值(第一个虚函数地址):" << (int*)*((int*)*(int*)pb+1) << std::endl;std::cout << "__vfptr[2]值(第二个虚函数地址):" << (int*)*((int*)*(int*)pb+2) << std::endl;std::cout << "__vfptr[3]值(第三个虚函数地址):" << (int*)*((int*)*(int*)pb+3) << std::endl;std::cout << "__vfptr[4]值(第四个虚函数地址):" << (int*)*((int*)*(int*)pb+4) << std::endl;std::cout << "__vfptr[5]值(第五个虚函数地址):" << (int*)*((int*)*(int*)pb+5) << std::endl;std::cout << "__vfptr[6]值(结束标记):" << *((int*)*(int*)pb+6) << std::endl; // 0std::cout << "------------调用虚函数-------------" << std::endl;pfn0 = (Destructor)*(int*)*(int*)pb; // Derived::`vector deleting destructor'(unsigned int)pfn1 = (Fun)*((int*)*(int*)pb+1);pfn2 = (Fun)*((int*)*(int*)pb+2);pfn3 = (Fun)*((int*)*(int*)pb+3);pfn4 = (Fun)*((int*)*(int*)pb+4);pfn5 = (Fun)*((int*)*(int*)pb+5);((Fun)*((int*)*(int*)pb+1))(); // Derived::f((Fun)*((int*)*(int*)pb+2))(); // Base::g((Fun)*((int*)*(int*)pb+3))(); // Base::h((Fun)*((int*)*(int*)pb+4))(); // Derived::f1((Fun)*((int*)*(int*)pb+5))(); // Derived::g1}它声明在Base类中,是static成员函数,作为一种测试用例,而又不失封装性:
static void test();调用方式很简单:
int main(){Base::test();return 0;}
这个测试代码将(以内存方式)打印基类和派生的数据表的地址、数据表的值、虚表的地址、虚表的值、并且调用虚表指向的虚函数
它将完全的验证1.4、2.2的两张图。
由于Derived类继承了Base类,所以以下语句:
Derived dObj;Base *pb = &dObj;指向派生类的基类指针:pb指向的便是图中dObj对象的地址,由于pb的类型是Base*,通过它的所有调用将被严格限制在“派生类的基类部分”,即图中绿色框中的部分。
这便是通过pb访问的只能是Base所拥有的成员(或继承它的)的原因了。如果pb是Derived*类型,它就可以访问整个Derived。
派生类对象构造:因为每个派生类的开头总有基类部分,所以先构造基类的、再派生类
派生类的虚表:从图中可以看出派生类的虚函数表不止包含自身显式定义的,还包括隐式从基类继承的(含重写的)。按先基类、再派生类(声明顺序)
派生类的析构:由于Base声明了虚析构,派生类的析构默认也是虚的,所以虚表开头是派生类的虚析构代理函数,将由它确保对象析构的正确操作:先派生类的、再基类的
重写与多态:虚表的第二项标示的很清楚,原本应该是Base::f()的那部分被Derived::f()所取代,通过pb访问到的是实际指向的f,所以是Derived::f()。pb自然可以指向Base对象,而Base的虚表中是Base::f()。如果Derived没有重写Base中的虚函数,则Derived虚表中的对应位置是Base虚函数。
私有的虚函数被继承:虚表中含Base::h(),并且通过内存方式可被调用。
私有的数据成员被继承:如j。
调试,虚表:
Base:
Derived:
2.3 继承自1.3中的基类
(1)Derived不含虚析构:
代码同2.2,上图:
基类虚析构函数的重要性:上图基本同2.2,只不过此时没有了析构代理虚函数,实际指向派生类对象的指针或引用析构时将导致错误的行为:pb析构将只调用Base的析构函数,所以k所在的内存没有释放,如果pb是堆上分配的,就是内存泄露问题;
(2)如果Derived显式定义了虚析构:
代码:添加一句:virtual ~Derived() {}
含虚析构的Derived内存图:
特别注意:Derived析构代理函数的位置,是在中间,并非开头
结果仍然是一样的,虽有Derived析构代理函数,但是不能改变Base的析构函数非虚这一事实。甚至实际指向Derived后代类的基类引用或指针对象也只能调用Base构造函数。事实上pb的范围(绿框)不包含Derived析构代理虚函数。通过pb没有办法调用到它。
可能有必要提供测试代码:
void Base::test8(){typedef void (*Destructor)(unsigned int);Destructor pfn3;typedef void(*Fun)(void); // 函数指针Fun pfna, pfnb, pfnc, pfn0, pfn1, pfn2, pfn4, pfn5;std::cout << "--------------------Base-------------------" << std::endl;Base bObj;// Base data member:// __vfptr指针的地址(void ***):__vfptr(void**)是指针数组(void *[]),成员是指向虚函数的指针(void *)std::cout << "指向Base虚函数表地址:__vfptr地址" << (int*)(&bObj) << std::endl; std::cout << "Base数据成员1(i)地址:" << (int*)(&bObj)+1 << std::endl;std::cout << "Base数据成员2(j)地址:" << (int*)(&bObj)+2 << std::endl;std::cout << "----------对应的值-----------" << std::endl;std::cout << "__vfptr值(vfptr[0]地址):" << (int*)*(int*)(&bObj) << std::endl; // void **std::cout << "i:" << *((int*)(&bObj)+1) << std::endl;std::cout << "j:" << *((int*)(&bObj)+2) << std::endl;std::cout << std::endl;// Base virtual function member:std::cout << "----------Base虚函数表-----------" << std::endl; // void **类型(void*[])std::cout << "__vfptr[0]地址:" << (int*)*(int*)(&bObj) << std::endl; // void **, __vfptr[0](void *)std::cout << "__vfptr[1]地址:" << (int*)*(int*)(&bObj)+1 << std::endl; // 指向函数的指针所在地址std::cout << "__vfptr[2]地址:" << (int*)*(int*)(&bObj)+2 << std::endl;std::cout << "__vfptr[3]地址:" << (int*)*(int*)(&bObj)+3 << std::endl;std::cout << "----------对应的值-----------" << std::endl; // void *类型 std::cout << "__vfptr[0]值(第一个虚函数地址):" << (int*)*(int*)*(int*)(&bObj) << std::endl; // void *:“指向虚函数的指针”,虚函数入口所在地址std::cout << "__vfptr[1]值(第二个虚函数地址):" << (int*)*((int*)*(int*)(&bObj)+1) << std::endl;std::cout << "__vfptr[2]值(第三个虚函数地址):" << (int*)*((int*)*(int*)(&bObj)+2) << std::endl;std::cout << "__vfptr[3]值(结束标记):" << *((int*)*(int*)(&bObj)+3) << std::endl;// 0std::cout << "------------调用虚函数-------------" << std::endl;pfna = (Fun)*((int*)*(int*)(&bObj)); // 指针解引用*(void**)-->指向函数入口指针(void*)-->(Fun)强制转换为函数指针pfnb = (Fun)*((int*)*(int*)(&bObj)+1);pfnc = (Fun)*((int*)*(int*)(&bObj)+2);pfna(); // Base::fpfnb(); // Base::gpfnc(); // Base::hstd::cout << std::endl << std::endl;std::cout << "--------------------Derived-------------------" << std::endl;Derived dObj;Base *pb = &dObj;// Derived data member:std::cout << "指向Derived虚函数表地址:__vfptr地址" << (int*)pb << std::endl; // (int*)pb:(int*)(&dObj)std::cout << "Derived中Base数据成员1(i)地址:" << (int*)pb+1 << std::endl;std::cout << "Derived中Base数据成员2(j)地址:" << (int*)pb+2 << std::endl;std::cout << "Derived数据成员(k)地址:" << (int*)pb+3 << std::endl;std::cout << "----------对应的值-----------" << std::endl;std::cout << "__vfptr值(vfptr[0]地址):" << (int*)*(int*)pb << std::endl;std::cout << "i:" << *((int*)pb+1) << std::endl;std::cout << "j:" << *((int*)pb+2) << std::endl;std::cout << "k:" << *((int*)pb+3) << std::endl;std::cout << std::endl;// Derived virtual function member:std::cout << "----------Derived虚函数表------------" << std::endl;std::cout << "__vfptr[0]地址:" << (int*)*(int*)pb << std::endl;std::cout << "__vfptr[1]地址:" << (int*)*(int*)pb+1 << std::endl;std::cout << "__vfptr[2]地址:" << (int*)*(int*)pb+2 << std::endl;std::cout << "__vfptr[3]地址:" << (int*)*(int*)pb+3 << std::endl;std::cout << "__vfptr[4]地址:" << (int*)*(int*)pb+4 << std::endl;std::cout << "__vfptr[5]地址:" << (int*)*(int*)pb+5 << std::endl;std::cout << "__vfptr[6]地址:" << (int*)*(int*)pb+6 << std::endl;std::cout << "----------对应的值-----------" << std::endl;std::cout << "__vfptr[0]值(第一个虚函数地址):" << (int*)*((int*)*(int*)pb) << std::endl;std::cout << "__vfptr[1]值(第二个虚函数地址):" << (int*)*((int*)*(int*)pb+1) << std::endl;std::cout << "__vfptr[2]值(第三个虚函数地址):" << (int*)*((int*)*(int*)pb+2) << std::endl;std::cout << "__vfptr[3]值(析构的代理虚函数地址):" << (int*)*((int*)*(int*)pb+3) << std::endl;std::cout << "__vfptr[4]值(第四个虚函数地址):" << (int*)*((int*)*(int*)pb+4) << std::endl;std::cout << "__vfptr[5]值(第五个虚函数地址):" << (int*)*((int*)*(int*)pb+5) << std::endl;std::cout << "__vfptr[6]值(结束标记):" << *((int*)*(int*)pb+6) << std::endl; // 0std::cout << "------------调用虚函数-------------" << std::endl;pfn0 = (Fun)*((int*)*(int*)pb);pfn1 = (Fun)*((int*)*(int*)pb+1);pfn2 = (Fun)*((int*)*(int*)pb+2);pfn3 = (Destructor)*((int*)*(int*)pb+3); // 注意,代理函数在这里pfn4 = (Fun)*((int*)*(int*)pb+4);pfn5 = (Fun)*((int*)*(int*)pb+5);((Fun)*((int*)*(int*)pb))(); // Derived::f((Fun)*((int*)*(int*)pb+1))(); // Base::g((Fun)*((int*)*(int*)pb+2))(); // Base::h((Fun)*((int*)*(int*)pb+4))(); // Derived::f1((Fun)*((int*)*(int*)pb+5))(); // Derived::g1}调试,虚表:
Derived:
2.4 继承自1.1的派生类
class Derived: public Base{public:Derived():k(5){} virtual ~Derived(){} virtual void f1() { std::cout << "Derived::f1" << std::endl; }private:virtual void g1() { std::cout << "Derived::g1" << std::endl; }int k;};
无需基类虚析构函数的情况:一个类不作为基类;或者,如果一个类是基类,又不用于多态,就是你保证不会定义一个指向派生类的基类指针或者引用对象,毕竟,一个派生类的(非指针、引用)对象总能够被正确的析构。
这种情况恰好是无需基类析构函数的一种,基类无虚函数,由此可见1.2的设计虚析构有点冗余
上图:
注意此时的pb与&dObj位置不同,这是因为C++标准规定指向虚表位置必须是类的开头,而此时Base没有虚表(无虚函数)。
pb无所谓多态了,pb虽然实际指向Derived对象dObj,显然不能访问Derived虚函数,显然的,析构pb又将导致遗落k,既然基类没有虚析构,就不该定义指向派生类的基类指针或引用。
有必要测试一下:
void Base::test3(){typedef void (*Destructor)(unsigned int);Destructor pfn0;typedef void(*Fun)(void);Fun pfn1, pfn2;std::cout << "--------------------Base-------------------" << std::endl;Base bObj;// Base data member:std::cout << "Base数据成员1(i)地址:" << (int*)(&bObj) << std::endl;std::cout << "Base数据成员2(j)地址:" << (int*)(&bObj)+1 << std::endl;std::cout << "----------对应的值-----------" << std::endl;std::cout << "i:" << *((int*)(&bObj)) << std::endl;std::cout << "j:" << *((int*)(&bObj)+1) << std::endl;std::cout << std::endl << std::endl;std::cout << "--------------------Derived-------------------" << std::endl;Derived dObj;Base *pb = &dObj; // 注意!指针发生了偏移:(int*)pb-1 == &dObj// Derived data member:std::cout << "指向Derived虚函数表地址:__vfptr地址" << (int*)pb-1 << std::endl;std::cout << "Derived中Base数据成员1(i)地址:" << (int*)pb << std::endl;std::cout << "Derived中Base数据成员2(j)地址:" << (int*)pb+1 << std::endl;std::cout << "Derived数据成员(k)地址:" << (int*)pb+2 << std::endl;std::cout << "----------对应的值-----------" << std::endl;std::cout << "__vfptr值(vfptr[0]地址):" << (int*)*((int*)pb-1) << std::endl; // 注意!指针发生了偏移std::cout << "i:" << *((int*)pb) << std::endl;std::cout << "j:" << *((int*)pb+1) << std::endl;std::cout << "k:" << *((int*)pb+2) << std::endl;std::cout << std::endl;// Derived virtual function member:std::cout << "----------Derived虚函数表------------" << std::endl;std::cout << "__vfptr[0]地址:" << (int*)*((int*)pb-1) << std::endl;std::cout << "__vfptr[1]地址:" << (int*)*((int*)pb-1)+1 << std::endl;std::cout << "__vfptr[2]地址:" << (int*)*((int*)pb-1)+2 << std::endl;std::cout << "__vfptr[3]地址:" << (int*)*((int*)pb-1)+3 << std::endl;std::cout << "----------对应的值-----------" << std::endl;std::cout << "__vfptr[0]值(析构的代理虚函数地址):" << (int*)*((int*)*((int*)pb-1)) << std::endl;std::cout << "__vfptr[1]值(第一个虚函数地址):" << (int*)*((int*)*((int*)pb-1)+1) << std::endl;std::cout << "__vfptr[2]值(第二个虚函数地址):" << (int*)*((int*)*((int*)pb-1)+2) << std::endl;std::cout << "__vfptr[3]值(结束标记):" << *((int*)*((int*)pb-1)+3) << std::endl;std::cout << "------------调用虚函数-------------" << std::endl;pfn0 = (Destructor)*((int*)*((int*)pb-1));pfn1 = (Fun)*((int*)*((int*)pb-1)+1);pfn2 = (Fun)*((int*)*((int*)pb-1)+2);((Fun)*((int*)*((int*)pb-1)+1))();// Derived::f1((Fun)*((int*)*((int*)pb-1)+2))();// Derived::g1}调试,虚表:
Derived:
pb与&dObj:(int*)pb - 1 == &dObj;
2.5 继承自1.2的派生类
代码同2.4
直接上图:
此时,pb==&dObj,但pb虚表范围只有一个析构代理函数,定义pb的意图显然不够明显。
至此,本文就要结束了,若有任何错误,欢迎指正。
最后,给一些好的文章链接,供参考:这些条目涵盖了本文未涉及的多重派生的情况
http://www.cnblogs.com/Binhua-Liu/archive/2010/06/16/1759019.html
http://www.uml.org.cn/c%2B%2B/200811143.asp
http://blog.csdn.net/w18767104183/article/details/7006304
- c++类的内存结构(单继承)
- 【C++】c++单继承、多继承、菱形继承内存布局(虚函数表结构)
- 【C++】c++单继承、多继承、菱形继承内存布局(虚函数表结构)
- c++单继承、多继承、菱形继承的内存布局(虚函数表结构)
- Java的单继承结构
- Java的单根继承结构--优点
- c++单继承、多继承、菱形继承内存布局(虚函数表结构)
- C++对象的内存布局---单继承
- 虚继承之单继承的内存布局
- 虚继承之单继承的内存布局
- 虚继承之单继承的内存布局
- (转载)虚继承之单继承的内存布局
- 虚继承之单继承的内存布局
- C++虚继承的内存结构
- 多重继承的内存结构分析
- c++对象多继承的内存结构
- C++ 类 内存分布 虚函数 单继承 多继承
- C程序的内存结构
- TTL/CMOS/RS232/RS485电平介绍
- HDU--IMNU集训三: Palindromes _easy version STL中reverse()用法以及string CString string.h cstring区别
- OpenSSH移植到ARM Linux教程
- 读head first servlet and jsp有感(一)
- 教你在mac上配置adb环境变量!!附上adb无法识别M2的解决办法
- c++类的内存结构(单继承)
- URAL 1167. Bicolored Horses dp
- Linux内核的Oops
- 解决Eclipse鼠标双击自动打开2345网页问题
- java编程中常见的错误信息翻译
- Build Setting Reference
- 浅谈朴素贝叶斯与文本分类(一)
- F2FS – A New Flash File System for Mobile Devices – ELCE 2012
- 嵌入式Linux工程师自学笔记