C++中抽象類、虛擬函式和純虛擬函式

linlinlinxi007發表於2010-01-28

abstract class是抽象類,至少包含一個純虛擬函式的類就叫做抽象類。

但是如果一個類,所有的成員都是純虛擬函式,那麼它和一般的抽象類在用法上是有區別的。至少Microsoft給的COM介面定義全部都是僅由純虛擬函式構成的類。因此把這樣的類定義叫做純虛類也不算錯。

純虛擬函式和虛擬函式的區別在於前者不包含定義,而後者包含函式體。

那麼純虛類就是不包含任何實現(包括成員函式定義和成員變數定義。前者代表演算法,後者代表結構)。不包含任何演算法和結構的類叫做純虛類,應該沒有問題。


justforfun626說我confused with the name of abstract class,那是因為在Java裡面的確沒有純虛類的概念,因為Java裡沒有純虛擬函式這個概念。Java管虛擬函式叫做abstract function,管抽象類叫做abstract class,直接說來,Java根本沒有virtual這個關鍵字,都用abstract代替,因此Java裡面根本就沒有Pure這個概念。有那就是interface。在interface裡面定義的函式都不能有函式體,這個在Java裡面叫做介面。那麼C++裡面與interface等同的概念就是純虛類了,C++用純虛類來模擬interface這個抽象概念,因此這裡說的“純虛類”與Java的abstract class不同,與C++的一般抽象類也不同。“純虛類”與C++一般抽象類的區別就好比Java裡面interface 和 abstract class的區別。

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

//今天深刻理解了虛擬函式和純虛擬函式的作用和用法
//如果子類實現了虛擬函式則呼叫子類的方法,
//如果子類沒有實現該函式則呼叫父類的方法。
//究竟怎麼用呢:
假如CChild 派生自 CMan ,有個虛擬函式 Eat();

CMan m_man;
CChild m_child;
//這才是使用的精髓,如果不定義基類的指標去使用,沒有太大的意義
CMan *p ;
p = &m_man ;
p->Eat(); //始終呼叫CMan的Eat成員函式,不會呼叫 CChild 的
p = &m_child;
p->Eat(); //如果子類實現了該方法,則始終呼叫CChild的Eat函式
//不會呼叫CMan 的 Eat 方法 如果子類沒有實現該函式
//則呼叫CMan的Eat函式
//呵呵,就這些了,還得用心體會
//純虛擬函式就是基類只定義了函式體,沒有實現過程 定義方法如下
// virtual void Eat() = 0; 直接=0 不要 在cpp中定義就可以了
//純虛擬函式相當於介面,不能直接例項話,需要派生類來實現函式定義
//有的人可能在想,定義這些有什麼用啊 ,我覺得很有用
//比如 你想描述一些事物的屬性給別人,而自己不想去實現,就可以定
//義為純虛擬函式。說的再透徹一些。比如蓋樓房,你是老闆,你給建築公司
//描述清楚你的樓房的特性,多少層,樓頂要有個花園什麼的
//建築公司就可以按照你的方法去實現了,如果你不說清楚這些,可能建築
//公司不太瞭解你需要樓房的特性。用純需函式就可以很好的分工合作了
//你覺得有道理嗎?

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

2、抽象類:

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

⑴抽象類的定義:

稱帶有純虛擬函式的類為抽象類。

⑵抽象類的作用:

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

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

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

•   抽象類是不能定義物件的。

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

(原名)我對C++中虛擬函式、純虛擬函式在實現多型中作用的一點淺薄認識

mahongxi(原作)


多型是物件導向程式設計和麵向過程程式設計的主要區別之一,何謂多型?記得在CSDN裡一篇論C++多型的文章裡有一名話:“龍生九子,子子不同”多型就是同一個處理手段可以用來處理多種不同的情況,在錢能老師的《C++程式設計教程》書中有這樣一個例子:
定義了一個小學生類
[本文全部程式碼均用偽碼]
class Student
{public:
Student(){}
~Student(){}
void 交學費(){}
//......
};
裡面有一個 “交學費”的處理函式,因為大學生和小學生一些情況類似,我們從小學生類中派生出大學生類:
class AcadStudent:public Student
{public:
AcadStudent(){}
~ AcadStudent(){}
void 交學費(){}

//.......
};

我們知道,中學生交費和大學生交費情況是不同的,所以雖然我們在大學生中繼承了中學生的"交學費"操作,但我們不用,把它過載,定義大學生自己的交學費操作,這樣當我們定義了一個小學生,一個大學生後:

