c++虛函數的作用是什麼?

虛函數一般用在哪些場合?如果沒有虛函數有些功能就無法實現了么?


虛函數是 C++ 實現動態單分派子類型多態(dynamic single-dispatch subtype polymorphism)的方式。

  • 動態:在運行時決定的(相對的是靜態,即在編譯期決定,如函數重載、模板類的非虛函數調用)
  • 單分派:基於一個類型去選擇調用哪個函數(相對於多分派,即由多個類型去選擇調用哪個函數)
  • 子類型多態:以子類型-超類型關係實現多態(相對於用參數形式,如函數重載、模版參數)


http://blog.csdn.net/hackbuteer1/article/details/7558868

看這篇博客

hackbuteer1 是個演算法大牛!

首先:強調一個概念

定義一個函數為虛函數,不代表函數為不被實現的函數。

定義他為虛函數是為了允許用基類的指針來調用子類的這個函數。

定義一個函數為純虛函數,才代表函數沒有被實現。

定義純虛函數是為了實現一個介面,起到一個規範的作用,規範繼承這個類的程序員必須實現這個函數。

1、簡介

假設我們有下面的類層次:

class A
{
public:
virtual void foo()
{
cout&<&<"A::foo() is called"&<&foo(); // 在這裡,a雖然是指向A的指針,但是被調用的函數(foo)卻是B的!
return 0;
}

這個例子是虛函數的一個典型應用,通過這個例子,也許你就對虛函數有了一些概念。它虛就虛在所謂「推遲聯編」或者「動態聯編」上,一個類函數的調用並不是在編譯時刻被確定的,而是在運行時刻被確定的。由於編寫代碼的時候並不能確定被調用的是基類的函數還是哪個派生類的函數,所以被成為「虛」函數。

虛函數只能藉助於指針或者引用來達到多態的效果。

C++純虛函數

一、定義

 純虛函數是在基類中聲明的虛函數,它在基類中沒有定義,但要求任何派生類都要定義自己的實現方法。在基類中實現純虛函數的方法是在函數原型後加「=0」

 virtual void funtion1()=0

二、引入原因

  1、為了方便使用多態特性,我們常常需要在基類中定義虛擬函數。

  2、在很多情況下,基類本身生成對象是不合情理的。例如,動物作為一個基類可以派生出老虎、孔雀等子類,但動物本身生成對象明顯不合常理。

