c++關於多態的複習整理
一、靜態聯編
1、將源代碼中的函數調用解釋爲執行特定的函數代碼被稱爲函數名聯編
2、根據函數名選擇對應的函數體執行,但是在C++有函數重載所以這必須還要看函數的參數及函數名才能確定使用那個函數
3、這種可以在編譯過程就完成的這種編譯(靜態聯編)
#include<iostream>
using namespace std;
class Animal
{
public:
void eat();
};
class Dog:public Animal
{
public:
void eat();
};
void Animal::eat()
{
cout << "animal is eating" << endl;
}
void Dog::eat()
{
cout << "dog is eating" << endl;
}
void fun(Animal*animal)
{
animal->eat();
}
int main()
{
Animal *animal=new Animal;
fun(animal);
Dog *dog=new Dog;
fun(dog);
system("pause");
return 0;
}
輸出爲:
animal is eating
animal is eating
因爲在上面的例子中,發生了繼承關係,所以在void fun(Animal&animal)
函數中,編譯器允許進行類型轉換,這裏的類型轉換是當傳入dog
變量時派生類轉爲基類,是安全的,因爲,派生類繼承基類,派生類繼承了基類成員,派生類有基類的全部成員,在強制轉換爲基類時,因爲派生類所擁有的內存足夠,所以轉換爲基類時,可以將用不到的部分內存拋棄,所以安全,但是基類轉派生類,不安全,因爲基類不一定有派生類的成員,因此內存數據較派生類來說太小,強轉爲派生類時不知道該如何處理派生類中填充後剩餘的空間,所以可能會造成錯誤。
由此我們也可以得出一個道理:派生類可以強轉爲基類,基類不可以強轉爲派生類。
二、動態聯編(多態)
根據程序運行的時候來選擇函數使用的方式(動態聯編)
通過基類指針只能訪問派生類的成員變量,但是不能訪問派生類的成員函數。
爲了防止上例的情況發生,讓基類指針能夠訪問派生類的成員函數,C++ 增加了虛函數(Virtual Function)。使用虛函數只需要在函數聲明前面增加 virtual
關鍵字。
#include<iostream>
using namespace std;
class Animal
{
public:
virtual void eat();
};
class Dog:public Animal
{
public:
virtual void eat();
};
void Animal::eat()
{
cout << "animal is eating" << endl;
}
void Dog::eat()
{
cout << "dog is eating" << endl;
}
void fun(Animal*animal)
{
animal->eat();
}
int main()
{
Animal *animal=new Animal;
fun(animal);
Dog *dog=new Dog;
fun(dog);
system("pause");
return 0;
}
輸出爲:
animal is eating
dog is eating
有了虛函數,基類指針指向基類對象時就使用基類的成員(包括成員函數和成員變量),指向派生類對象時就使用派生類的成員。基類指針可以按照基類的方式來做事,也可以按照派生類的方式來做事,它有多種形態,或者說有多種表現方式,我們將這種現象稱爲多態(Polymorphism)。
C++ 虛函數對於多態具有決定性的作用,有虛函數才能構成多態。
-
只需要在虛函數的聲明處加上 virtual 關鍵字,類外函數定義處可以加也可以不加。
-
可以只將基類中的函數聲明爲虛函數,這樣所有派生類中的同名函數都將自動成爲虛函數。
-
當在基類中定義了虛函數時,如果派生類沒有定義新的同名函數作爲虛函數,那麼將使用基類的虛函數。
-
只有派生類的虛函數與基類的虛函數函數原型相同,才能構成多態(通過基類指針訪問派生類函數)
-
構造函數不能是虛函數。對於基類的構造函數,它僅僅是在派生類構造函數中被調用。
-
析構函數可以聲明爲虛函數,而且有時候必須要聲明爲虛函數。
三、純虛函數和虛析構
1、純虛函數
在C++中,可以將虛函數聲明爲純虛函數:
virtual 返回值類型 函數名(形參)=0
純虛函數沒有函數體,只有函數聲明,在虛函數聲明的結尾加上=0,表明此函數爲純虛函數。
一個類中如果有了純虛函數或純虛析構,那麼這個類就不能用來實例化對象,但是可以定義指針,這個類被定義爲抽象類。
#include<iostream>
using namespace std;
class Animal
{
public:
virtual void eat()=0;
};
class Dog:public Animal
{
public:
void eat();
};
void Dog::eat()
{
cout << "dog is eating" << endl;
}
void fun(Animal*animal)
{
animal->eat();
}
int main()
{
Animal *animal=new Dog;
fun(animal);
system("pause");
return 0;
}
2、虛析構
父類指針指向子類對象時,比如上面的 Animal *animal=new Dog
;delete父類指針,會調用父類指針析構,不會調用子類指針析構,所以當我們需要調用子類析構時(需要釋放子類對象的空間)需要把父類析構函數前面加上virtual,變成虛析構,這樣在釋放父類指針時會調用子類的析構