C與面向?qū)ο骔ord版_第1頁
C與面向?qū)ο骔ord版_第2頁
C與面向?qū)ο骔ord版_第3頁
C與面向?qū)ο骔ord版_第4頁
C與面向?qū)ο骔ord版_第5頁
已閱讀5頁,還剩8頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、傳播優(yōu)秀Word版文檔 ,希望對您有幫助,可雙擊去除!C與面向?qū)ο? 面向?qū)ο蟮幕咎匦悦嫦驅(qū)ο蟮娜齻€基本特征是:封裝、繼承、多態(tài)。圖1 面向?qū)ο蠡咎匦?.1 封裝封裝最好理解了。封裝是面向?qū)ο蟮奶卣髦唬菍ο蠛皖惛拍畹闹饕匦?。封裝,也就是把客觀事物封裝成抽象的類,并且類可以把自己的數(shù)據(jù)和方法只讓可信的類或者對象操作,對不可信的進(jìn)行信息隱藏。1.2 繼承面向?qū)ο缶幊?(OOP) 語言的一個主要功能就是“繼承”。繼承是指這樣一種能力:它可以使用現(xiàn)有類的所有功能,并在無需重新編寫原來的類的情況下對這些功能進(jìn)行擴(kuò)展。通過繼承創(chuàng)建的新類稱為“子類”或“派生類”。被繼承的類稱為“基類”、“父類”或

2、“超類”。繼承的過程,就是從一般到特殊的過程。要實(shí)現(xiàn)繼承,可以通過“繼承”(Inheritance)和“組合”(Composition)來實(shí)現(xiàn)。在某些 OOP 語言中,一個子類可以繼承多個基類。但是一般情況下,一個子類只能有一個基類,要實(shí)現(xiàn)多重繼承,可以通過多級繼承來實(shí)現(xiàn)。 繼承概念的實(shí)現(xiàn)方式有三類:實(shí)現(xiàn)繼承、接口繼承和可視繼承。傳播優(yōu)秀Word版文檔 ,希望對您有幫助,可雙擊去除!l 實(shí)現(xiàn)繼承是指使用基類的屬性和方法而無需額外編碼的能力;l 接口繼承是指僅使用屬性和方法的名稱、但是子類必須提供實(shí)現(xiàn)的能力;l 可視繼承是指子窗體(類)使用基窗體(類)的外觀和實(shí)現(xiàn)代碼的能力。在考慮使用繼承時,有

3、一點(diǎn)需要注意,那就是兩個類之間的關(guān)系應(yīng)該是“屬于”關(guān)系。例如,Employee 是一個人,Manager 也是一個人,因此這兩個類都可以繼承 Person 類。但是 Leg類卻不能繼承 Person 類,因?yàn)橥炔⒉皇且粋€人。抽象類僅定義將由子類創(chuàng)建的一般屬性和方法,創(chuàng)建抽象類時,請使用關(guān)鍵字 Interface 而不是 Class。OO開發(fā)范式大致為:劃分對象抽象類將類組織成為層次化結(jié)構(gòu)(繼承和合成) 用類與實(shí)例進(jìn)行設(shè)計和實(shí)現(xiàn)幾個階段。1.3 多態(tài)多態(tài)性(polymorphisn)是允許你將父對象設(shè)置成為和一個或更多的他的子對象相等的技術(shù),賦值之后,父對象就可以根據(jù)當(dāng)前賦值給它的子對象的特性以

4、不同的方式運(yùn)作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。實(shí)現(xiàn)多態(tài),有二種方式,覆蓋,重載。覆蓋,是指子類重新定義父類的虛函數(shù)的做法。重載,是指允許存在多個同名函數(shù),而這些函數(shù)的參數(shù)表不同(或許參數(shù)個數(shù)不同,或許參數(shù)類型不同,或許兩者都不同)。其實(shí),重載的概念并不屬于“面向?qū)ο缶幊獭?,重載的實(shí)現(xiàn)是:編譯器根據(jù)函數(shù)不同的參數(shù)表,對同名函數(shù)的名稱做修飾,然后這些同名函數(shù)就成了不同的函數(shù)(至少對于編譯器來說是這樣的)。如,有兩個同名函數(shù):function func(p:integer):integer;和function func(p:string):integer;。那么編譯

