前言 在上一則教程中,敘述了關(guān)于
C 類型轉(zhuǎn)換的相關(guān)內(nèi)容,在本節(jié)教程中,將敘述?
C 的另一個內(nèi)容,也就是
抽象 ,這也是?
C 相對于?
C語言來說獨特的一點,下面我們就來著重敘述這一點。
純虛函數(shù) 在介紹
抽象類 之前,需要弄明白何為純虛函數(shù),下面假定我們有這樣一個需求:
做一個“各個國家的人的調(diào)查”,調(diào)查各個國家的人的:飲食、穿衣、開車 要完成這樣一個事情,那我們現(xiàn)在就需要實現(xiàn)這樣幾個類,一個是?
Human類,其他國家的人從?
Human類里派生而來,就比如說是
Chinese和
Englishman,我們再回過頭來想,我們所要實現(xiàn)的需求是調(diào)查
各個國家的人,那么這個過程中,由
Human類派生得到?
Chinese和?
Englishman,那么在實例化對象的時候,我們實際上是不會用到
Human類去定義一個對象的,考慮到這層因素,我們在?
Human類里使用到了純虛函數(shù)的概念,類實現(xiàn)的代碼如下所示:
class ?Human {private : ????int ?a;public : ????/*純虛函數(shù)*/ ????virtual ?void ?eating (void ) ? =?0 ; ????virtual ?void ?wearing (void ) ? =?0 ; ????virtual ?void ?driving (void ) ? =?0 ; };class ?Englishman ?: ?public ?Human {public : ????void ?eating (void ) ?? {?cout <<"use?knife?to?eat" <<endl ;?} ????void ?wearing (void ) ? {cout <<"wear?english?style" <<endl ;?} ????void ?driving (void ) ? {cout <<"drive?english?car" <<endl ;?} };class ?Chinese ?: ?public ?Human? {public : ????void ?eating (void ) ?? {?cout <<"use?chopsticks?to?eat" <<endl ;?} ????void ?wearing (void ) ? {cout <<"wear?chinese?style" <<endl ;?} ????void ?driving (void ) ? {cout <<"drive?chinese?car" <<endl ;?} };我們可以看到在上述的代碼中,
Human類的成員函數(shù)跟前幾講所寫的成員函數(shù)有所不同,而如上述?
Human類的成員函數(shù)這般寫法,也就被稱之為是
純虛函數(shù) 。
抽象類 上述引出了純虛函數(shù)的寫法,那純虛函數(shù)和抽象類之間有什么關(guān)系呢?實際上,
抽象類就是具有純虛函數(shù)的類 ,那這抽象類存在的意義是什么呢?總的來說,其作用也就是:
向下定義好框架,向上提供統(tǒng)一的接口,其不能夠?qū)嵗瘜ο?/strong>,基于上述幾個類的前提下,我們編寫主函數(shù)的代碼:int ?main (int ?argc,char ?**argv) { ????Human?h; ????return ?0 ; }因為抽象類不能夠?qū)嵗瘜ο螅陨鲜龃a編譯結(jié)果是錯誤的,錯誤信息如下所示: 而使用通過抽象類派生得到的派生類實例化對象是可行的,代碼如下所示:int ?main (int ?argc,?char **?argv) { ????Englishman?e; ????Chinese????g; ????return ?0 ; }另外需要注意的是:在派生抽象類的過程中,如果派生得到的子類沒有覆寫所有的純虛函數(shù),那么這個子類還是抽象類,比如有如下所示的代碼,Human類沿用的是上述的寫法,代碼不變,如果我們將上述的?Chinese類進行更改,更改后的代碼如下所示:class ?Chinese ?: ?public ?Human? {public : ????void ?eating (void ) ? {?cout <<"use?chopsticks?to?eat" <<endl ;?} ????void ?wearing (void ) ? {cout <<"wear?chinese?style" <<endl ;?} ????//void?driving(void)?{cout<<"drive?chinese?car"< }; 如上述代碼所示,我們將?driving()函數(shù)注釋掉了,那么也就是說,我們并沒有將抽象類的全部純虛函數(shù)進行覆寫,那么當前這個Chinese類也是一個抽象類,也是不能夠進行實例化對象的,要使得?Chinese類有作用,我們必須派生出來另一個類,代碼如下所示:class ?Guangximan ?: ?public ?Chinese? { ????void ?driving (void ) ? {cout <<"drive?guangxi?car" <<endl ;?} };這個時候,就可以用?Guangximan這個類來實例化對象了。多文件編程 在前面的教程中,有一則教程說到了多文件編程,在?C 中也就是將類的聲明放到頭文件中,將類的實現(xiàn)放在.cpp文件中,為了更好地闡述這種方法,我們用實例來進行講解,首先,來看一下,所涉及到地所有文件有哪些:image-20210222103409774 可以看到上述有6個文件,我們首先來看?Chinese.h這個文件,代碼如下所示:#ifndef ?_CHINESE_H #define ?_CHINESE_H #include ? #include ? #include ? using ?namespace ?std ;class ?Chinese {public : ????void ?eating (void ) ;? ????void ?wearing (void ) ; ????void ?drivering (void ) ; };#endif 通過上述地.h文件可以看出,在這里的Chinese類中,它只涉及到類成員函數(shù)的一個聲明,并沒有成員函數(shù)的實現(xiàn),我們繼續(xù)來看Chinese.cpp的類實現(xiàn):#include ?"Chinese.h" void ?Chinese::eating(void ) { ????cout ?<"use?chopsticks?to?eat" ?<endl ; }void ?Chinese::wearing(void ) { ????cout ?<"wear?chinese?style" ?<endl ; }void ?Chinese::drivering(void ) { ????cout ?<"driver?china?car" ?<endl ; }按照上述這樣一種方法,我們繼續(xù)來實現(xiàn)Englishman類中的代碼,首先是Englishman.h中的代碼,代碼如下所示:#ifndef ?_ENGLISHMAN_H #define ?_ENGLISHMAN_H #include ? #include ? #include ? using ?namespace ?std ;class ?Englishman {public : ????void ?eating (void ) ; ????void ?wearing (void ) ; ????void ?driver (void ) ; };#endif 繼續(xù)看.cpp中的代碼,代碼如下所示:#include ?"Englishman.h" void ?Englishman::eating(void ) { ????cout ?<"use?chopsticks?to?eat" ?<endl ; }void ?Englishman::wearing(void ) { ????cout ?<"wear?chinese?style" ?<endl ; }void ?Englishman::drivering(void ) { ????cout ?<"driver?china?car" ?<endl ; }至此,除了主函數(shù)以外的代碼就編寫完了,我們繼續(xù)來看主函數(shù)的代碼:#include ?"Englishman.h" #include ?"Chinese.h" int ?main (int ?argc,?char ?**argv) { ????Englishman?e; ????Chinese?c; ????e.eating(); ????c.eating(); ????return ?0 ; }在前面的教程中,我們就說過,如果是多文件的話,需要編寫?Makefile文件,Makefile文件代碼如下:Human:?main.o?Chinese.o?Englishman.o?Human.o ????g ?-o?$@ ?$^ %.o?:?%.cpp ????g ?-c?-o?$@ ?$< clean: ????rm?-f?*.o?Human?上述代碼就不再這里贅述了,跟之前教程中的?Makefile基本是一樣的,有了Makefile之后,編譯代碼只需要使用?make命令就行了,編譯結(jié)果如下所示:image-20210222105051169 上述代碼中,如果我們想要增添功能,比如說Chinese和Englishman都有名字,那么就可以增添設置名字和獲取名字這兩種方法,首先是?Chinese的代碼,代碼如下:#ifndef ?_CHINESE_H #define ?_CHINESE_H #include ? #include ? #include ? using ?namespace ?std ;class ?Chinese {private : ????char ?*name;public : ????void ?setName (char ?*name) ; ????char ?*getName (void ) ; ????void ?eating (void ) ; ????void ?wearing (void ) ; ????void ?driving (void ) ; ????~Chinese(); };#endif 然后是.cpp中的代碼:#include ?"Chinese.h" void ?Chinese::setName(char ?*name)? { ????this ->name?=?name; }char ?*Chinese::getName(void )? { ????return ?this ->name; }/*其他成員函數(shù)實現(xiàn)同上,這里省略*/ 寫完了?Chinese的代碼,然后是Englishman中的代碼,首先是Englishman.h中的代碼:#ifndef ?_ENGLISHMAN_H #define ?_ENGLISHMAN_H #include ? #include ? #include ? using ?namespace ?std ;class ?Englishman ?{private : ????char ?*name;public : ????void ?setName (char ?*name) ; ????char ?*getName (void ) ; ????void ?eating (void ) ; ????void ?wearing (void ) ; ????void ?driving (void ) ; ????~Englishman(); };#endif 緊接著,是.cpp中的代碼:#include ?"Englishman.h" void ?Englishman::setName(char ?*name)? { ????this ->name?=?name; }char ?*Englishman::getName(void )? { ????return ?this ->name; }以這樣的方式增添功能,確實是可行的,但是我們假設一下,如果類很多,除了中國人和英國人還有很多個國家的人,如果這些類都要增加相同的功能,這個工作量就比較大了,那要如何解決這個問題呢?這個時候,我們就可以引入一個新類Human,然后,將每個類相同的部分寫在這個類里面,其他類,諸如Englisnman和Chinese就可以從Human類中繼承而來,那這個時候,增添的操作,就只需要在?Human類中增加就好了,不需要改動Chinese和Englishman,工作量就小了很多。我們來看?Human類的代碼實現(xiàn),首先是.h代碼的實現(xiàn):#ifndef ?_HUMAN_H #define ?_HUMAN_H #include ? #include ? #include ? using ?namespace ?std ;class ?Human ?{private : ????char ?*name;public : ????void ?setName (char ?*name) ; ????char ?*getName (void ) ;???? };#endif 然后是.cpp代碼的實現(xiàn):#include ?"Human.h" void ?Human::setName(char ?*name)? { ????this ->name?=?name; }char ?*Human::getName(void )? { ????return ?this ->name; }有了?Human類之后,我們就可以來實現(xiàn)我們所說的?Englishman和Chinese類了,代碼如下所示:#ifndef ?_ENGLISHMAN_H #define ?_ENGLISHMAN_H #include ? #include ? #include ? #include ?"Human.h" using ?namespace ?std ;class ?Englishman ?: ?public ?Human? {public : ????void ?eating (void ) ; ????void ?wearing (void ) ; ????void ?driving (void ) ; ????~Englishman(); };#endif 然后是Chinese的代碼:#ifndef ?_CHINESE_H #define ?_CHINESE_H #include ? #include ? #include ? #include ?"Human.h" using ?namespace ?std ;class ?Chinese ?: ?public ?Human {public : ????void ?eating (void ) ; ????void ?wearing (void ) ; ????void ?driving (void ) ; ????~Chinese(); };#endif 可以看到?Englishman和Chinese都是繼承自Human類,這個時候,就不需要再自己實現(xiàn)setName和getName了。我們繼續(xù)來完善我們的代碼,先從主函數(shù)說起,主函數(shù)代碼如下所示:void ?test_eating (Human?*h) { ????h->eating(); }int ?main (int ?argc,?char ?**argv) { ????Englishman?e; ????Chinese?c; ????Human?*?h[2 ]?=?{