c++的对象模型,单继承和多继承的对象模型探索
程序员文章站
2022-03-10 17:33:19
虚函数表就是通过一块连续内存来保存虚函数的地址
单继承虚函数的对象模型
class a{public:virtual void func1(){printf("a::func1\n&quo...
虚函数表就是通过一块连续内存来保存虚函数的地址
单继承虚函数的对象模型
class a{public:virtual void func1(){printf("a::func1\n");}virtual void func2(){printf("a::func2\n");}public:int _a;};class b{public:virtual void func1(){printf("b::func1\n");}virtual void func3(){printf("b::func3\n");}virtual void func4(){printf("b::func4\n");}public:int _b;};typedef void(*func) ();void printvtable(int *vtable){size_t i = 0;for (; vtable[i] != null; i++){printf("第%d个虚函数:0x%p---->", i, vtable[i]);//将取出来的地址强转为函数指针调用函数func f = (func)vtable[i];f();printf("\n");}}int main(){a a;b b;//打印两个类的虚表printvtable((int *)*((int *)(&a)));printvtable((int *)*((int *)(&b)));system("pause");return 0;}
因为虚表里存的是一个一个的函数指针,而虚标的地址又存在类的最上面的四个字节,所以呀只要我们将最上面的四个字节里的地址取出来然后去根据这个地址来找到虚表,然后我门可以将虚表看做一个数组,数组里的每一个元素都是地址(函数指针),那么我们将这个数组打印即可
那么这个打印虚表的难点为这句printvtable((int *)*((int *)(&a)))此句就时将类的地址取出来然后强转为(int*),只将头上4个字节取出,在解引用,但是解引用了之后的值是一个int型的数据,那我们再将它强转为(int *)然后根据地址找虚表即可
那么我们可以发现此代码是在32位下运行的,那么我们怎么写一份代码既可以在32位下运行也可以在64位运行呢printvtable((int **)*((int **)(&a)))int**解引用了之后,在多少位下指针就是多少位
我们来一下内存和打印的对应我们可以看到在原来a类的func1函数的地方b进行了虚函数的重写
我门并没有看到func3和func4 在虚表中这个编译器的一个bug,但其实它是真实存在的,通过打印我们也能看出来
我们再来手动的梳理一下来画张图
多继承虚函数的对象模型
class a{public:virtual void func1(){printf("a::func1\n");}virtual void func2(){printf("a::func2\n");}public:int _a;};class b{public:virtual void func1(){printf("b::func1\n");}virtual void func2(){printf("b::func2\n");}public:int _b;};class c :public a,public b{virtual void func1(){printf("c::func1\n");}virtual void func3(){printf("c::func3\n");}public:int _c;};typedef void(*func) ();void printvtable(int *vtable){size_t i = 0;printf("虚表地址:%p\n", vtable);for (; vtable[i] != null; i++){printf("第%d个虚函数:0x%p---->", i, vtable[i]);//将取出来的地址强转为函数指针调用函数func f = (func)vtable[i];f();}printf("\n\n");}int main(){c c;//打印两个类的虚表printvtable((int *)*((int *)(&c)));printvtable((int *)*((int *)(&c) + sizeof(a) / 4));system("pause");return 0;}然后我们来看一下多继承的模型
以下几点需注意当子类里有对两个父类都重写的虚函数时,都会进行覆盖重写子类会继承父类的虚表子类的没有重写的函数放在(先继承哪个父类)就放在那个父类的虚表中,(以上代码子类先继承了a,所以子类里其他的函数都放在a的虚表里)
单继承虚函数的对象模型
class a{public:virtual void func1(){printf("a::func1\n");}virtual void func2(){printf("a::func2\n");}public:int _a;};class b{public:virtual void func1(){printf("b::func1\n");}virtual void func3(){printf("b::func3\n");}virtual void func4(){printf("b::func4\n");}public:int _b;};typedef void(*func) ();void printvtable(int *vtable){size_t i = 0;for (; vtable[i] != null; i++){printf("第%d个虚函数:0x%p---->", i, vtable[i]);//将取出来的地址强转为函数指针调用函数func f = (func)vtable[i];f();printf("\n");}}int main(){a a;b b;//打印两个类的虚表printvtable((int *)*((int *)(&a)));printvtable((int *)*((int *)(&b)));system("pause");return 0;}
因为虚表里存的是一个一个的函数指针,而虚标的地址又存在类的最上面的四个字节,所以呀只要我们将最上面的四个字节里的地址取出来然后去根据这个地址来找到虚表,然后我门可以将虚表看做一个数组,数组里的每一个元素都是地址(函数指针),那么我们将这个数组打印即可
那么这个打印虚表的难点为这句printvtable((int *)*((int *)(&a)))此句就时将类的地址取出来然后强转为(int*),只将头上4个字节取出,在解引用,但是解引用了之后的值是一个int型的数据,那我们再将它强转为(int *)然后根据地址找虚表即可
那么我们可以发现此代码是在32位下运行的,那么我们怎么写一份代码既可以在32位下运行也可以在64位运行呢printvtable((int **)*((int **)(&a)))int**解引用了之后,在多少位下指针就是多少位
我们来一下内存和打印的对应我们可以看到在原来a类的func1函数的地方b进行了虚函数的重写
我门并没有看到func3和func4 在虚表中这个编译器的一个bug,但其实它是真实存在的,通过打印我们也能看出来
我们再来手动的梳理一下来画张图
多继承虚函数的对象模型
class a{public:virtual void func1(){printf("a::func1\n");}virtual void func2(){printf("a::func2\n");}public:int _a;};class b{public:virtual void func1(){printf("b::func1\n");}virtual void func2(){printf("b::func2\n");}public:int _b;};class c :public a,public b{virtual void func1(){printf("c::func1\n");}virtual void func3(){printf("c::func3\n");}public:int _c;};typedef void(*func) ();void printvtable(int *vtable){size_t i = 0;printf("虚表地址:%p\n", vtable);for (; vtable[i] != null; i++){printf("第%d个虚函数:0x%p---->", i, vtable[i]);//将取出来的地址强转为函数指针调用函数func f = (func)vtable[i];f();}printf("\n\n");}int main(){c c;//打印两个类的虚表printvtable((int *)*((int *)(&c)));printvtable((int *)*((int *)(&c) + sizeof(a) / 4));system("pause");return 0;}然后我们来看一下多继承的模型
以下几点需注意当子类里有对两个父类都重写的虚函数时,都会进行覆盖重写子类会继承父类的虚表子类的没有重写的函数放在(先继承哪个父类)就放在那个父类的虚表中,(以上代码子类先继承了a,所以子类里其他的函数都放在a的虚表里)
上一篇: redis缓存应用分析
下一篇: Java无序数组排序后实现二分查找
推荐阅读
-
C++面向对象编程之对象模型(objectmodel)关于vptr和vtbl的讲解
-
c++的对象模型,单继承和多继承的对象模型探索
-
C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容...
-
继承与继承体系中派生类的对象模型
-
理解C++存在继承和组合的对象构造函数调用顺序(转载)
-
C++面向对象编程之对象模型(objectmodel)关于vptr和vtbl的讲解
-
多态以及它的单继承、多继承、菱形继承的对象模型
-
用JavaScript实现单继承和多继承的简单方法_js面向对象
-
c++的对象模型,单继承和多继承的对象模型探索
-
用JavaScript实现单继承和多继承的简单方法_js面向对象