5、器做過修飾后的函數(shù)名稱可能是這樣的:int_func、str_func。對于這兩個函數(shù)的調(diào)用,在編譯器間就已經(jīng)確定了,是靜態(tài)的(記?。菏庆o態(tài))。也就是說,它們的地址在編譯期就綁定了(早綁定),因此,重載和面向?qū)ο缶幊虩o關(guān)!真正相關(guān)的是“覆蓋”。當(dāng)子類重新定義了父類的虛函數(shù)后,父類指針根據(jù)賦給它的不同的子類指針,動態(tài)(記住:是動態(tài)?。┑恼{(diào)用屬于子類的該函數(shù),這樣的函數(shù)調(diào)用在編譯期間是無法確定的(調(diào)用的子類的虛函數(shù)的地址無法給出)。因此,這樣的函數(shù)地址是在運(yùn)行期綁定的(晚邦定)。結(jié)論就是:重載只是一種語言特性,與多態(tài)無關(guān),與面向?qū)ο笠矡o關(guān)!引用一句Bruce Eckel的話:“不要犯傻,如果它不是

6、晚邦定,它就不是多態(tài)?!蹦敲?,多態(tài)的作用是什么呢?我們知道,封裝可以隱藏實(shí)現(xiàn)細(xì)節(jié),使得代碼模塊化;繼承可以擴(kuò)展已存在的代碼模塊(類);它們的目的都是為了代碼重用。而多態(tài)則是為了實(shí)現(xiàn)另一個目的接口重用!多態(tài)的作用,就是為了類在繼承和派生的時候,保證使用“家譜”中任一類的實(shí)例的某一屬性時的正確調(diào)用。 傳播優(yōu)秀Word版文檔 ,希望對您有幫助,可雙擊去除!1.4 幾個概念泛化(Generalization)圖2 泛化在上圖中,空心的三角表示繼承關(guān)系(類繼承),在UML的術(shù)語中,這種關(guān)系被稱為泛化(Generalization)。Person(人)是基類,Teacher(教師)、Student(學(xué)生)

7、、Guest(來賓)是子類。若在邏輯上B是A的“一種”,并且A的所有功能和屬性對B而言都有意義,則允許B繼承A的功能和屬性。例如,教師是人,Teacher 是Person的“一種”(a kind of )。那么類Teacher可以從類Person派生(繼承)。如果A是基類,B是A的派生類,那么B將繼承A的數(shù)據(jù)和函數(shù)。如果類A和類B毫不相關(guān),不可以為了使B的功能更多些而讓B繼承A的功能和屬性。若在邏輯上B是A的“一種”(a kind of ),則允許B繼承A的功能和屬性。聚合(組合)圖3 組合若在邏輯上A是B的“一部分”(a part of),則不允許B從A派生,而是要用A和其它東西組合出B。例

8、如,眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是頭(Head)的一部分,所以類Head應(yīng)該由類Eye、Nose、Mouth、Ear組合而成,不是派生(繼承)而成。聚合的類型分為無、共享(聚合)、復(fù)合(組合)三類。 聚合(aggregation) 傳播優(yōu)秀Word版文檔 ,希望對您有幫助,可雙擊去除!圖4 共享上面圖中,有一個菱形(空心)表示聚合(aggregation)(聚合類型為共享),聚合的意義表示has-a關(guān)系。聚合是一種相對松散的關(guān)系,聚合類B不需要對被聚合的類A負(fù)責(zé)。 組合(composition)圖5 復(fù)合這幅圖與上面的唯一區(qū)別是菱形為實(shí)心的,它代表了一種更為堅固

9、的關(guān)系組合(composition)(聚合類型為復(fù)合)。組合表示的關(guān)系也是has-a,不過在這里,A的生命期受B控制。即A會隨著B的創(chuàng)建而創(chuàng)建,隨B的消亡而消亡。 依賴(Dependency)圖6 依賴這里B與A的關(guān)系只是一種依賴(Dependency)關(guān)系,這種關(guān)系表明,如果類A被修改,那么類B會受到影響。 2 C實(shí)現(xiàn)繼承 Linux 內(nèi)核鏈表從前面我們已經(jīng)知道,繼承的本意,是為了進(jìn)行代碼重用。對于c+等OO語言而言,class本身就提供了這樣的機(jī)制,但是也必須付出代價:你必須非常仔細(xì)地設(shè)計你的類族譜,要有前瞻性,要有可擴(kuò)展性,要決定分多少個層次.這些都不是容易做到的事。但是對于C語言,這個

10、結(jié)構(gòu)化的語音而言,只有一個struct可用,那么C語言能不能做到代碼重用呢?事實(shí)上,以C這么強(qiáng)大的功能,當(dāng)然是可以做到的,下面我們來學(xué)習(xí)Linux內(nèi)核的鏈表實(shí)現(xiàn),堪稱經(jīng)典。2.1 節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)Linux鏈表是雙向循環(huán)鏈表,因?yàn)檫@樣的鏈表具有最好的靈活性,下面是其節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)定義(內(nèi)核也提供了帶頭結(jié)點(diǎn)的鏈表hlist_head / hlist_node):struct list_head struct list_head *prev;struct list_head *next;傳播優(yōu)秀Word版文檔 ,希望對您有幫助,可雙擊去除!從這個結(jié)構(gòu)體的命名就可以發(fā)現(xiàn),內(nèi)核實(shí)際上并沒有為鏈表固定表頭,

