深入理解虚表之非虚拟继承及虚拟继承

非虚拟继承

【带虚函数的类】

class Base

{

public:

virtual void FunTest1()

{

cout<<"Base::FunTest1()"<<endl;

}

virtual void FunTest2()

{

cout<<"Base::FunTest2()"<<endl;

}

int _data1;

};

int main()

{

Base b;

b._data1 = 0x01;

return 0;

}

Base类没有显式定义自己的构造函数,此时编译器会和成默认的构造函数,

合成的构造函数中主要完成在对象头4个字节中填写虚表地址:

Base类对象最后的模型如下:

注意:同一个类的对象共用同一个虚表

Base b1, b2, b3;

从上述的结果中可以得到印证。

【单继承(派生类中没有虚函数覆盖)

class Base

{

public:

virtual void FunTest1()

{cout<<"Base::FunTest1()"<<endl;}

virtual void FunTest2()

{cout<<"Base::FunTest2()"<<endl;}

int _data1;

};

class Derive:public Base

{

public:

virtual void FunTest3()

{cout<<"Derive::FunTest3()"<<endl;}

virtual void FunTest4()

{cout<<"Derive::FunTest4()"<<endl;}

int _data2;

};

//打印虚表

typedef void (*VtbFun)();

void PrintVtable()

{

cout<<"Derive类的虚函数表:"<<endl;

Derive d1;

d1._data1 = 0x01;

d1._data2 = 0x02;

int* pVTable = (int*)*(int*)&d1;

VtbFun FunTest = (VtbFun)*pVTable;

while(NULL != FunTest)

{

FunTest();

cout<<(int*)FunTest<<endl;

pVTable += 1;

FunTest = (VtbFun)*pVTable;

}

cout<<"虚表结束:"<<endl;

}

int main()

{

Base b1;

Derive d1;

return 0;

}

按照如上分析的顺序,探索下单继承下派生类对象模型以及虚表

首先看看编译器为派生类合成的缺省构造函数:

派生类构造函数中进行了如下事情:

Derive d1;

d1._data1 = 0x01;

d1._data2 = 0x02;

派生类最后的对象模型为:

【单继承(派生类中有虚函数覆盖)

class Base

{

public:

virtual void FunTest1()

{

cout<<"Base::FunTest1()"<<endl;

}

virtual void FunTest2()

{

cout<<"Base::FunTest2()"<<endl;

}

int _data1;

};

class Derive:public Base

{

public:

virtual void FunTest1()

{

cout<<"Derive::FunTest1()"<<endl;

}

virtual void FunTest3()

{

cout<<"Derive::FunTest3()"<<endl;

}

virtual void FunTest4()

{

cout<<"Derive::FunTest4()"<<endl;

}

int _data2;

};

int main()

{

PrintVtable();

return 0;

}

派生类对象模型及虚表建议规则:

【多继承(派生类不覆盖基类虚函数)

class Base

{

public:

virtual void FunTest1()

{

cout<<"Base::FunTest1()"<<endl;

}

virtual void FunTest2()

{

cout<<"Base::FunTest2()"<<endl;

}

int _data1;

};

class Base1

{

public:

virtual void FunTest3()

{

cout<<"Base1::FunTest3()"<<endl;

}

virtual void FunTest4()

{

cout<<"Base1::FunTest4()"<<endl;

}

int _data2;

};

class Derive:public Base, public Base1

{

public:

virtual void FunTest5()

{

cout<<"Derive::FunTest5()"<<endl;

}

int _data3;

};

int main()

{

cout<<"sizeof(Derive) = "<<sizeof(Derive)<<endl;

Derive d;

d._data1 = 0x01;

d._data2 = 0x02;

d._data3 = 0x03;

PrintVtable();

return 0;

}

同样:看看编译器合成的派生类的对象做了什么工作

观察下派生类的对象模型和虚表的建立过程

从上面的结果可以看出,Derive类自己特有的虚函数直接添加在Base类对应虚函数表最后的位置,大家可将BaseBase1的顺序交换验证下。

【多继承(派生类覆盖基类虚函数)

class Base

{

public:

virtual void FunTest1()

{

cout<<"Base::FunTest1()"<<endl;

}

virtual void FunTest2()

{

cout<<"Base::FunTest2()"<<endl;

}

int _data1;

};

class Base1

{

public:

virtual void FunTest3()

{

cout<<"Base1::FunTest3()"<<endl;

}

virtual void FunTest4()

{

cout<<"Base1::FunTest4()"<<endl;

}

int _data2;

};

//这次将继承列表中Base和Base1的位置互换

class Derive:public Base1, public Base

{

public:

virtual void FunTest1()

{

cout<<"Derive::FunTest1()"<<endl;

}

virtual void FunTest3()

{

cout<<"Derive::FunTest3()"<<endl;

}

virtual void FunTest5()

{

cout<<"Derive::FunTest5()"<<endl;

}

int _data3;

};

int main()

{

PrintVtable();

return 0;

}

此时派生类的对象模型和虚表的结构:

虚拟继承

//没有虚函数覆盖,但派生类有自己的虚函数

class Base

{

public:

virtual void FunTest1()

{

cout<<"Base::FunTest1()"<<endl;

}

virtual void FunTest2()

{

cout<<"Base::FunTest2()"<<endl;

}

int _data1;

};

class Derive:virtual public Base

{

public:

virtual void FunTest3()

{

cout<<"Derive::FunTest3()"<<endl;

}

virtual void FunTest4()

{

cout<<"Derive::FunTest4()"<<endl;

}

int _data2;

};

虚拟继承编译器为派生类合成的默认构造函数分析

编译器为派生类合成的默认构造函数任务分析:

