虛函數出現是動態聯編的需要。
動態聯編又稱滯后聯編、晚期聯編。是因為在程序中出現函數調用時,在編譯階段無法確定調用哪一個函數,只有到了程序的運行階段才能確定調用哪一個函數。
virtual <數據類型> <函數名>( ) {........}
C++中對虛函數的處理方法:在編譯階段不確定調用哪一個函數,在此處保留所有同名虛函數的入口地址,在程序運行時,根據實參的類型來確定。
Tips:
(1) 虛函數,名字,參數個數,參數類型必須完全相同,否則屬於函數重載
(2) virtual 基類中不可缺省,派生類可以省略
(3)
虛函數不可為友元函數,因為友元函數不是類的成員函數
(4)
虛函數不可為內聯函數,因為內聯函數的調用處理在編譯時。
(5)
類的構造 函數不能定義為虛函數,但析構函數可以。因為構造函數構造一個對象的時候,必須知道對象的實際類型,而虛函數行為是在運行期間確定實際類型的。而在構造一個對象時,由於對象還未構造成功。編譯器無法知道對象 的實際類型,是該類本身,還是該類的一個派生類,或是更深層次的派生類。尤其構造函數中存在有動態申請空間時,在析構函數釋放,虛函數的析構函數可以實現撤銷對象的動態性
(6) 虛函數一般執行速度會比較慢,為了動態聯編,
編譯器為每個含有虛函數的對象增加了指向虛函數地址表的指針,通過該指針實現虛函數的間接調用。因此盡量不要使用虛函數。
構造函數之所以不能設置成虛函數,主要有以下的幾個原因。下面分別闡述一下。
1.虛函數的作用是什么?是實現部分或默認的功能,而且該功能可以被子類所修改。如果父類的構造函數設置成虛函數,那么子類的構造函數會直接覆蓋掉父類的構造函數。而父類的構造函數就失去了一些初始化的功能。這與子類的構造需要先完成父類的構造的流程相違背了。而這個后果會相當嚴重。
2.虛函數的調用是需要通過“虛函數表”來進行的,而虛函數表也需要在對象實例化之后才能夠進行調用。在構造對象的過程中,還沒有為“虛函數表”分配內存。所以,這個調用也是違背先實例化后調用的准則。
3.虛函數的調用是由父類指針進行完成的,而對象的構造則是由編譯器完成的,由於在創建一個對象的過程中,涉及到資源的創建,類型的確定,而這些是無法在運行過程中確定的,需要在編譯的過程中就確定下來。而多態是在運行過程中體現出來的,所以是不能夠通過虛函數來創建構造函數的,與實例化的次序不同也有關系。
純虛函數———virtual <數據類型> <函數名>( ) = 0
不等價於virtual <數據類型> <函數名>( ) { }
語法上講,純虛函數可以給出函數體。比如可以cout一句話。
定義了純虛函數的類是抽象類,抽象類只能做基類,不可定義抽象類的對象。
———summerized by Coconut