11、每一個節(jié)點(diǎn)都可以看做表頭,事實(shí)上是一個雙向循環(huán)的結(jié)構(gòu)。當(dāng)然,單獨(dú)一個這樣的結(jié)構(gòu)并沒有什么意義,我們需要把它放到我們自己的結(jié)構(gòu)體中:struct my_struct struct list_headlist;int cat;void *dog;2.2 鏈表初始化使用之前,需要進(jìn)行初始化:Struct my_struct *p = malloc p-cat = p-dog = INIT_LIST_HEAD(&p-list)初始化用到的相關(guān)宏如下:#define INIT_LIST_HEAD(ptr) do (ptr)-next = (ptr); (ptr)-prev = (ptr); while

12、(0)另外,內(nèi)核也提供了另外的初始化宏:#define LIST_HEAD_INIT(name) &(name), &(name) #define LIST_HEAD(name) /即將前驅(qū)與后繼都指向了自己struct list_head name = LIST_HEAD_INIT(name)這種初始化,一般用于編譯時靜態(tài)聲明與初始化鏈表:struct my_structmine.list = LIST_HEAD(mine.list);.cat = 0;.dog = NULL;2.3 操作鏈表內(nèi)核提供操作鏈表的函數(shù),一般是static inline 類型的函數(shù)值得注意的是,從鏈表中刪除節(jié)點(diǎn),

13、只是將節(jié)點(diǎn)摘除,內(nèi)存需要另外用代碼進(jìn)行釋放2.4 遍歷鏈表操作鏈表雖然重要,但是也要能夠找到待操作的節(jié)點(diǎn)才可以,因此鏈表的遍歷才是關(guān)鍵之處。不僅要遍歷鏈表節(jié)點(diǎn),并且還要取出該節(jié)點(diǎn)對應(yīng)的自己的用戶數(shù)據(jù)。下面的代碼是典型的遍歷方法:struct list_head *p;struct my_struct my;list_for_each (p, &my-list)my = list_entry(p, struct my_struct , list);能夠通過任何一個節(jié)點(diǎn)操作這個鏈表其中用到的宏定義如下:#define list_for_each(pos, head) for (pos = (hea

14、d)-next; prefetch(pos-next), pos != (head); pos = pos-next)傳播優(yōu)秀Word版文檔 ,希望對您有幫助,可雙擊去除!#define list_entry(ptr, type, member)container_of(ptr, type, member)#define 這個宏十分重要思路就是,取出ptr,并減去其所在結(jié)構(gòu)體中的偏移量,最后將結(jié)果強(qiáng)轉(zhuǎn)為my_struct(本宏具有普遍性)當(dāng)然還有一個簡單的法子,就是把鏈表的list結(jié)構(gòu)放到my_struct結(jié)構(gòu)的開頭,則只需強(qiáng)轉(zhuǎn)即可(限制較多,我司貌似是這么干的)container_of(pt

