1、c++多型實現
c++中的虛函式的作用主要是實現多型的機制。關於多型,簡而言之就是用父型別的指標指向其子類的例項,然後通過父類的指標呼叫實際子類的成員函式。
例如:
#include #include using namespace std;
class base
; ~base(){};
virtual void fun()
這種技術可以讓父類的指標有「多種形態」,這是一種泛型技術。所謂泛型技術,說白了就是試圖使用不變的**來實現可變的演算法。比如:模板技術,rtti技術,虛函式技術,要麼是試圖做到在編譯時決議,要麼試圖做到執行時決議。
編譯器會為每個有虛函式的類建立乙個虛函式表(編譯的時候),該虛函式表將被該類的所有物件共享。類的每個虛成員佔據虛函式表中的一行。如果類中有n個虛函式,那麼其虛函式表將有n*4位元組的大小。
靜態繫結:編譯時繫結,通過物件呼叫
動態繫結:執行時繫結,通過位址實現
只有採用「指標->函式()」或「引用變數.函式()」的方式呼叫c++類中的
虛函式才會執行動態繫結。對於c++中的
非虛函式
,因為其不具備動態繫結的特徵,所以不管採用什麼樣的方式呼叫,都不會執行動態繫結。
即所謂動態繫結,就是基類的指標或引用有可能指向不同的派生類物件,對於非虛函式,執行時實際呼叫該函式的物件型別即為該指標或引用的靜態型別(基類型別);而對於虛函式,執行時實際呼叫該函式的物件型別為該指標或引用所指物件的實際型別
多重繼承時
用來做基類的類的析構函式一般都是虛函式。可是,為什麼要這樣做呢?
class clxbase
; virtual ~clxbase() {};
virtual void dosomething() ;
};class clxderived : public clxbase
; ~clxderived() ;
void dosomething() ;
};
clxbase
*ptest
=new
clxderived;
ptest
->
dosomething();
delete ptest;
的輸出結果是:
do something in class clxderived!
output from the destructor of class clxderived!
也就是說,類clxderived的
析構函式根本沒有被呼叫!一般情況下類的析構函式裡面都是釋放記憶體資源,而析構函式不被呼叫的話就會造成記憶體洩漏。我想所有的c++程式設計師都知道這樣的危險性。當然,如果在析構函式中做了其他工作的話,那你的所有努力也都是白費力氣。
所以,文章開頭的那個問題的答案就是--這樣做是為了當用乙個基類的指標刪除乙個派生類的物件時,派生類的析構函式會被呼叫。
當然,並不是要把所有類的析構函式都寫成虛函式。因為當類裡面有虛函式的時候,編譯器會給類新增乙個虛函式表,裡面來存放虛函式指標,這樣就會增加類的儲存空間。所以,只有當乙個類被用來作為基類的時候,才把析構函式寫成虛函式。
因為如果建構函式為虛函式的話,它將在執行期間被構造,而執行期則需要物件已經建立,建構函式所完成的工作就是為了建立合適的物件,因此在沒有構建好的物件上不可能執行多型(虛函式的目的就在於實現多型性)的工作。在繼承體系中,構造的順序就是從基類到派生類,其目的就在於確保物件能夠成功地構建。建構函式同時承擔著虛函式表的建立,如果它本身都是虛函式的話,如何確保vtbl的構建成功呢?
下面,再讓我們來看看多重繼承中的情況,假設有下面這樣乙個類的繼承關係。注意:子類並沒有覆蓋父類的函式。
對於子類例項中的虛函式表,是下面這個樣子:
我們可以看到:
1) 每個父類都有自己的虛表。
2) 子類的成員函式被放到了第乙個父類的表中。(所謂的第乙個父類是按照宣告順序來判斷的)
這樣做就是為了解決不同的父類型別的指標指向同乙個子類例項,而能夠呼叫到實際的函式。
下面我們再來看看,如果發生虛函式覆蓋的情況。
下圖中,我們在子類中覆蓋了父類的f()函式。
下面是對於子類例項中的虛函式表的圖:
我們可以看見,三個父類虛函式表中的f()的位置被替換成了子類的函式指標。這樣,我們就可以用任乙個父類指標來指向子類,並呼叫子類的f()了。如:
derive d;
base1 *b1 = &d;
base2 *b2 = &d;
base3 *b3 = &d;
b1->f(); //derive::f()
b2->f(); //derive::f()
b3->f(); //derive::f()
b1->g(); //base1::g()
b2->g(); //base2::g()
b3->g(); //base3::g()
我們知道,子類沒有過載父類的虛函式是一件毫無意義的事情。因為多型也是要基於函式過載的。雖然在上面的圖中我們可以看到子類的虛表中有derive自己的虛函式,但我們根本不可能使用基類的指標來呼叫子類的自有虛函式:
base1 *b1 = new derive();
b1->f1(); //編譯出錯
任何妄圖使用父類指標想呼叫子類中的未覆蓋父類的成員函式的行為都會被編譯器視為非法,所以,這樣的程式根本無法編譯通過。
但在執行時,我們可以通過指標的方式訪問虛函式表來達到違反c++語義的行為。
c++中虛擬繼承的概念
為了解決從不同途徑繼承來的同名的資料成員在記憶體中有不同的拷貝造成資料不一致問題,將共同基類設定為虛基類。這時從不同的路徑繼承過來的同名資料成員在記憶體中就只有乙個拷貝,同乙個函式名也只有乙個對映。這樣不僅就解決了二義性問題,也節省了記憶體,避免了資料不一致的問題
C 面試題 多型,虛函式,純虛函式
多型 是對於不同物件接收相同訊息時產生不同的動作。c 的多型性具體體現在執行和編譯兩個方面 在程式執行時的多型性通過繼承和虛函式來體現 在程式編譯時多型性體現在函式和運算子的過載上 開課吧廣場c 面試 虛函式 在基類中冠以關鍵字 virtual 的成員函式。它提供了一種介面介面。允許在派生類中對基類...
虛函式多型
一 知識點 1 乙個操作隨著所傳遞的物件型別的不同能夠做出不同的反應,其行為模式成為多型。p413 2 基類與派生類的同名操作,只要標記上virtual,則該操作便具有多型性。p416 3 一旦標記基類的函式為虛函式,便有連鎖反應,後面繼承的類中一切同名成員函式都變成了虛函式。如果是引發實際複製動作...
多型 虛函式
繼承模式分為單繼承和多繼承 在多繼承中會有相應的一些問題產生,比如資料冗餘,二義性等問題,那麼我們要如何解決這種問題。利用虛繼承便可以解決,在虛繼承的時候由虛基表可以去除資料冗餘和二義性。那麼函式可不可以寫成是虛函式呢?寫成虛函式有什麼作用?在派生類裡重寫基類的虛函式可以實現多型。include u...