面向對象的三大特性及多態的理解和原理剖析

面向對象的三大特性

一、封裝    突破了函數的概念ios

    把客觀事物封裝成抽象的類,經過訪問控制符把本身的成員變量和函數提供給可信的類和對象來操做,對不可信的進行信息隱藏。數據結構

二、繼承    實現了之前代碼的複用框架

    經過繼承能夠使用現有類的全部功能和成員變量(固然也須要看訪問控制符),並在無需從新編寫原來的類的狀況下對這些功能進行擴展。函數

三、多態    實現了對後來的代碼的複用佈局

    多態表現爲一樣的調用語句,有多種不一樣的調用方式spa

    例如:80年代的人寫的框架,90年代的框架什麼都不須要變,只須要根據業務須要繼承之前的父類,重寫父類之前的方法,將子類對象指針(或引用)傳入框架便可。指針

 

多態相關的知識

重載、重寫、重定義聯繫和區別

重載一、必須在一個類中code

          二、函數名字相同,參數列表不一樣(參數的類型、順序、個數決定),返回類型可同可不一樣,virtual關鍵字無關緊要.對象

          三、在編譯期間就肯定了調用了具體的某個函數。繼承

  注意:子類不能重載父類的函數(若是子類和父類中的函數名字相同,子類將會把父類的名稱覆蓋,佔用了父類函數的位置,此同名函數只有子類的函數能夠調用,父類的將不能被調用(若是要調用必須添加域名)

重寫一、必須在兩個類中且具備繼承關係

          二、方法名、參數個數和參數類型 都必須相同

          三、 基類函數必須有virtual關鍵字

重定義(隱藏):

        一、若是派生類的函數和基類的函數同名,可是參數不一樣,此時,無論有無virtual,基類的函數被隱藏

         二、若是派生類的函數與基類的函數同名,而且參數也相同,可是基類函數沒有vitual關鍵字,此時,基類的函數被隱藏

多態:符合重寫規則,要有父類引用(指針)指向子類對象。

靜態聯編和動態聯編

靜態聯編:在編譯器編譯階段,已經決定了函數的調用(不寫virtual關鍵字,是靜態聯編)

動態聯編:在運行階段,根據具體的對象(具體的類型),執行不一樣對象的函數,表現爲多態

 

爲何須要多態

若是沒有多態,C++編譯器只能根據咱們調用語句具體的對象類型,調用具體對象的方法(如父類對象只能調用父類的print方法,子類對象只能調用子類的print方法),

而不能對一樣的調用語句,有多種不一樣的調用方式(父類對象能夠調用子類的print方法)

代碼以下(沒有使用多態)

#include <iostream>
using namespace std;

class Parent    //父類
{
public:
	 void print()
	{
		cout<<"parent print..."<<endl;
	}
};

class Child:public Parent    //子類
{
public:
	 void print()
	{
		cout<<"child print..."<<endl;
	}
};

void playObj(Parent* p)    //調用函數
{
	p->print();
}

int main()
{
	Parent par;
	Child  chld;
	playObj(&par);    //調用函數傳入父類對象指針
	playObj(&chld);    //調用函數傳入子類對象指針
	system("pause");
	return 0;
}

使用多態後代碼(只是加了virtual關鍵字)

#include <iostream>
using namespace std;

class Parent    //父類
{
public:
	 virtual void print()    多態,在重寫的函數前加virtual關鍵字 
	{
		cout<<"parent print..."<<endl;
	}
};

class Child:public Parent    //子類
{
public:
	 virtual void print()    //多態,在重寫的函數前加virtual關鍵字 
	{
		cout<<"child  print..."<<endl;
	}
};

void playObj(Parent* p)    //調用函數
{
	p->print();
}

int main()
{
	Parent par;
	Child  chld;
	playObj(&par);    //調用函數傳入父類對象指針
	playObj(&chld);    //調用函數傳入子類對象指針
	system("pause");
	return 0;
}

多態成立條件

一、要有繼承         上例中類Child繼承了類Parent

二、要有函數重寫(虛函數)  上例中子類重寫了父類的虛函數print()

三、要有父類指針(父類引用)指向子類對象 上例中Parent* p指向了子類對象chld

多態意義

實現了接口重用(統一的表達式),接口重用帶來的好處是程序更易於擴展,代碼重用更加方便,更具備靈活性。

接口是最有價值的資源,只要定義好了接口,能夠省去不少人力物力去作重複的事情。

多態原理剖析

一、當類中聲明虛函數時,編譯器會在類中生成一個虛函數表

二、虛函數表是一個存儲類成員函數指針的數據結構

三、當實例化對象時,若是此對象類存在虛函數時,則此對象中會生成一個指向虛函數表的指針C++編譯器給父類對象、子類對象提早佈局vptr指針

四、當進行playObj(animal *pAnimal)函數是,C++編譯器不須要區分子類對象或者父類對象,只須要在pAnimal指針中,找vptr指針便可

代碼以下:

#include <iostream>
using namespace std;

class animal
{
public:
	virtual void  cry()		//聲明虛函數時,編譯器會在類中生成一個虛函數表
	{
		cout<<"animal cry..."<<endl;
	}
};

class dog:public animal
{
public:
	virtual void cry()		//聲明虛函數時,編譯器會在類中生成一個虛函數表
	{
		cout<<"dog cry...  wang wang "<<endl;
	}
};

class cat:public animal
{
public:
	virtual void cry()		//聲明虛函數時,編譯器會在類中生成一個虛函數表
	{
		cout<<"cat cry...  miao miao "<<endl;
	}
};

void playObj(animal *pAnimal)
{
	pAnimal->cry();		//C++編譯器不須要區分子類對象或者父類對象,只須要在pAnimal指針中,找vptr指針便可
}

int main()
{
	cat cc;		//實例化對象cc,在對象中會生成一個指向虛函數表的vptr指針
	dog dd;		//實例化對象dd,在對象中會生成一個指向虛函數表的vptr指針
	playObj(&cc);
	playObj(&dd);

	system("pause");
	return 0;
}

子類的vptr指針的分步初始化

一、當執行父類的構造函數時,子類的vptr指針指向父類的虛函數表

二、當父類的構造函數運行完畢後,會把子類的vptr指針指向子類的虛函數表

多態時,注意子類和父類的步長不同。

相關文章
相關標籤/搜索