最近查看有關多態的知識,感覺有些陌生,因此梳理一下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()
引用也相同。
爲什麼基類對象引用派生類對象,打印的是基類函數?
因爲在靜多態時期,編譯時期就將函數的實現和函數的地址綁定,無論指針還是引用都在編譯時期就確定了基類對象,因此打印基類函數。爲了避免這種情況,我們引入了動態多態。
什麼叫多態?
基類指針(同引用)指向不同的派生類對象,調用派生類和基類的同名覆蓋方法,基類指針指向哪個派生類對象,調用的就是它的覆蓋方法!程序運行期間(非編譯期)才能判斷所引用對象的實際類型,根據其實際類型調用相應的方法。多態就是通過動態綁定來實現的 =》 動態綁定通過vfptr和vftable來實現的,繼承+虛函數實現的。
實現格式:
基類成員函數加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、要求函數名相同,函數參數列表相同,返回值也相同
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 只有類的非靜態成員函數才能定義爲虛函數,靜態成員函數和友元函數不能定義爲虛函數。
2 只有類的非靜態成員函數才能定義爲虛函數,靜態成員函數和友元函數不能定義爲虛函數。
3
最好將基類的析構函數聲明爲虛函數。(析構函數比較特殊,因爲派生類的析構函數跟基類的析構
函數名稱不一樣,但是構成覆蓋,這裏編譯器做了特殊處理)。