C++primer讀書筆記11-多態
多態也是C++中的一個重要的方面,多態和動態類型,虛函數本質上是指相同的事情。
1 虛函數
類中的成員函數原型前面加上virtual 表面這個函數是個虛函數。虛函數的目的是為了在繼承它的派生類中重新定義這個函數,以便於通過基類的指針或引用在運行時對派生類的函數進行調用。
2 派生類和虛函數
派生類一般情況下要重定義所繼承的虛函數,有幾個注意事項。
<1>虛函數的聲明必須和基類中的函數聲明原型完全一致,例外的是當基類返回基類型的指針或者引用的時候,派生類可以派生類類型的指針或者引用
<2>virtual 關鍵詞加不加無所謂,但是一般要加上,能看的清楚這是個虛函數
<3>virtual的標簽一旦在基類中的函數前面加上,就永遠也去不掉
3 動態綁定的觸發
C++的函數調用默認不使用動態綁定,使用動態綁定需要兩個必須的條件
<1>通過基類類型的指針或者引用進行函數的調用
<2>函數是虛函數
通過這裡也可以說明,當通過指針或者引用調用函數的時候,即便派生類重定義了基類的版本,也不會調用派生類的版本,因為它不會觸發多態。
4 虛函數的覆蓋機制
有時候通過指針或者引用不想觸發多態,此時可以通過加限定符來顯式的調用基類的函數
Derived d;
Base *p = &d;
p->Base::func();
如果在派生類中的函數定義的時候想要調用基類的同名函數那麼也要這樣顯式的調用,否則會引發無窮遞歸。
5 虛函數跟復制控制
<1>通常將析構函數設置為虛函數,這是因為析構函數在執行的時候只會執行自身的部分。例如通過基類的指針或者引用來調用析構函數的話,那麼只會調用基類的構造函數,派生類的構造函數將不會被調用。
<2>構造函數不能定義為虛函數,因為在構造函數是在對象完全構造之前運行的,在構造函數執行的時候,對象的動態類型還不完整。
<3>賦值操作符原理上可以設置為虛函數,但是這樣做是沒意義的。原因如下:
虛函數要求函數原型完全一致,這其中包括了函數參數類型的一致,如果我們把operator = 設置為虛函數,那麼在基類中函數的參數是基類類型,在派生類虛函數的參數類型依然是基類類型。但是賦值操作符是要求函數參數類型和類類型一致,這樣就會產生非常容易混淆的東西。
class Base{
public:
virtual Base& operator =(Base& xx);
}
class Derived : public Base
{
public:
virtual Derived& operator =(Base& xx); //virtual function
Derived& operator =(Derived& xx); //real operator =
}
6 構造函數和析構函數中的虛函數
在運行構造函數和析構函數的時候,此時的對象都不是一個完整的類,這個時候編譯器將對象的對象視作在構造期間發生了變化,如果在其中調用虛函數的話,那麼調用的將會是類自身類型
定義的版本。
原因:基類在構造的時候,派生類部分的成員還沒有初始化,如果此時調用的是派生類的虛函數,那麼派生類的虛函數訪問類成員就會出問題。同理基類的析構函數也是這樣子,因為析構函
數直管自身成員的釋放,派生類在調用析構函數的時候先調用自身的析構函數,然後再去調用基類的析構函數,所以基類的析構函數不可能調用派生類的虛函數,因為派生類的成員已經被釋
放。實例如下:
//Base.h
#pragma once
#include
using namespace std;
class Base
{
public:
Base(void){ func();};
~Base(void){ funp();};
virtual void func(){cout<<"this is in Base constructor"<
//Derived.h
#pragma once
#include "base.h"
class Derived :
public Base
{
public:
Derived(void){func();}
~Derived(void){func();}
virtual void func(){cout<<"this is in Derived constructor"<
//main.cpp
#include "Derived.h"
void main()
{
Base X;
Derived Y;
}
7 純虛函數
虛函數的形參後面跟上“=0”則表示純虛函數。純虛函數需要注意的有以下幾點:
<1>純虛函數沒有函數體;
<2>最後面的“=0”並不表示函數返回值為0,它只起形式上的作用,告訴編譯系統“這是純虛函數”;
<3>這是一個聲明語句,最後應有分號。
<4>函數不能被調用
<5>不能創建有純虛函數的類對象
<6>如果在一個類中聲明了純虛函數,而在其派生類中沒有對該函數定義,則該虛函數在派生類中仍然為純虛函數。
含有一個或者多個純虛函數的類被稱為抽象類,抽象類不能創建對象。抽象類的意義在於提供功能基礎接口,然後由派生類來實現,在此基礎上實現多態。但是抽象類可以作為引用或者指針。實例程序如下:
//Base.h
#pragma once
#include
using namespace std;
class Base
{
public:
Base(void){ func();};
~Base(void){ funp();};
virtual void func(){cout<<"this is in Base constructor"<
//Derived.h
#pragma once
#include "base.h"
class Derived :
public Base
{
public:
Derived(void){func();}
~Derived(void){func();}
virtual void func(){cout<<"this is in Derived constructor"<
//main.cpp
#include "Derived.h"
void main()
{
Derived Y;
Base& X = Y;
}