  為了解決上述問題,引入了純虛函數的概念,將函數定義為純虛函數(方法:virtual ReturnType Function()= 0;),則編譯器要求在派生類中必須予以重寫以實現多態性。同時含有純虛擬函數的類稱為抽象類,它不能生成對象。這樣就很好地解決了上述兩個問題。

聲明了純虛函數的類是一個抽象類。所以,用戶不能創建類的實例,只能創建它的派生類的實例。

純虛函數最顯著的特徵是:它們必須在繼承類中重新聲明函數(不要後面的=0,否則該派生類也不能實例化),而且它們在抽象類中往往沒有定義。

定義純虛函數的目的在於,使派生類僅僅只是繼承函數的介面。

純虛函數的意義,讓所有的類對象(主要是派生類對象)都可以執行純虛函數的動作,但類無法為純虛函數提供一個合理的預設實現。所以類純虛函數的聲明就是在告訴子類的設計者,「你必須提供一個純虛函數的實現,但我不知道你會怎樣實現它」。

抽象類的介紹

抽象類是一種特殊的類,它是為了抽象和設計的目的為建立的,它處於繼承層次結構的較上層。

(1)抽象類的定義: 稱帶有純虛函數的類為抽象類。

(2)抽象類的作用:

抽象類的主要作用是將有關的操作作為結果介面組織在一個繼承層次結構中,由它來為派生類提供一個公共的根,派生類將具體實現在其基類中作為介面的操作。所以派生類實際上刻畫了一組子類的操作介面的通用語義,這些語義也傳給子類,子類可以具體實現這些語義,也可以再將這些語義傳給自己的子類。

(3)使用抽象類時注意:

? 抽象類只能作為基類來使用,其純虛函數的實現由派生類給出。如果派生類中沒有重新定義純虛函數,而只是繼承基類的純虛函數,則這個派生類仍然還是一個抽象類。如果派生類中給出了基類純虛函數的實現,則該派生類就不再是抽象類了,它是一個可以建立對象的具體的類。

? 抽象類是不能定義對象的。

總結:

1、純虛函數聲明如下: virtual void funtion1()=0; 純虛函數一定沒有定義,純虛函數用來規範派生類的行為,即介面。包含純虛函數的類是抽象類,抽象類不能定義實例,但可以聲明指向實現該抽象類的具體類的指針或引用。

2、虛函數聲明如下:virtual ReturnType FunctionName(Parameter);虛函數必須實現,如果不實現,編譯器將報錯,錯誤提示為:

error LNK****: unresolved external symbol "public: virtual void __thiscall ClassName::virtualFunctionName(void)"

3、對於虛函數來說,父類和子類都有各自的版本。由多態方式調用的時候動態綁定。

4、實現了純虛函數的子類,該純虛函數在子類中就編程了虛函數,子類的子類即孫子類可以覆蓋該虛函數,由多態方式調用的時候動態綁定。

5、虛函數是C++中用於實現多態(polymorphism)的機制。核心理念就是通過基類訪問派生類定義的函數。

6、在有動態分配堆上內存的時候,析構函數必須是虛函數,但沒有必要是純虛的。

7、友元不是成員函數,只有成員函數才可以是虛擬的,因此友元不能是虛擬函數。但可以通過讓友元函數調用虛擬成員函數來解決友元的虛擬問題。

8、析構函數應當是虛函數,將調用相應對象類型的析構函數,因此,如果指針指向的是子類對象,將調用子類的析構函數,然後自動調用基類的析構函數。

有純虛函數的類是抽象類,不能生成對象,只能派生。他派生的類的純虛函數沒有被改寫,那麼,它的派生類還是個抽象類。

定義純虛函數就是為了讓基類不可實例化化

因為實例化這樣的抽象數據結構本身並沒有意義。

或者給出實現也沒有意義

實際上我個人認為純虛函數的引入,是出於兩個目的

1、為了安全,因為避免任何需要明確但是因為不小心而導致的未知的結果,提醒子類去做應做的實現。

2、為了效率,不是程序執行的效率,而是為了編碼的效率。


虛函數主要是用來實現多態和多重繼承的

沒有虛函數理論上也可以實現多態,但是太麻煩了,沒有虛函數清晰

主要是在多態上使用。

多態是什麼呢:

多態就是一個指針指向子類對象,那麼他調用的函數就是子類的對象的。

這就實現了什麼?

實現了一個函數會根據傳入參數的不同有不同的功能。

函數有多個狀態。 就是多態

void fuck (Animal *a);

如果沒有多態 那麼 這個函數 只能fuck Animal

想要fuck多種 就要用重載,寫多個同名函數 參數不同

但是有了多態 他可以fuck所有Animal的子類 比如牛 羊 耗子 什麼的

根據傳進來的參數不同,fuck不同的動物。

這樣這個函數就是fuckAnimalYouWant 函數了

這就多態了

如果現在你想fuck狗

要做的只是:

1.創建狗類繼承Animal

2.傳入到fuck函數中

此時 並不需要該代碼 即使傳入到fuck函數中 也可以是通過配置文件這種形式改變傳入參數,並沒有改變代碼

如果沒有多態呢?

1.創建狗對象繼承Animal

2.在類中增加一個fuck函數 參數是Dog

這就改變代碼了。

違反了設計模式開閉原則:對擴展開放,對修改關閉

實現多態的方式是什麼

子類有和父類 同名同參數(重寫) 的函數

所以 對於多態的狀態來說,一定會有兩個同名同參數函數,分別定義在子類中和父類中

那麼當用指針調用一個函數的時候,究竟是調用子類的還是父類的:

對於一個類成員函數的調用:

Animal *a = new Dog()

a-&>bark()

表面上看來是在一個對象內部去調用函數,好像是這個函數是存在於對象內部的感覺。。

但是實際上不是

實際上所有的函數都存放在單獨的一個代碼區,而函數對象裡面只有成員變數。

也就是說

a-&>bark()

實際上被編譯器變成了

Animal::bark(a) 把對象a傳進去 才告訴了編譯器具體是哪個對象調用的這個函數

但是對於多態來說:

Animal::bark() 有

Dog::bark()也一定有

究竟選哪個函數執行呢?

此時還沒運行,還是在編譯階段,也就是內存中什麼Dog啊 Animal 的對象還沒有存在。

所以只能根據a這個指針的類型 來決定使用哪個。也就是 Animal::bark()

所以

無論a指向什麼子類,他調用的bark函數 都是Animal版本的父類版本的bark函數。

所以

現在沒有辦法達到多態的效果

這個在編譯時決定函數是哪個類的函數的方式就叫做靜態聯編

a-&>bark()

現在希望調用的是Dog::bark(a)

調用的是Dog類的

所以就不能使用靜態聯編,靜態聯編在編譯的時候決定函數是哪個。

為什麼知道調用的是Dog::bark(a) 而不是 Cat::bark(a)呢?

因為a指向的對象是一個Dog 而不是一個Cat

所以必須在a指向的對象創建出來後才能決定究竟調用哪個函數

這就是動態聯編

怎麼把靜態聯編改成動態聯編呢??

編譯器在靜態聯編時做了以下轉換

a-&>bark ---&> Animal::bark(a)

當bark函數被設置為虛函數時,就不會進行那個轉換了,而是轉化為

a-&>bark ----》 (a-&>vtpl[1])(a)

先通過a指針找到對象,再找到對象中的虛表,再在虛表裡面找到該調用的那個函數的函數指針

因為必須要在a指向的對象裡面找,所以 必須等到a被創建出來,所以必須是運行時

所以這就是動態聯編 運行時決定調用哪個函數

而這個vtpl就是虛表指針,

當類中出現一個virtual指針時編譯器就自動在類的成員變數里加入一個指針成員。

這個指針成員指向這個類的虛表指針。

當子類重寫父類方法時 同時也是把繼承自父類的虛表中對應函數的索引的函數指針從父類函數改成了自己的函數。。

這就造成了子類和父類對象裡面的虛表內容不同。

所以動態聯編時 去子類 或者父類裡面找虛表,

調用的函數不同。

就形成了多態的效果

這個過程其實自己寫邏輯也能寫出來。

但是官方作出了virtual就是為了釋放這個工作量

當你寫了virtual這個關鍵字 ,以上的創建虛表 繼承 等一系列工作就都自動執行了。

實現了面向對象的思想

但是降低了執行效率。


沒虛函數當然能過,只要有函數指針就行了。有人說那幹嘛不用C,我的解釋是C當然可以實現dynamic dispatch,只是效率和bug-free不可兼得罷了 - 簡單的方案(比如fuse那樣搞一大坨函數指針)太低效;手寫虛表雖然一樣高效,但是要破壞類型系統弄很多cast,還要算偏移量。

什麼「OOP」都是虛妄。C++虛函數能實現的只是簡單的dynamic dispatch,還是single dispatch,很多現實問題根本不夠用。舉例來說,你實現個bool Intersect(const Shape, const Shape)我看看。。還不是得到處開洞手寫double dispatch。而模板特化要解決這個問題就漂亮多了。

至於複雜的類繼承關係。。你們開心就好:)