虚拟继承派生类对象模型分析

说明:使用环境VS2010,不同版本编译器可能会有差异

     说明:使用环境VS2010,不同版本编译器可能会有差异

时间: 2024-10-07 19:39:26

深入理解虚表之非虚拟继承及虚拟继承的相关文章

More Effective C++----(24)理解虚拟函数、多继承、虚继承和RTTI所需的代价

Item M24:理解虚拟函数.多继承.虚继承和RTTI所需的代价 C++编译器们必须实现语言的每一个特性.这些实现的细节当然是由编译器来决定的,并且不同的编译器有不同的方法实现语言的特性.在多数情况下,你不用关心这些事情.然而有些特性的实现对对象大小和其成员函数执行速度有很大的影响,所以对于这些特性有一个基本的了解,知道编译器可能在背后做了些什么,就显得很重要.这种特性中最重要的例子是虚拟函数. 当调用一个虚拟函数时,被执行的代码必须与调用函数的对象的动态类型相一致:指向对象的指针或引用的类型

C++ 继承、多继承、虚拟继承对象模型

C++面向对象语言一大难点是继承,但又是不得不掌握的.简单的继承是很容易理解的,但是当涉及到多继承,设计到虚函数的继承,特别是涉及到虚继承时,问题就会变得复杂.下面的内容来自参考资料中的三篇文章.C++的继承学习中,最主要是要掌握派生类的对象模型,基类和派生类指针之间的向上向下类型转换,当继承中的出现虚函数成员函数的访问(多态),虚继承是如何通过引入虚基表解决"菱形继承"中存在多份公共基类的问题. 一.简单的对象模型 1.定义 class MyClass { public: int v

C++ 深入理解 继承和直接继承

[摘要] 本文从5段代码实例出发,通过类中类的普通继承,类的虚继承,类的多重继承,多个虚函数类的普通继承.虚继承与多重继承,几个交叉概念,详细的阐释了继承.虚函数与虚继承的基本概念,深入剖析了继承于虚继承的区别于联系. [Exp.001-虚继承] #include <stdio.h> class A { public: int a; };//sizeof(A)=4 class B : virtual public A { public: int b; };//sizeof(B)=4(A副本)+

【整理】C++虚函数及其继承、虚继承类大小

参考文章: http://blog.chinaunix.net/uid-25132162-id-1564955.html http://blog.csdn.net/haoel/article/details/1948051/ 一.虚函数与继承 1.空类,空类单继承,空类多继承的sizeof #include <iostream> using namespace std; class Base1 { }; class Base2 { }; class Derived1:public Base1

C++ 多继承和虚继承的内存布局(Memory Layout for Multiple and Virtual Inheritance)

警告. 本文有点技术难度,需要读者了解C++和一些汇编语言知识. 在本文中,我们解释由gcc编译器实现多继承和虚继承的对象的布局.虽然在理想的C++程序中不需要知道这些编译器内部细节,但不幸的是多重继承(特别是虚拟继承)的实现方式有各种各样的不太明确的结论(尤其是,关于向下转型指针,使用指向指针的指针,还有虚拟基类的构造方法的调用命令). 如果你了解多重继承是如何实现的,你就能预见到这些结论并运用到你的代码中.而且,如果你关心性能,理解虚拟继承的开销也是非常有用的.最后,这很有趣. :-) 多重

转载:C++ 多继承和虚继承的内存布局

C++ 多继承和虚继承的内存布局[已翻译100%] 英文原文:Memory Layout for Multiple and Virtual Inheritance 标签: <无> run_mei 推荐于 4年前 (共 14 段, 翻译完成于 10-17) 评论 46 分享 收藏 198 参与翻译 (5人) : super0555, polarisxxm, Ley, zaobao, 开源中国吹牛第一仅中文 | 中英文对照 | 仅英文 | 打印此文章 警告. 本文有点技术难度,需要读者了解C++和

C++ 多继承和虚继承的内存布局(转)

转自:http://www.oschina.net/translate/cpp-virtual-inheritance 警告. 本文有点技术难度,需要读者了解C++和一些汇编语言知识. 在本文中,我们解释由gcc编译器实现多继承和虚继承的对象的布局.虽然在理想的C++程序中不需要知道这些编译器内部细节,但不幸的是多重继承(特别是虚拟继承)的实现方式有各种各样的不太明确的结论(尤其是,关于向下转型指针,使用指向指针的指针,还有虚拟基类的构造方法的调用命令). 如果你了解多重继承是如何实现的,你就能

《Effective C++》:条款34:区分接口继承和实现继承

public继承的概念,由2部分构成:函数接口(function Interface)继承和函数实现(function implementation)继承.这两种继承的差异有点像函数的声明和函数的定义之间的差异. 我们在设计class时,有时希望derived class只继承函数的接口(即函数声明):有时候希望derived class继承函数接口和实现,但又覆写它们所继承的实现:又有时候希望derived class同时继承函数的接口和实现,但不覆写任何东西. 为了更好理解上述差异,用一个绘

虚继承之单继承的内存布局(VC在编译时会把vfptr放到类的头部,这和Delphi完全一致)

C++2.0以后全面支持虚函数与虚继承,这两个特性的引入为C++增强了不少功能,也引入了不少烦恼.虚函数与虚继承有哪些特性,今天就不记录了,如果能搞了解一下编译器是如何实现虚函数和虚继承,它们在类的内存空间中又是如何布局的,却可以对C++的了解深入不少.这段时间花了一些时间了解这些玩意,搞得偶都,不过总算有些收获,嘿嘿. 先看一段代码class A{      virtual aa(){};}; class B : public virtual  A{      char j[3];