15、r, type, member) ( const typeof( (type *)0)-member ) *_mptr = (ptr); (type *)( (char *)_mptr - offsetof(type,member) );)2.5 小結(jié)上面的例子,實(shí)際上是一個可重用性很強(qiáng)的,用C實(shí)現(xiàn)繼承的例子。我們可以發(fā)現(xiàn),在代碼重用方面,C雖然沒有提供專門的機(jī)制,但我們還是能夠?qū)崿F(xiàn)這一點(diǎn)的,只不過需要我們更好的設(shè)計數(shù)據(jù)結(jié)構(gòu),以及抽象、封裝我們的代碼3 C實(shí)現(xiàn)多態(tài)同樣從第一節(jié)我們可以知道,多態(tài)的目的就是要進(jìn)行接口復(fù)用,C語言同樣有能力實(shí)現(xiàn)多態(tài)。在c+中,多態(tài)一般通過重載、覆蓋、模板等等,那么C

16、語言該如何實(shí)現(xiàn)?3.1 重載前面提到過,c+在編譯的時候,就能夠結(jié)合函數(shù)名、返回值、參數(shù)等在編譯器中生成唯一的符號,即對于同一個可執(zhí)行文件而言,其內(nèi)部的符號還是唯一的。C的編譯器不提供這樣的功能,那么該如何進(jìn)行模擬?3.1.1 方法一:用可變參數(shù)這個的思路就是通過va_arg()、va_start()、va_end()等宏,結(jié)合va_list結(jié)構(gòu)體進(jìn)行函數(shù)的設(shè)計。這樣就可以向函數(shù)傳入不定個數(shù)的參數(shù)但是這個方法有一個問題,那就是對于0個參數(shù)的情況,可變參數(shù)不能支持3.1.2 方法二:用void *這里的處理思路也很簡單,通過void *這個 萬能指針傳遞不同類型的變量,只要在處理函數(shù)內(nèi)部進(jìn)行強(qiáng)制

17、轉(zhuǎn)換即可當(dāng)然這個方法也是有代價的,必須仔細(xì)進(jìn)行設(shè)計,因?yàn)槿绾卧诟鞣N指針類型之間進(jìn)行轉(zhuǎn)換,有時候?qū)嵲谑且粋€麻煩的事情。另外對于參數(shù)個數(shù)也會有限制舉例1:void HandleMsg(unsinged int id, void *p) Msg1 *p1; Msg2 *p2; switch(id) case key1: p1 = (Msg1*)p; /do something break; 傳播優(yōu)秀Word版文檔 ,希望對您有幫助,可雙擊去除! case key2: p2 = (Msg2*)p; /do something break; default: break; 舉例2:void * memc

18、py(void *dest, const void *src, size_t len)3.1.3 方法三:綜合這里可以考慮入?yún)魅胍粋€結(jié)構(gòu)體:struct my_structmineunsigned numArg; /參數(shù)個數(shù)struct *st_Arg; /參數(shù)鏈表,每個節(jié)點(diǎn)再分別定義參數(shù)類型,與取值3.2 覆蓋一般而言,覆蓋的實(shí)現(xiàn),主要是通過函數(shù)指針,通過記錄操作符的方式。最經(jīng)典的例子,莫過于Linux中的VFS層的實(shí)現(xiàn)了,下面簡單介紹一下3.2.1 Linux VFS的原理與層次圖VFS在底層的各種文件系統(tǒng)之上,建立了一層抽象層,對上屏蔽了各種文件系統(tǒng) 的差異。這么一來,使得Linux能

19、夠支持多種文件系統(tǒng),即使不同的文件系統(tǒng)在功能和行為上有很大的差異。VFS實(shí)際上是提供了一個模型,該模型定義了各種文件系統(tǒng)實(shí)際上應(yīng)該支持的各種功能,比如“打開文件”、“文件讀寫”等等。與此同時,各種文件系統(tǒng)也必須按照VFS的規(guī)定,來實(shí)現(xiàn)各種接口所對應(yīng)的功能,提供VFS所期望的接口與數(shù)據(jù)結(jié)構(gòu),這樣VFS就能夠毫不費(fèi)力的進(jìn)行文件系統(tǒng)的管理了傳播優(yōu)秀Word版文檔 ,希望對您有幫助,可雙擊去除!3.2.2 Linux VFS的面向?qū)ο笤O(shè)計VFS主要有四個對象類型:超級快對象:代表一個已經(jīng)安裝的文件系統(tǒng);索引節(jié)點(diǎn)對象:代表一個文件;目錄項(xiàng)對象:路徑的一個組成部分;文件對象:代表有進(jìn)程打開的文件。每個對象