虛函數比較實用的地方第一是表達unbounded discriminated union/variant/sum type,隨你們怎麼叫。。基本上是表示數據結構的,虛函數介面不應該承載太多邏輯,甚至可以只留一個虛析構。這時候就應該勇敢地配合dynamic_cast用,不要羞澀。。

第二個就是type erasure並以此封裝獨立的二進位模塊了。一個極端的例子是boost::any,幾乎抹得一乾二淨。shared_ptr把allocator和deleter都抹了,libstdc++就是用虛函數抹的。一般來說設計正常的模塊邊界的介面比較簡單,虛函數也能承載。

這兩點在LLVM的編程手冊中有提及:LLVM Programmer』s Manual ,Designing Type Hiercharies and Polymorphic Interfaces 一節。


最常用的是類型擦除了吧。


可能給例子比較好理解的說~~

這樣運行會輸出「A」。

----------------------------------------------------------------------------------

這樣會輸出「B」。

--------------------------------------------------------------------------

補充:上面兩個因為是圖我就不改了~ @vczh 輪子哥教導我們A的析構函數也要加virtual,否則delete p的時候調的是A的析構函數可能會有問題~~

--------------------------------------------------------------------------

簡單說就是

當基類指針指向一個子類對象,通過這個指針調用子類和基類同名成員函數的時候,基類聲明為虛函數「子類不寫也可以」就會調子類的這個函數,不聲明就會調用基類的。自己體會一下0.0

--------------------------------------------------------------------------

需要用的情形的話大概是這樣:

比如你有個遊戲,遊戲里有個虛基類叫「怪物」,有純虛函數 「攻擊」。然後派生出了三個子類「狼」「蜘蛛」「蟒蛇」,都實現了自己不同的「攻擊」函數,比如狼是咬人,蜘蛛是吐絲,蟒蛇把你纏起來~~

