c++多態

最近查看有關多態的知識,感覺有些陌生,因此梳理一下c++多態的知識

     多態:分爲靜多態和動多態,分別對應編譯時期綁定的稱爲靜多態(早綁定),非編譯時期綁定的稱爲動多態(晚綁定)

靜多態通過函數模板和重載實現.本質是接口的複用。

函數重載:

int add(int a, int b)
{
	return a + b;
}
double add(double a, double b)
{
	return a + b;
}
函數模板(泛型編程):
template<typename T>
T add(T a, T b)
{
	return a + b;
}
多態:爲了實現代碼的接口重用,無論傳遞過來那個對象,函數都能找到合適的實現方法。

class Base {
public:
	void print()
	{
		cout << "Base::print()" << endl;
	}
};

class Derived : public Base {
public:
	//void print()
	//{
	//  cout << "Derived::print()" << endl;
	//}

};

void Test()
{
	Base b;
	Derived d;
	b.print();//調用基類的打印Base::print()
	d.print();//子類繼承了基類的print()且子類本身沒有print(),因此此處還是調用基類的print()打印 Base::print()
}

int main()
{
	Test();
	return 0;
}
我們取消派生類print()函數註釋,

    Base b;
    Derived d;
    b.print();//調用基類的print()打印Base::print()
    d.print();//子類本身中print(),此時構成了重定義,即基類中的print()被隱藏,因此調用的是子類的print()打印    Derived::print()
    d.Base::print();//若想調用基類的print()需要加類的作用域限定符,打印Base::print()
試試用指針來調用:

    Base b;
    Derived d;
    Base* pb = &b;
    Derived* pd = &d;
    pb->print();//pb指向基類,打印Base::print()//call Base::fun(0DE14C9H)

    pd->print();//pd指向子類,打印Derived::print()

    pb = &d; pb->print();//pb指向子類,卻打印Base::print()
引用也相同。

爲什麼基類對象引用派生類對象,打印的是基類函數?

因爲在靜多態時期,編譯時期就將函數的實現和函數的地址綁定,無論指針還是引用都在編譯時期就確定了基類對象,因此打印基類函數。爲了避免這種情況,我們引入了動態多態

什麼叫多態?

基類指針(同引用)指向不同的派生類對象,調用派生類和基類的同名覆蓋方法,基類指針指向哪個派生類對象,調用的就是它的覆蓋方法!程序運行期間(非編譯期)才能判斷所引用對象的實際類型,根據其實際類型調用相應的方法。多態就是通過動態綁定來實現的  =》  動態綁定通過vfptrvftable來實現的,繼承+虛函數實現的。

實現格式:

基類成員函數加virtual,聲明爲虛函數,派生類重寫該函數,編譯器將實現動態綁定。

class Base {
public:
	virtual void print()
	{
		cout << "print::fun()" << endl;
	}
};
派生類保持不變
void Test()
{
	Derived d;

	Base* pb = &d;
	pb->print();//基類指針pb指向子類,可以打印Derived::print()//
	Base& rb = d;
	rb.print();//基類對象pb引用子類,可以打印Derived::print()//lea eax,[d]   mov dword ptr[rb],eax
}
實現動態綁定條件:虛函數,基類指針/引用調用虛函數。

重載:1、在同一作用域中
    2、函數名相同、參數個數或參數類型不同,返回值可同可不同
隱藏(重定義):1、在不同作用域中,分別在基類和派生類中
    2、隱藏只要求函數名相同就行
    3、在派生類中只要不是重寫就是隱藏
 隱藏是因爲在子類中定義了與基類同名的函數,而將基類的函數隱藏掉,要想訪問基類的函數,則必須加作用域限定符。

覆蓋(重寫):
    1、不再同一作用域中,分別在父類和子類中
    2、要求函數名相同,函數參數列表相同,返回值也相同
    3、基類函數必須是虛函數(virtual)
    4、訪問修飾符可以不同

純虛函數

在成員函數的形參後面寫上=0,則成員函數爲純虛函數。包含純虛函數的類叫做抽象類(也叫接口類),抽象類不能實例化出對象 。純虛函數必須在派生類中重新定義以後,派生類才能實例化出對象。

class Person
{
	virtual void Display() = 0; 
protected:
	string _name;
};
class Student : public Person
{
	void Display()
	{
		cout << "Student" << endl;
	}
};
int main()
{
	//Person p;//不能實例化對象
	Student stu;
}

抽象類 含有純虛函數的類就成爲抽象類。抽象類只是一種基本的數據類型,用戶需要在這個基礎上根據自己的需要定義處各種功能的派生類。抽象類的作用就是爲一個類族提供一個公共接口。抽象類不能定義對象,一個水果類可以派生出橘子香蕉蘋果等等,但是水果類本身定義對象並不合理也沒有必要。但是可以定義指向抽象類的指針變量,通過這個指針變量可以實現多態。

1 只有類的非靜態成員函數才能定義爲虛函數,靜態成員函數和友元函數不能定義爲虛函數。 

只有類的非靜態成員函數才能定義爲虛函數,靜態成員函數和友元函數不能定義爲虛函數。 

3 最好將基類的析構函數聲明爲虛函數。(析構函數比較特殊,因爲派生類的析構函數跟基類的析構 
函數名稱不一樣,但是構成覆蓋,這裏編譯器做了特殊處理)。 








  












發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章