1
、建構函式能不能是虛函式:
1.1
從儲存空間角度
虛函式對應乙個vtable
,這大家都知道,可是這個
vtable
其實是儲存在物件的記憶體空間的。問題出來了,如果建構函式是虛的,就需要通過
vtable
來呼叫,可是物件還沒有例項化,也就是記憶體空間還沒有,怎麼找
vtable
呢?所以建構函式不能是虛函式。
1.2
從使用角度
虛函式主要用於在資訊不全的情況下,能使過載的函式得到對應的呼叫。建構函式本身就是要
初始化例項,那使用虛函式也沒有實際意義呀。所以建構函式沒有必要是虛函式。
1.3
從作用
虛函式的作用在於通過父類的指標或者引用來呼叫它的時候能夠變成呼叫子類的那個成員函式。而建構函式是在建立物件時自動呼叫的,不可能通過父類的指標或者引用去呼叫,因此也就規定建構函式不能是虛函式。
1.4 總結
vbtl
在構造函式呼叫後才建立,因而建構函式不可能成為虛函式。在呼叫建構函式時還不能確定物件的真實型別(因為子類會調父類的建構函式);而且建構函式的作用是提供初始化,在物件生命期只執行一次,不是物件的動態行為,也沒有太大的必要成為虛函式
2
、析構函式可以為虛函式,
甚至是純虛的
我們往往通過基類的指標來銷毀物件。這時候如果析構函式不是虛函式,就不能正確識別物件型別從而不能正確呼叫析構函式。
class a
;
當乙個類打算被用作其它類的基類時,它的析構函式必須是虛的
。考慮下面的例子:
class a
~a() // 非虛析構函式
private:
char * ptra_;
};class b: public a
~b()
private:
char * ptrb_;
};void foo()
在這個例子中,程式也許不會象你想象的那樣執行,在執行
delete a
的時候,實際上只有
a::~a()
被呼叫了,而
b類的析構函式並沒有被呼叫!這是否有點兒可怕?
如果將上面
a::~a()
改為virtual
,就可以保證
b::~b()
也在delete a
的時候被呼叫了。因此基類的析構函式都必須是
virtual的。
純虛的析構函式並沒有什麼作用,是虛的就夠了。通常只有在希望將乙個類變成抽象類(不能例項化的類),而這個類又沒有合適的函式可以被純虛化的時候,可以使用純虛的析構函式來達到目的。
在有動態分配堆上記憶體的時候,析構函式必須是虛函式,但沒有必要是純虛的。
3
、關於建構函式
編譯器對每個包含虛函式的類建立乙個表(稱為
當乙個建構函式被呼叫時,它做的首要的事情之一是初始化它的vptr。因此,它只能知道它是「當前」類的,而完全忽視這個物件後面是否還有繼承者。當編譯器為這個建構函式產生**時,它是為這個類的建構函式產生**--既不是為基類,也不是為它的派生類(因為類不知道誰繼承它)。
所以它使用的vptr必須是對於這個類的vtable。而且,只要它是最後的構造函式呼叫,那麼在這個物件的生命期內,vptr將保持被初始化為指向這個vtable。但如果接著還有乙個更晚派生的建構函式被呼叫,這個建構函式又將設定vptr指向它的vtable,等.直到最後的建構函式結束。vptr的狀態是由被
最後呼叫的建構函式確定的。這就是為什麼構造函式呼叫是從
基類到派生類順序的另乙個理由。
但是,當這一系列構造函式呼叫正發生時,每個建構函式都已經設定vptr指向它自己的vtable。如果函式呼叫使用虛機制,它將只產生通過它自己的vtable的呼叫,而不是最後的vtable(所有建構函式被呼叫後才會有最後的vtable)。
7
、things to remember
定義乙個函式為虛函式,不代表函式為不被實現的函式。定義他為虛函式是為了允許用基類的指標來呼叫子類的這個函式。
定義乙個函式為純虛函式,才代表函式沒有被實現。定義他是為了實現乙個介面,起到乙個規範的作用,規範繼承這個。類的程式設計師必須實現這個函式。
有純虛函式的類是不可能生成類物件的,如果沒有純虛函式則可以。
多型一般就是通過指向基類的指標來實現的。
虛函式和純虛函式的區別:
虛函式1, 虛函式是非靜態的、非內聯的成員函式。
2, 若類中乙個成員函式被說明為虛函式,則該成員函式在派生類中可能有不同的實現。當使用該成員函式操作指標或引用所標識的物件時,對該成員函式呼叫可採用動態聯編。
5, 定義了虛函式後,程式中宣告的指向基類的指標就可以指向其派生類。在執行過程中,該函式可以不斷改變它所指向的物件,呼叫不同版本的成員函式,而且這些動作都是在執行時動態實現的。虛函式充分體現了物件導向程式設計的動態多型性。
純虛函式
1, 當在基類中不能為虛函式給出乙個有意義的實現時,可以將其宣告為純虛函式,其實現留待派生類完成。
2, 純虛函式的作用是為派生類提供乙個
一致的介面,它只是個函式的宣告而已,它告訴編譯器,在這個類中的這個純虛函式是沒有函式定義的,
該類不能建立物件(即不能例項化)
,但可以宣告指標,該類的
派生類負責給出這個虛函式的過載定義。
>
c++純虛函式
一、定義
純虛函式是在基類中宣告的虛函式,它在基類中沒有定義,但要求任何派生類都要定義自己的實現方法。在基類中實現純虛函式的方法是在函式原型後加「=0」
virtual void funtion1()=0
二、引入原因
1、為了方便使用多型特性,我們常常需要在基類中定義虛函式。
2、在很多情況下,基類本身生成物件是不合情理的。例如,動物作為乙個基類可以派生出老虎、孔雀等子類,但動物本身生成物件明顯不合常理。
為了解決上述問題,引入了純虛函式的概念,將函式定義為純虛函式(方法:virtual returntype function()= 0;),則編譯器要求在派生類中必須予以重寫以實現多型性。同時含有純虛函式的類稱為
抽象類,它不能生成物件
。這樣就很好地解決了上述兩個問題。
宣告了純虛函式的類是乙個抽象類。所以,使用者不能建立類的例項,只能建立它的派生類的例項。
純虛函式最顯著的特徵是:它們必須在繼承類中重新宣告函式(不要後面的=0,否則該派生類也不能例項化),而且它們在抽象類中往往沒有定義。
定義純虛函式的目的在於,使派生類僅僅只是繼承函式的介面。
純虛函式的意義,讓所有的類物件(主要是派生類物件)都可以執行純虛函式的動作,但類無法為純虛函式提供乙個合理的預設實現。所以類純虛函式的宣告就是在告訴子類的設計者,「你必須提供乙個純虛函式的實現,但我不知道你會怎樣實現它」。
抽象類的介紹
抽象類是一種特殊的類,它是為了抽象和設計的目的為建立的,它處於繼承層次結構的較上層。
(1)抽象類的定義: 稱帶有純虛函式的類為抽象類。
(2)抽象類的作用:
抽象類的主要作用是將有關的操作作為結果介面組織在乙個繼承層次結構中,由它來為派生類提供乙個公共的根,派生類將具體實現在其基類中作為介面的操作。所以派生類實際上刻畫了一組子類的操作介面的通用語義,這些語義也傳給子類,子類可以具體實現這些語義,也可以再將這些語義傳給自己的子類。
(3)使用抽象類時注意:
• 抽象類只能作為基類來使用,其純虛函式的實現由派生類給出。如果派生類中沒有重新定義純虛函式,而只是繼承基類的純虛函式,則這個派生類仍然還是乙個抽象類。如果派生類中給出了基類純虛函式的實現,則該派生類就不再是抽象類了,它是乙個可以建立物件的具體的類。
• 抽象類是不能定義物件的。
實際上我個人認為純虛函式的引入,是出於兩個目的
1、為了安全,因為避免任何需要明確但是因為不小心而導致的未知的結果,提醒子類去做應做的實現。
2、為了效率,不是程式執行的效率,而是為了編碼的效率。
>
C 虛析構函式 純虛析構函式
虛析構函式 析構函式的工作方式是 最底層的派生類 most derived class 的析構函式最先被呼叫,然後呼叫每乙個基類的析構函式。因為在c 中,當乙個派生類物件通過使用乙個基類指標刪除,而這個基類有乙個非虛的析構函式,則結果是未定義的。執行時比較有代表性的後果是物件的派生部分不會被銷毀。然...
C 虛析構函式 純虛析構函式
虛析構函式 析構函式的工作方式是 最底層的派生類 most derived class 的析構函式最先被呼叫,然後呼叫每乙個基類的析構函式。因為在c 中,當乙個派生類物件通過使用乙個基類指標刪除,而這個基類有乙個非虛的析構函式,則結果是未定義的。執行時比較有代表性的後果是物件的派生部分不會被銷毀。然...
C 虛析構函式 純虛析構函式
虛析構函式 析構函式的工作方式是 最底層的派生類 most derived class 的析構函式最先被呼叫,然後呼叫每乙個基類的析構函式。因為在c 中,當乙個派生類物件通過使用乙個基類指標刪除,而這個基類有乙個非虛的析構函式,則結果是未定義的。執行時比較有代表性的後果是物件的派生部分不會被銷毀。然...