然後出現好多怪物的時候就可以定義一個 虛基類指針數組,把各種怪物的指針給它,然後迭代循環的時候直接 monster[i]-&>attack() 攻擊玩家就行了,大概見下圖:

如果沒有虛函數的話,管理一堆差不多但是類型又不一樣的對象的時候就比較麻煩了。

---------------------------------------------------------------

大一水平求指導~~不知道說的對不對~~還有代碼格式,變數命名神馬的都求指導~0.0~ @vczh


上帝是一個程序員,創造了動物(基類),給予了動物吃飯,睡覺,叫喚等通用功能。(封裝)

只指定了平均睡覺八小時(虛函數),其中沒有指定具體的吃飯,叫喚的行為。(純虛函數)

然後細分一下,動物有貓狗羊和人。(繼承)

人類明確它們物種的時候(明確類型的派生類指針)

貓吃魚 狗吃肉 羊吃草

貓喵喵 狗汪汪 羊咩咩

(多態 同名覆蓋)

一切都如此順理成章。

突然人發現一隻動物!

這只是什麼呢?誒?這貨不知道是啥!只能用"動物"來稱呼他!(基類指針指向子類對象)

當沒有虛函數的時候,人類發現這隻動物不會叫也不會吃!因為他根本沒有這樣的實現!(注意 真正編程上如果派生類不對純虛函數進行實現將無法通過編譯)

有了虛函數,讓那隻動物"吃",發現他吃草!於是捅一下這隻動物,發現它會 哞哞叫!於是得知這是一頭牛!

可以吃!於是人類就把它吃掉了。

總結:虛函數目的是在用基類指針指向派生類的時候還能正確調用派生的實現。


實現面向對象,模擬類型反射等

#include &
#include &
#include &

enum ObjectType {
T_Object,
T_Number,
T_String,
};
class Object
{
public:
virtual ObjectType get_type() const { return T_Object; }
};

class Number : public Object
{
public:
ObjectType get_type() const { return T_Number; }
private:
int data;
};

class String : public Object
{
public:
ObjectType get_type() const { return T_String; }
private:
std::string data;
};

ObjectType get_type(const Object *object)
{
return object-&>get_type();
}
Object* createObject(ObjectType type)
{
Object *object;

switch (type)
{
case T_Object:
object = new Object();
break;
case T_Number:
object = new Number();
break;
case T_String:
object = new String();
break;
default:
object = new Object();
}

return object;
}

int main(void)
{
std::vector& vec;
vec.push_back(createObject(T_Object));
vec.push_back(createObject(T_Number));
vec.push_back(createObject(T_String));

for (Object *e : vec) {
std::cout &<&< static_cast&(get_type(e)) &<&< std::endl; } }


多態


用c語言的理解就是函數指針。常見於介面設計,比如驅動程序介面,不同的驅動程序會有一些介面是必須的,比如初始化,讀,寫,seek。調用這些介面,不需要區別不同的驅動。跟重載的區別就是重載知道要調用哪個驅動,而虛函數調用者不清楚是哪個驅動。簡單來說,就是驅動程序修改之後,調用者那部分代碼無需修改。


C++ 對象的內存布局

C++ 虛函數表解析

推薦前輩 @陳皓 的兩篇文章。


虛函數是為了將方法和對象綁定在一起,而不是直接將方法和類型綁定在一起,從而可以實現運行時的多態


學習C++的多態性,你必然聽過虛函數的概念,你必然知道有關她的種種語法,但你未必了解她為什麼要那樣做,未必了解她種種行為背後的所思所想。深知你不想在流於表面語法上的蜻蜓點水似是而非,今天我們就一起來揭開擋在你和虛函數(女神)之間的這一層窗戶紙。

首先,我們要搞清楚女神的所作所為,即語法規範。然後再去探究她背後的邏輯道理。她的語法說來也不複雜,概括起來就這麼幾條:

  1. 在類成員方法的聲明(不是定義)語句前面加個單詞:virtual,她就會搖身一變成為虛函數。
  2. 在虛函數的聲明語句末尾中加個 =0 ,她就會搖身一變成為純虛函數。
  3. 子類可以重新定義基類的虛函數,我們把這個行為稱之為複寫override)。
  4. 不管是虛函數還是純虛函數,基類都可以為提供他們的實現implementation),如果有的話子類可以調用基類的這些實現。
  5. 子類可自主選擇是否要提供一份屬於自己的個性化虛函數實現。
  6. 子類必須提供一份屬於自己的個性化純虛函數實現。

