理論知識:ios
當類中聲明虛函數時,編譯器會在類中生成一個虛函數表。c++
虛函數表是一個存儲類成員函數指針的數據結構。數據結構
虛函數表是由編譯器自動生成與維護的。函數
virtual成員函數會被編譯器放入虛函數表中。佈局
當存在虛函數時,每一個對象中都有一個指向虛函數表的指針(C++編譯器給父類對象、子類對象提早佈局vptr指針;當進行howToPrint(Parent *base)函數是,C++編譯器不須要區分子類對象或者父類對象,只須要再base指針中,找vptr指針便可。)。this
VPTR通常做爲類對象的第一個成員。spa
多態的實現原理
指針
說明1:code
經過虛函數表指針VPTR調用重寫函數是在程序運行時進行的,所以須要經過尋址操做才能肯定真正應該調用的函數。而普通成員函數是在編譯時就肯定了調用的函數。在效率上,虛函數的效率要低不少。對象
說明2:
出於效率考慮,沒有必要將全部成員函數都聲明爲虛函數。demo
#include <iostream> using namespace std; //多態成立的三個條件 //要有繼承 虛函數重寫 父類指針指向子類對象 class Parent { public: Parent(int a=0) { this->a = a; } virtual void print() //1 動手腳 寫virtal關鍵字 會特殊處理 //虛函數表 { cout<<"父類"<<endl; } virtual void print2() //1 動手腳 寫virtal關鍵字 會特殊處理 //虛函數表 { cout<<"父類"<<endl; } private: int a; }; class Child : public Parent { public: Child(int a = 0, int b=0):Parent(a) { this->b = b; } virtual void print() { cout<<"子類"<<endl; } private: int b; }; void HowToPlay(Parent *base) { base->print(); //有多態發生 //2 動手腳 //效果:傳來子類時,執行子類的print函數,傳來父類時執行父類的print函數 //C++編譯器根本不須要區分是子類對象,仍是父類對象 //父類對象和子類對象分步有vptr指針 , ==>虛函數表===>函數的入口地址 //遲綁定 (運行時的時候,c++編譯器纔去判斷) } int main() { Parent p1; //3 動手腳 提早佈局 //用類定義對象的時候,C++編譯器會在對象中添加一個vptr指針 Child c1; //子類裏面也有一個vptr指針 HowToPlay(&p1); HowToPlay(&c1); return 0; }
說明3 :C++編譯器,執行HowToPrint函數,不須要區分是子類對象仍是父類對象
下面來證實vptr指針的存在。
demo
#include <iostream> using namespace std; class Parent1 { public: Parent1(int a=0) { this->a = a; } void print() { cout<<"父類"<<endl; } private: int a; }; class Parent2 { public: Parent2(int a=0) { this->a = a; } virtual void print() { cout<<"虛析構函數的父類"<<endl; } private: int a; }; int main() { printf("sizeof(Parent):%d sizeof(Parent2):%d \n", sizeof(Parent1), sizeof(Parent2)); // 結果是普通類大小爲4,而把函數變成虛構函數以後大小爲8,因此證實了這裏vptr指針的存在性 return 0; }