C++之探索多态的本质(虚函数与虚表)2
来源:互联网 发布:java调用db2存储过程 编辑:程序博客网 时间:2024/06/06 02:26
我们都知道对于类中存在虚函数的时候,当实例化一个对象的时候,就会为这个虚函数创建一个虚表,就是用来存储这个类中的虚函数地址
一.对于不进行继承的类中的虚函数与虚表
/********************************************************************** * * Copyright (c)2015,WK Studios * * Filename: A.h * * Compiler: GCC vc 6.0 * * Author:WK * * Time: 2015 29 6 * **********************************************************************/ #include <iostream>using namespace std;class A{public: A(int x=0):m_data(x){ cout<<"------Create A::-----\n";}virtual void show(){cout<<"---show::Data: is: "<<m_data<<endl;}virtual void print(){ cout<<"---print::Data: is: "<<m_data<<endl;}virtual ~A(){cout<<"------Free A::-----\n";}private:int m_data;};class B{public:B(int i=0):data(i){ cout<<"------Create B::-----\n";}virtual void show1(){cout<<"---show1::Data: is "<<data<<endl;}virtual void print1(){cout<<"---print1::Data: is: "<<data<<endl;}virtual ~B(){ cout<<"------Free B::-----\n";}private:int data;};typedef void (*P)();void main(){A a;cout<<"\n";cout<<"-----使用对象调用虚函数----\n";a.print();a.show();cout<<endl;cout<<hex<<*(int *)(&a)<<endl; cout<<*(int*)(*(int*)(&a))<<endl;cout<<*(int*)((*(int*)(&a)+0))<<endl;cout<<"\n"; cout<<"-----使用地址调用虚函数----\n"; int *q=(int*)(*(int *)(&a)); cout<<*q<<" "<<*(q+1)<<endl;((void (*)())(*q))(); ((void (*)())(*(q+1)))();((void (*)())(q[0]))(); cout<<"\n";P p,n; p=(P)(*(((int*)(*(int*)(&a)))+0)); p();p=(P)(*(((int*)(*(int*)(&a)))+1)); p(); cout<<"\n";B b;cout<<"\n"; b.print1();b.show1(); cout<<"\n";}
所以现在当使用多态时候将 base*p; p= &Derived 实现多态的时候是把子类的对象的首部地址赋给这个指针,这个指针只能读取前4个字节所以就是虚表的首地址,之后根据函数名在虚表中调用虚函数,因为继承之后基类的与子类的三同函数,而且是虚函数被子类的函数覆盖,所以此时调用的只能是子类的函数,因此实现了多态!
三同是返回值 参数 函数名 全相同,想区分覆盖和隐藏请看这篇文章
二.对于单继承类的虚函数和虚表
/********************************************************************** * * Copyright (c)2015,WK Studios * * Filename: A.h * * Compiler: GCC vc 6.0 * * Author:WK * * Time: 2015 29 6 * **********************************************************************/ #include <iostream>using namespace std;class A{public: A(int x=0):m_data(x){ cout<<"------Create A::-----\n";}virtual void show(){cout<<"---A::--show::Data: is: "<<m_data<<endl;}virtual void print(){ cout<<"---A::--print::Data: is: "<<m_data<<endl;}virtual ~A(){cout<<"------Free A::-----\n";}private:int m_data;};class B:public A{public:B(int i=0,int j=0):data(i),A(j){ cout<<"------Create B::-----\n";} void show1(){cout<<"---B::--show1::Data: is "<<data<<endl;}void print(){cout<<"---B::--print::Data: is: "<<data<<endl;}virtual ~B(){ cout<<"------Free B::-----\n";}private:int data;};void main(){ B a;}
这种覆盖只会出现在子类继承了父类的并且存在三同函数而且三同函数必须在基类中是虚函数,此时子类中继承父类的三同函数会在子类的虚表里被子类的函数覆盖,注意不是大家同用一个虚表对于任意一个实例化的类对象只要类中存在虚函数就会出现一个虚表,只是我们如果不再继承上使用覆盖来使用虚表就会使虚表失去原本用来实现动态连编的初衷,也就是多态的实现
可以自己测一下在基类被继承后基类的那个虚函数还在呢
上边的mian函数中加上一句 A b ;
其实有时候也有一些东西隐藏着
#include<iostream>#include<assert.h>using namespace std;/*********************************************************************** * Copyright (c)2015,WK Studios* * Filename: A.h* * Compiler: GCC vc 6.0* * Author:WK* * Time: 2015 29 6* **********************************************************************/#include <iostream>using namespace std;class A{public:A(int x = 0) :m_data(x){cout << "------Create A::-----\n";}virtual void print(){cout << "---A::--print::Data: is: " << m_data << endl;}virtual ~A(){cout << "------Free A::-----\n";}private:int m_data;};class B :public A{public:B(int i = 0, int j = 0) :data(i), A(j){cout << "------Create B::-----\n";}virtual void show1(){cout << "---B::--show::Data: is " << data << endl;}void print(){cout << "---B::--print::Data: is: " << data << endl;}virtual ~B(){cout << "------Free B::-----\n";}private:int data;};void main(){B b;}
可以从类B中看出还有一个虚函数show1()但是查看子类的变量时候并没有它,但是一看内存其实他是存在的
0x000000000作为虚表的结束。表示表中有三个函数,但是右边只显示了两个,所欲有时候编译器是有隐藏的,VC6.0 VS 2013都是这样的,但是如果B类不继承可以在参数中看到那个虚函数show1(),读者可以自己试一下啊
三.对于多继承中虚函数和虚表
/********************************************************************** * * Copyright (c)2015,WK Studios * * Filename: A.h * * Compiler: GCC vc 6.0 * * Author:WK * * Time: 2015 29 6 * **********************************************************************/ #include <iostream>using namespace std;class A{public:virtual void show(){cout<<"---A::--show: "<<endl;}virtual void print(){ cout<<"---A::--print: "<<endl;}virtual ~A(){cout<<"------Free A::-----\n";}};class B{public:virtual void show(){cout<<"---B::--show:: "<<endl;}virtual void print(){cout<<"---B::--print:"<<endl;}virtual ~B(){ cout<<"------Free B::-----\n";}};class C {public: virtual void show(){ cout<<"---C::--show1"<<endl;}virtual void print(){cout<<"---C::--print::"<<endl;}virtual~C(){ cout<<"------Free C::-----\n";}};class D:public A, public B,public C{public:virtual void show(){cout<<"---D::--show1"<<endl;}virtual void print(){cout<<"---D::--print::"<<endl;}virtual void DD(){cout<<"---D::--DD::"<<endl; }virtual~D(){ cout<<"------Free C::-----\n";}};void main(){ D d;}
可以看出派生类的其余不是覆盖的虚函数放在了在继承时候声明顺序中第一个的基类中其余的基类中只放了覆盖的函数
具体如下图
下来实现一下多态:
/********************************************************************** * * Copyright (c)2015,WK Studios * * Filename: A.h * * Compiler: GCC vc 6.0 * * Author:WK * * Time: 2015 29 6 * **********************************************************************/ #include <iostream>using namespace std;class A{public:virtual void show(){cout<<"---A::--show: "<<endl;}virtual void print(){ cout<<"---A::--print: "<<endl;}virtual ~A(){cout<<"------Free A::-----\n";}};class B{public:virtual void show(){cout<<"---B::--show:: "<<endl;}virtual void print(){cout<<"---B::--print:"<<endl;}virtual ~B(){ cout<<"------Free B::-----\n";}};class C {public: virtual void show(){ cout<<"---C::--show1"<<endl;}virtual void print(){cout<<"---C::--print::"<<endl;}virtual~C(){ cout<<"------Free C::-----\n";}};class D:public A, public B,public C{public:virtual void show(){cout<<"---D::--show1"<<endl;}virtual void print(){cout<<"---D::--print::"<<endl;}virtual void DD(){cout<<"---D::--DD::"<<endl; }virtual~D(){ cout<<"------Free C::-----\n";}};void main(){ D d; cout<<"----实现多态-----\n"; A *p; p=&d; p->show(); B *q; q=&d; q->show(); C *f; f=&d; f->show(); cout<<endl; cout<<"---D类中实例对象的内存首部依次存储基类虚指针----\n"; cout<<"A&D虚表地址 "<<hex<<*(int*)(&d)<<endl;//在子类对象内存首部依次是基类A的虚表指针 cout<<"B&D虚表地址 "<<hex<<*(((int*)(&d))+1)<<endl;//基类B的虚表指针 cout<<"B&D虚表地址 "<<hex<<*(((int*)(&d))+2)<<endl;//基类C的虚表指针 cout<<endl; cout<<"---D类中D类与A类形成的覆盖后的虚表-----\n"; cout<<"A&D虚表第一个函数地址 "<<*((int*)(*(int*)(&d))+0)<<endl;//基类A与D类在D的作用域内发生覆盖形成的虚表的第一个虚函数 cout<<"A&D虚表第二个函数地址 "<<*(((int*)(*(int*)(&d)))+1)<<endl;//第二个虚函数 cout<<"A&D虚表第三个函数地址 "<<*(((int*)(*(int*)(&d)))+2)<<endl;//第三个虚函数 cout<<"A&D虚表第四个函数地址 "<<*(((int*)(*(int*)(&d)))+3)<<endl;//第四个虚函数,对于本程序这个是D类自己的虚函数,就是没有和A类发生覆盖的虚函数 cout<<"A&D虚表结束标志 "<<*(((int*)(*(int*)(&d)))+4)<<endl;//虚表的结束标志 cout<<"越界访问 "<<*(((int*)(*(int*)(&d)))+5)<<endl;//当然也可以越过虚表查看地址,但是已经无意义了 cout<<endl; cout<<"---D类中D类与B类形成的覆盖后的虚表-----\n"; cout<<"B&D虚表第一个函数地址 "<<*(((int*)(*(((int*)(&d))+1)))+0)<<endl; cout<<"B&D虚表第二个函数地址 "<<*(((int*)(*(((int*)(&d))+1)))+1)<<endl; cout<<"B&D虚表第三个函数地址 "<<*(((int*)(*(((int*)(&d))+1)))+2)<<endl; cout<<"B&D虚表结束标志 "<<*(((int*)(*(((int*)(&d))+1)))+3)<<endl; cout<<endl; //C类与D类的虚表这里就不再多写,与上边的雷同 //可以将这些函数的地址赋给一个函数指针进行函数的调用,前边已经实现这里不再写出}运行结果:
- C++之探索多态的本质(虚函数与虚表)2
- 探索虚函数与多态
- C语言的本质(31)——C语言与汇编之函数调用的本质
- 虚函数的本质
- 虚函数的本质
- C 的本质 函数接口
- 探索C++对象模型之 多重继承与虚函数表
- 探索虚函数表的位置
- 【c++程序】虚函数的本质
- 函数调用的本质与函数指针
- C++对象模型之继承与多态的探索
- 探索ETL的本质(1)
- 探索动态规划的本质
- 虚继承与虚基类的本质
- 虚继承与虚基类的本质
- 虚继承与虚基类的本质
- 虚继承与虚基类的本质
- 虚继承与虚基类的本质
- R语言文本挖掘之jieba分词与wordcloud展现
- poj 2965 The Pilots Brothers' refrigerator 高斯消元法 暑假第二题
- 我所见过的最简短、最灵活的javascript日期转字符串工具函数
- iOS学习笔记(10下)六种手势
- 机器视觉、模式识别库集合
- C++之探索多态的本质(虚函数与虚表)2
- 从ACM中删除一个已经创建的Library
- oracle数据库优化
- 4、mkdir命令
- 菜鸟程序员的在CSDN中的开篇
- Hadoop入门系列(2) -- Hadoop运行环境搭建
- tomcat项目发布
- Linux打tar包命令
- ReactJS学习笔记七:表单