適合具備 C 語言基礎(chǔ)的 C 教程(九)
[導(dǎo)讀]本文將敘述 C 的另一個(gè)內(nèi)容,也就是抽象,這也是 C 相對(duì)于 C語言來說獨(dú)特的一點(diǎn)。
前言
在上一則教程中,敘述了關(guān)于C 類型轉(zhuǎn)換的相關(guān)內(nèi)容,在本節(jié)教程中,將敘述?C 的另一個(gè)內(nèi)容,也就是抽象,這也是?C 相對(duì)于?C語言來說獨(dú)特的一點(diǎn),下面我們就來著重?cái)⑹鲞@一點(diǎn)。純虛函數(shù)
在介紹抽象類之前,需要弄明白何為純虛函數(shù),下面假定我們有這樣一個(gè)需求:做一個(gè)“各個(gè)國家的人的調(diào)查”,調(diào)查各個(gè)國家的人的:飲食、穿衣、開車要完成這樣一個(gè)事情,那我們現(xiàn)在就需要實(shí)現(xiàn)這樣幾個(gè)類,一個(gè)是?
Human類,其他國家的人從?Human類里派生而來,就比如說是Chinese和Englishman,我們?cè)倩剡^頭來想,我們所要實(shí)現(xiàn)的需求是調(diào)查各個(gè)國家的人,那么這個(gè)過程中,由Human類派生得到?Chinese和?Englishman,那么在實(shí)例化對(duì)象的時(shí)候,我們實(shí)際上是不會(huì)用到Human類去定義一個(gè)對(duì)象的,考慮到這層因素,我們?cè)?Human類里使用到了純虛函數(shù)的概念,類實(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í)際上,抽象類就是具有純虛函數(shù)的類,那這抽象類存在的意義是什么呢?總的來說,其作用也就是:向下定義好框架,向上提供統(tǒng)一的接口,其不能夠?qū)嵗瘜?duì)象,基于上述幾個(gè)類的前提下,我們編寫主函數(shù)的代碼:int?main(int?argc,char?**argv)
{
????Human?h;
????return?0;
}
因?yàn)槌橄箢惒荒軌驅(qū)嵗瘜?duì)象,所以上述代碼編譯結(jié)果是錯(cuò)誤的,錯(cuò)誤信息如下所示:int?main(int?argc,?char**?argv)
{
????Englishman?e;
????Chinese????g;
????return?0;
}
另外需要注意的是:在派生抽象類的過程中,如果派生得到的子類沒有覆寫所有的純虛函數(shù),那么這個(gè)子類還是抽象類,比如有如下所示的代碼,Human類沿用的是上述的寫法,代碼不變,如果我們將上述的?Chinese類進(jìn)行更改,更改后的代碼如下所示: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ù)進(jìn)行覆寫,那么當(dāng)前這個(gè)Chinese類也是一個(gè)抽象類,也是不能夠進(jìn)行實(shí)例化對(duì)象的,要使得?Chinese類有作用,我們必須派生出來另一個(gè)類,代碼如下所示:class?Guangximan?:?public?Chinese?
{
????void?driving(void)?{cout<<"drive?guangxi?car"<<endl;?}
};
這個(gè)時(shí)候,就可以用?Guangximan這個(gè)類來實(shí)例化對(duì)象了。多文件編程
在前面的教程中,有一則教程說到了多文件編程,在?C 中也就是將類的聲明放到頭文件中,將類的實(shí)現(xiàn)放在.cpp文件中,為了更好地闡述這種方法,我們用實(shí)例來進(jìn)行講解,首先,來看一下,所涉及到地所有文件有哪些:6個(gè)文件,我們首先來看?Chinese.h這個(gè)文件,代碼如下所示:#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ù)的一個(gè)聲明,并沒有成員函數(shù)的實(shí)現(xiàn),我們繼續(xù)來看Chinese.cpp的類實(shí)現(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ù)來實(shí)現(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é)果如下所示:Chinese和Englishman都有名字,那么就可以增添設(shè)置名字和獲取名字這兩種方法,首先是?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ù)實(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;
}
以這樣的方式增添功能,確實(shí)是可行的,但是我們假設(shè)一下,如果類很多,除了中國人和英國人還有很多個(gè)國家的人,如果這些類都要增加相同的功能,這個(gè)工作量就比較大了,那要如何解決這個(gè)問題呢?這個(gè)時(shí)候,我們就可以引入一個(gè)新類Human,然后,將每個(gè)類相同的部分寫在這個(gè)類里面,其他類,諸如Englisnman和Chinese就可以從Human類中繼承而來,那這個(gè)時(shí)候,增添的操作,就只需要在?Human類中增加就好了,不需要改動(dòng)Chinese和Englishman,工作量就小了很多。我們來看?Human類的代碼實(shí)現(xiàn),首先是.h代碼的實(shí)現(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代碼的實(shí)現(xiàn):#include?"Human.h"
void?Human::setName(char?*name)?
{
????this->name?=?name;
}
char?*Human::getName(void)?
{
????return?this->name;
}
有了?Human類之后,我們就可以來實(shí)現(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類,這個(gè)時(shí)候,就不需要再自己實(shí)現(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]?=?{ 




