I.定義仮想関数
キーワード仮想メンバ関数を変更し、目的の多型を達成することです
PS:
多型について実装から{別のインタフェース、親ポインタを可能にする親クラスのポインタのサブクラスを、呼び出してサブクラスとメンバ関数の親クラスのインスタンスへのポインタは、種々の形態、いわゆる多型を有します]
II。のVtable
アドレステーブルには、問題の範囲を解決するためのクラスの仮想関数のテーブル、および継承され
図1は、仮想関数はvtableのを持っています
2.クラスに属するvtableのすべてのオブジェクト、共有クラスの仮想関数テーブルポインタによって、クラスの仮想関数テーブル
3.仮想関数テーブルの役割:実際の機能を示すマップが呼び出さなければならないように、仮想テーブルのように、サブクラスオブジェクトを操作するために、親クラスのポインタを使用して
4.C ++コンパイラは、vtableのポインタは、我々は、オブジェクト・インスタンスを介して対処することができることを意味し、(多重継承継承または多層の場合に得られた関数テーブルの性能を確保するために)最前位置・オブジェクト・インスタンス内に存在する保証します仮想関数テーブルを取得し、仮想関数呼び出しに応答して、仮想関数ポインタ項を横断することができ、
PS:多重継承:親クラスの複数、継承する多層:親クラスの親クラスがあります
[仮想関数ポインタを移動することで、仮想関数テーブル、仮想関数呼び出し応答]
#include <ビット/ STDC ++ H> 使用して 名前空間STDを、 クラスベース { パブリック: 仮想 ボイドF() { COUT << " ベース:: F " << ENDL。 } 仮想 空隙G() { COUT << " ベース:: G " << ENDL。 } 仮想 ボイドH() { COUT << " ベース:: H " << ENDL。 typedefのボイド(*ファン)(ボイド); ベースB; ファンpFun = NULL; int型のmain() { COUT << " 仮想関数テーブルアドレス:" <<(INT *)(&B)<< ENDL; COUT << " vtableの-関数の最初のアドレス:" <<(int型 *)*(int型 *)(&B)<< てendl; // 仮想関数を仮想関数テーブルによって呼び出される pFun =(楽しい)*((int型 *)* (int型 *)(&B)); // ベース:: F() pFun(); pFun =(楽しい)*((int型*)*(INT *)(&B)+ 1)。 // ベース:: G() pFun(); pFun =(楽しい)*((int型 *)*(int型 *)(&B)+ 2); // ベース:: H() pFun(); }
結果:
仮想関数テーブルアドレス:0x477008 仮想関数テーブル-関数の先頭アドレス:0x473668 ベース:: F ベースG :: ベース:: H
上記には連続ではありません
報道せずに継承された1つの仮想関数
1)宣言順に仮想関数テーブルへ
2)旧親クラスの仮想関数、サブクラスで仮想関数
3)エンドポイント識別子番号仮想関数テーブルの端、異なるコンパイラで異なる値
#include<bits/stdc++.h> using namespace std; class Base { public: virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } }; class Base_son:public Base { public: virtual void f1() { cout << "Base_son::f1" << endl; } virtual void g1() { cout << "Base_son::g1" << endl; } virtual void h1() { cout << "Base_son::h1" << endl; } }; typedef void(*Fun)(void); Base_son d; Fun pFun = NULL; int main() { cout << "虚函数表地址:" << (int*)(&d) << endl; cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&d) << endl; //通过虚函数表调用虚函数 pFun = (Fun)*((int*)*(int*)(&d)); // Base::f() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+1); // Base::g() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+2); // Base::h() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+3); // Base_son::f1() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+4); // Base_son::g1() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+5); // Base_son::h1() pFun(); return 0; }
结果:
虚函数表地址:0x477008 虚函数表 — 第一个函数地址:0x473668 Base::f Base::g Base::h Base_son::f1 Base_son::g1 Base_son::h1
2.单层继承有虚函数覆盖的情况
1)覆盖的f()函数被放到了虚函数表中原父类虚函数的位置
2)没有被覆盖的函数没有变化
#include<bits/stdc++.h> using namespace std; class Base { public: virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } }; class Base_son:public Base { public: virtual void f() { cout << "Base_son::f" << endl; } virtual void g1() { cout << "Base_son::g1" << endl; } virtual void h1() { cout << "Base_son::h1" << endl; } }; typedef void(*Fun)(void); Base_son d; Fun pFun = NULL; int main() { cout << "虚函数表地址:" << (int*)(&d) << endl; cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&d) << endl; //通过虚函数表调用虚函数 pFun = (Fun)*((int*)*(int*)(&d)); // Base_son::f() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+1); // Base::g() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+2); // Base::h() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+3); // Base_son::g1() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+4); // Base_son::h1() pFun(); return 0; }
结果:
虚函数表地址:0x477008 虚函数表 — 第一个函数地址:0x473650 Base_son::f Base::g Base::h Base_son::g1 Base_son::h1
通过父类指针指向子类实例,子类覆盖父类方法,然后调用子类的方法,这样就实现了多态
3.多重继承无虚函数覆盖
1)每个父类都有自己的虚函数表
2)子类的虚函数被放到第一个父类的虚函数表中
这样做是为了解决不同的父类类型指针指向同一个子类实例,而能够调用到实际的函数
4.多重继承存在虚函数覆盖
1)父类虚函数表中被覆盖的虚函数全部被替换成了子类的覆盖虚函数
这样我们就通过父类指向子类从而访问子类的f()了
Derive d; Base1 *b1 = &d; Base2 *b2 = &d; Base3 *b3 = &d; b1->f(); //Derive::f() b2->f(); //Derive::f() b3->f(); //Derive::f() b1->g(); //Base1::g() b2->g(); //Base2::g() b3->g(); //Base3::g()
使用虚函数表可以做一些违反c++语义的事情:
1)通过父类指针访问子类自己的虚函数
子类的虚函数X在父类中没有,所以子类的虚函数X没有覆盖父类的虚函数,但是如果我们通过父类的指针来访问子类自己的虚函数的编译器会报错
Base1 *b1 = new Derive(); b1->f1(); //编译出错
但是我们通过虚函数表可以做到这种违背C++语义的事情:使用父类指针访问子类自己的虚函数
2)访问父类non-public的虚函数
如果父类的虚函数是private或protected的,但是这些feipublic的父类虚函数同样会存在于虚函数表中,所以我们可以通过访问虚函数表访问到这些虚函数
附上多重继承有虚函数覆盖的样例代码:
#include <iostream> using namespace std; class Base1 { public: virtual void f() { cout << "Base1::f" << endl; } virtual void g() { cout << "Base1::g" << endl; } virtual void h() { cout << "Base1::h" << endl; } }; class Base2 { public: virtual void f() { cout << "Base2::f" << endl; } virtual void g() { cout << "Base2::g" << endl; } virtual void h() { cout << "Base2::h" << endl; } }; class Base3 { public: virtual void f() { cout << "Base3::f" << endl; } virtual void g() { cout << "Base3::g" << endl; } virtual void h() { cout << "Base3::h" << endl; } }; class Derive : public Base1, public Base2, public Base3 { public: virtual void f() { cout << "Derive::f" << endl; } virtual void g1() { cout << "Derive::g1" << endl; } }; typedef void(*Fun)(void); int main() { Fun pFun = NULL; Derive d; int** pVtab = (int**)&d; //Base1's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0); pFun = (Fun)pVtab[0][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1); pFun = (Fun)pVtab[0][1]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2); pFun = (Fun)pVtab[0][2]; pFun(); //Derive's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3); pFun = (Fun)pVtab[0][3]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[0][4]; cout<<pFun<<endl; //Base2's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); pFun = (Fun)pVtab[1][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); pFun = (Fun)pVtab[1][1]; pFun(); pFun = (Fun)pVtab[1][2]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[1][3]; cout<<pFun<<endl; //Base3's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); pFun = (Fun)pVtab[2][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); pFun = (Fun)pVtab[2][1 ]。 pFun(); pFun =(FUN)pVtab [ 2 ] [ 2 ]。 pFun(); // のvtableの尾 pFun =(FUN)pVtab [ 2 ] [ 3 ]。 coutの << pFun << てendl; リターン 0 ; }
仮想関数と正常な機能について:
1.仮想関数を動的に仮想関数テーブルメモリにアクセスポイントによって生成されたクラスは、オブジェクトクラスに割り当てられていない、ない仮想関数テーブル、仮想関数にアクセスすることはできません
2.クラス通常の機能にアクセスできるオブジェクト・クラスを割り当てていない、静的に生成します
参考:マウスの左耳:C ++の仮想関数テーブルの解析