20、不僅定義了自己的屬性,關(guān)鍵的是,都會有一個操作符結(jié)構(gòu)體,里面定義了不同的對象需要具備的方法:這些方法由不同的文件系統(tǒng)對其進(jìn)行賦值,即不同的文件系統(tǒng)負(fù)責(zé)給其提供具體的實(shí)現(xiàn)。下圖是inode對象的操作符也就是所謂的 函數(shù)列表,在里面可以看到我們很多熟悉的操作:開源的dhcpd,其對universe的處理,事實(shí)上也是同樣的設(shè)計思路傳播優(yōu)秀Word版文檔 ,希望對您有幫助,可雙擊去除!3.2.3 小結(jié)從上面可以看出,文件系統(tǒng)的操作符的實(shí)現(xiàn),實(shí)際上就是函數(shù)指針的運(yùn)用,不同的對象向上層的抽象管理層提供各自的處理函數(shù),使得抽象層能夠屏蔽掉下面的實(shí)現(xiàn)細(xì)節(jié)這樣處理的實(shí)質(zhì),就是接口復(fù)用3.3 模板一般而言,覆蓋的

21、C實(shí)現(xiàn),主要是運(yùn)用好預(yù)處理中的 # 符號,通過該符號,將name作為宏的參數(shù)傳入,即可在編譯階段生成新的類,并具有各自的處理函數(shù)。當(dāng)然類型也需要傳入具體細(xì)節(jié)可以參見后面的示例4 例子4.1 運(yùn)行時多態(tài)#ifndef C_Class #define C_Class struct定義自己的關(guān)鍵字,親切 #endifC_Class A C_Class A *A_this; void (*Foo)(C_Class A *A_this); int a; int b; ;C_Class B /B繼承了A C_Class B *B_this; /順序很重要 void (*Foo)(C_Class B *Bt

22、his); /虛函數(shù) int a; int b; int c; ; void B_F2(C_Class B *Bthis) printf(It is B_Funn); void A_Foo(C_Class A *Athis) printf(It is A.a=%dn,Athis-a);/或者這里void B_Foo(C_Class B *Bthis) printf(It is B.c=%dn,Bthis-c); void A_Creat(struct A* p) p-Foo=A_Foo; p-a=1; p-b=2; p-A_this=p; void B_Creat(struct B* p) p

23、-Foo=B_Foo; p-a=11; p-b=12; p-c=13; p-B_this=p;傳播優(yōu)秀Word版文檔 ,希望對您有幫助,可雙擊去除!int main(int argc, char* argv) C_Class A *ma,a; C_Class B *mb,b; A_Creat(&a);/實(shí)例化 B_Creat(&b); mb=&b; ma=&a; ma=(C_Class A*)mb; /引入多態(tài)指針 printf(%dn,ma-a); /可惜的就是 函數(shù)變量沒有private ma-Foo(ma); /多態(tài) a.Foo(&a); /不是多態(tài)了 B_F2(&b); /成員函數(shù),因

24、為效率問題不使用函數(shù)指針 return 0; 4.2 純虛類/-結(jié)構(gòu)體中的函數(shù)指針類似于聲明子類中必須實(shí)現(xiàn)的虛函數(shù)- typedef struct void (*Foo1)(); char (*Foo2)(); char* (*Foo3)(char* st); MyVirtualInterface; /-類似于純虛類的定義- MyVirtualInterface* m_pInterface; DoMyAct_SetInterface(MyVirtualInterface* pInterface) m_pInterface = pInterface; void DoMyAct_Do() if(m

25、_pInterface = NULL) return; m_pInterface-Foo1(); c = m_pInterface-Foo2(); /-子類一-MyVirtualInterface stMAX;/接著定義一些需要實(shí)現(xiàn)的函數(shù) Act1_Foo1,Act1_Foo2,Act1_Foo3MyVirtualInterface* Act1_CreatInterface() index = FindValid() /對象池或者使用Malloc!應(yīng)該留在外面申請,實(shí)例化 if(index = -1) return NULL; stindex.Foo1 = Act1_Foo1; / Act1_Foo1要在下面具體實(shí)現(xiàn) stindex.Foo2 = Act1_Foo2; stindex.Foo3 = Act1_Foo3; Return &stindex; /-主函數(shù)- if(p = Act1_CreatInterface() != NULL) List_AddObject(&List, p); /Add All While(p = List_GetObject() DoMyAct_SetInterface(p);

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論