語法都列出來了,背後的邏輯含義是什麼呢?我們用一個生動的例子來說明,虛函數是如何實現多態性的。

假設我們要設計關於飛行器的類,並且提供類似加油、飛行的實現代碼,考慮具體情況,飛行器多種多樣,有民航客機、殲擊機、轟炸機、直升機、熱氣球、火箭甚至竄天猴、孔明燈、紙飛機!

假設我們有一位牛得一比的飛行員,他能給各式各樣的飛行器加充不同的燃料,也能駕駛各式各樣的飛行器。下面我們來看看這些類可以怎麼設計。

牛得一比的飛行員

首先,飛行器。由於我們假設所有的飛行器都有兩種行為:加油飛行。因此我們可以將這兩種行為抽象到一個基類中,並由它來派生具體的某款飛行器。

這是一個描述飛行器的基類,提供了兩個基本的功能:加油和飛行

class aircraft { void refuel(); // 加燃油,普通虛函數

void fly()=0; // 飛行,純虛函數

};

這是一個普通虛函數,意味著基類希望子類提供自己的個性化實現代碼,但基類同時也提供一個預設的虛函數實現版本,在子類不複寫該虛函數的情況下作為備選方案

void aircraft::refuel() {

// 加充通用型燃油

}

這是一個純虛函數,意味著基類強制子類必須提供自己的個性化版本,否則編譯將失敗。但讓人驚奇的是,C++仍然保留了基類提供該純虛函數代碼實現的權利,這也許是給千變萬化的實際情況留下後路

void aircraft::fly() {

// 一種不應該被使用的預設飛行方案

}

有了基類aircraft,我們就可以瀟洒地派生出各式各樣的飛行器了,比如轟炸機直升機

轟炸機類定義,複寫了加油和飛行

class bomber : public aircraft { void refuel(){} // 加充轟炸機的特殊燃油!

void fly(){} // 轟炸機實彈飛行!

};

直升機類定義,複寫了飛行代碼,但沒有複寫加油

class copter: public aircraft { void fly(){} // 直升機盤旋!

};

以上代碼可以看到,直升機類(copter)沒有自己的加油方式,直接使用了基類提供的預設加油的方式。此時我們來定義一個能駕馭多機型的王牌飛行員類:

一個能王牌飛行員

class pilot { void refuelPlane(aircraft *p); void dirvePlane(aircraft *p); };

給我什麼飛機我就加什麼油

void pilot::refuelPlane(aircraft *p) { p-&>refuel(); }

給我什麼飛機我就怎麼飛

void pilot::dirvePlane(aircraft *p) { p-&>fly(); }

很明顯,我們接下來要給這位很浪的飛行員表演一下操縱各種飛行器的機會,我們來定義各種飛機然後丟給他去處理

定義兩架飛機,一架轟6K,一架武直10

aircraft *H6K = new bomber; aircraft *WZ10 = new copter;

來一個王牌飛行員,給H6K加油(加的是轟炸機特殊燃油),並且按照H6K的特點飛行

pilot Jack; Jack.refuelPlane(H6K); // 加充轟炸機燃油 Jack.flyPlane(H6K); // 轟炸機實彈飛行

給WZ10加油(加的是基類提供的通用燃油),按照WZ10的特點飛行

Jack.refuelPlane(WZ10); // 加充通用型燃油 Jack.flyPlane(WZ10); // 直升機盤旋

上述代碼體現了最經典的所謂多態的場景,給Jack不同的飛機,就能表現不同的結果。虛函數和純虛函數都能做到這一點,區別是,子類如果不提供虛函數的實現,那就會自動調用基類的預設方案。而子類如果不提供純虛函數的實現,則編譯將會失敗。基類提供的純虛函數實現版本,無法通過指向子類對象的基類類型指針或引用來調用,因此不能作為子類相應虛函數的備選方案。下面給出總結。

第一,當基類的某個成員方法,在大多數情形下都應該由子類提供個性化實現,但基類也可以提供一個備選方案的時候,請將其設計為虛函數。例如飛行器的加油動作,每種不同的飛行器原則上都應該有自己的個性化的加充然後的方式,但也不免可以有一種通用的然後和加充方式。

第二,當基類的某個成員方法,必須由子類提供個性化實現的時候,請將其設計為純虛函數。例如飛行器的飛行動作,邏輯上每種飛行器都必須提供為其特殊設計的個性化飛行行為,而不應該有任何一種「通用的飛行方式」。