Student A;
AcadStudent B;

A.交學費(); 即呼叫小學生的,B.交學費();是呼叫大學生的,功能是實現了,但是你要意識到,可能情況不僅這兩種,可能N種如:小學生、初中生、高中生、研究生.....
它們都可以以Student[小學生類]為基類。

如果系統要求你在一群這樣的學生中,隨便抽出一位交納學費,你怎麼做?

:
//A為抽出來的要交學費的同學
{switch(typeof(A))
{case 小學生:A.小學生:: 交學費 ();break;
case 初中生:A.初學生:: 交學費 ();break;
case 高中生:A.高學生:: 交學費 ();break;

default:
.............
}
}

首先,我們要在每個類中定義一個 typeof()用來識別它的型別,然後還要在程式中進行區別,這樣一來,雖然也行,但是,如果再增加型別則要改動switch,又走了程式導向的老路,而且想通過一個模組進行操作實現起來也有難度。
所以C++中提供了多型,即能通過遲後聯編的技術實現動態的區分。
在基類的"交學費"前加個Virtual 用來告訴系統,遇到這個處理過程要等到執行時再確定到底呼叫哪個類的處理過程。這樣一來就可以:

void 通用的交學費操作 (Student &A)
{A.交學費();
}

一下全部搞定,你再加新的型別我也不怕!!![具體的實現原理參考:《Inside The C++ Object Model》]。如果沒有 virtual這一宣告,那麼,系統在執行前就確定了操作,比如把“大學生”傳給
void 通用的交學費操作 (Student &A)
{A.交學費();
}
,則A.交學費();呼叫的是大學生類中繼承於Student類中的“交學費操作”
所以虛擬函式對多型的實現是必要的。

為什麼會出現純虛擬函式呢?

如果按上面的例子進行程式設計,所有型別都繼承小學生類,我們會發現一此小學生自己特定的東東[比如 void 上美術課();],也都被大學生繼承來了,雖然不影響大學生的操作,但是隨時間的加長,小學生類中自已所特定的東東越來越多,這樣下去,大學生中冗餘的資料就多了,有什麼辦法可以解決????

就是定義基類時定義一個抽象類,如學生類,在學生類中實現一此大家都有的操作
。這個過程就叫分解。
這個類子對純虛擬函式的說明還不夠明顯,換個例子比如:



class 人()
{public :
//......
void 吃()
{人吃飯;
}
//......
char *Name;
int age;
};

class 狗()
{public :
//......
void 吃()
{狗吃屎;
}
//......
char *Name;
int age;
};
人類、狗類有一些相同的東東,如名字,年紀,吃的動作等,有人想到了為了程式碼的重用,讓人類繼承狗類,可是資料的冗餘讓這個想法完蛋了,所以有人又想出可不可以定義一個這樣的類:


這個類界於人類狗類之間,有人類和狗類共有的一些東東,比如年紀,名字,體重什麼的,但它是不存在例項的,它的存在意義也是隻是為了派生其它類,如人類、狗類,這樣可以使系統清淅、。。。。。、、反正好處多多。

在這個世界上根本不存在界於人狗之間的東東,所以這個“人狗之間類”中的“吃”也是沒什麼意義,你也很難為它的內容下個定義,況且也沒必要,所以定義它為純虛擬函式,形式為:virtual void 吃()=0; 用來告訴系統:
1、這個函式可以沒有函式定義;
2、擁有本函式的類是抽象類;
你可能會說,即然純虛擬函式沒什麼意義,為什麼還要它,它的作用是什麼呢?
為實現多型作準備!!!

由於抽象類的例項沒意義,所以C++中不允許定義它的例項。(如果定義了這樣的例項A,那麼你呼叫A.吃()怎麼辦?)


當然了,你也可以在基類中,virtual 吃(){}
這樣一來,基類就不是抽象類了,可以有例項,而且對於其它方面都不影響。

但你也要承認這樣的物件是沒有什麼意識的,它的存在只能使你思考上增加負擔,除錯時還要考慮到是不是有這樣類的物件在作怪,所以C++乾脆提供了“虛擬函式”、抽象類,的機制,給我們操作時加了限制也可以認為是保險[不可以有抽象類的物件],試想當程式碼變得非常之龐大時,它的存在是多麼有必要啊!!!

相關文章