多態原理探究-從C++編譯器角度理解多態的實現原理

理論知識: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;
}
相關文章
相關標籤/搜索