第三,使用一個基類類型的指針或者引用,來指向子類對象,進而調用經由子類複寫了的個性化的虛函數,這是C++實現多態性的一個最經典的場景

第四,基類提供的純虛函數的實現版本,並非為了多態性考慮,因為指向子類對象的基類指針和引用無法調用該版本。純虛函數在基類中的實現跟多態性無關,它只是提供了一種語法上的便利,在變化多端的應用場景中留有後路

第五,虛函數和普通的函數實際上是存儲在不同的區域的,虛函數所在的區域是可被覆蓋(也稱複寫override)的,每當子類定義相同名稱的虛函數時就將原來基類的版本給覆蓋了,另一側面也說明了為什麼基類中聲明的虛函數在後代類中不需要另加聲明一律自動為虛函數,因為它所存儲的位置不會發生改變。而普通函數的存儲區域不會覆蓋,每個類都有自己獨立的區域互不相干。

最後附一幅草圖以供參考

識別下面二維碼進入 微店●秘籍酷 瞅一眼唄!也許有你喜歡的東西

http://weidian.com/s/260920190 (二維碼自動識別)

原文鏈接:當我們使用虛函數的時候,我們究竟想幹什麼?


需要處理派生自同一基類的不同子類時,調用(子類)各自的成員函數實例。

不用虛函數自然也有辦法實現相同的功能,比如函數重載和模板。但重載和模板的binding是靜態的,本質上是替你把你應該補充的代碼自動生成;而virtual是靜態的,virtual的內部實現過程類似函數指針。

實例說話,假如存在以下類層次關係:

class GF{
public:
void toString(){
cout &<&< "Grandfather class" &<&< endl; } }; class Ft : public GF{ public: void toString(){ cout &<&< "Father class" &<&< endl; } }; class Son :public Ft{ public: void toString(){ cout &<&< "Son class" &<&< endl; } };

存在函數foo(GF *),作用是處理類型為GF*的實例(instance):

void foo(GF * who){
who-&>toString();
}

實例化Ft類和Son類:

Ft * father = new Ft();
Son * son = new Son();

//以上聲明也可以為

GF * father = new Ft();
GF * son = new Son();

當我們調用:

foo(son);

foo(father);

自然地,我們希望的運行結果為:

Son class

Father class

然而以上代碼的運行結果為:

Grandfather class

Grandfather class

可見,在foo(GF * who) 函數中,who-&>toString()指向的始終是

GF::toString()

即是說,在調用foo(son)時,son 指針被隱式提升為了GF * 類型。

另一種聲明:

GF * son = new Son();

雖然調用的是Son 的構造函數,但是son的類型同樣被提升為了GF*。因此,調用foo(GF * who)時,其運行表現同上。

此時,為了達到目的,我們可以:

  1. 重載foo(),定義Ft*和Son*的版本。顯然,在GF-Ft-Son的繼承鏈上再派生出新的子類時,需要增加foo()的重載版本。

  2. 引入virtual關鍵字,將基類的定義改為:

class GF{
public:
virtual void toString(){
cout &<&< "Grandfather class" &<&< endl; } };

這樣不需要修改foo()函數,即只需保證foo(GF*)的參數類型為祖先類即可保證調用的函數是實例所指向的類中的版本。

一個類函數的調用並不是在編譯時刻被確定的,而是在運行時刻被確定的。由於編寫代碼的時候並不能確定被調用的是基類的函數還是哪個派生類的函數,所以被成為「虛」函數。

推薦 @陳皓 前輩的博客。


C++學習之深入理解虛函數--虛函數表解析


類型擦除,分離介面,多態,好像沒其他的用處了。


個人覺得c++的虛函數設計的太過於靈活了,很容易出錯

其實俺用虛函數最主要的也都是定義純虛函數的基類,用來定義一個公共的介面,類似於Java 的interface那種用法


多態,介面

有人說用函數指針,我想說並不是每個人都是林納斯,能把C寫出面向對象的感覺,還能很少出bug。


就是結了婚以後的函數啊


推薦閱讀:

為什麼學習編程一定要多寫多敲代碼?
程序分析中的 {path,context,flow}-sensitive 問題?
Lua 語言的靈活、高擴展性優點體現在哪裡?
計算機科學與技術(CS)專業學生該如何系統地自學?
為什麼asp.net沒能比php更流行?

TAG:編程語言 | 編程 | 計算機科學 | C | 虛函數C |