C語言也有封裝,繼承和多態(tài)你知道嗎_第1頁
C語言也有封裝,繼承和多態(tài)你知道嗎_第2頁
C語言也有封裝,繼承和多態(tài)你知道嗎_第3頁
C語言也有封裝,繼承和多態(tài)你知道嗎_第4頁
C語言也有封裝,繼承和多態(tài)你知道嗎_第5頁
已閱讀5頁,還剩3頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

第C語言也有封裝,繼承和多態(tài)你知道嗎目錄封裝繼承多態(tài)函數(shù)的指針通過函數(shù)的指針實(shí)現(xiàn)多態(tài)總結(jié)我們知道封裝、繼承、多態(tài)是面向?qū)ο蟮娜筇匦?,我們也知道C語言是面向過程的語言,那么可不可以在面向過程的語言中用面向?qū)ο蟮乃枷刖幊棠亍,F(xiàn)在我們就一起看看用C語言如何實(shí)現(xiàn)封裝、繼承、多態(tài)。

封裝

所謂封裝就是把實(shí)現(xiàn)的細(xì)節(jié)隱藏起來,外部只能通過相關(guān)的函數(shù)對一個(gè)類進(jìn)行操作,一呢是方便代碼的復(fù)用,二也可以有效的保證代碼的安全性。那么我們看看Redis源碼中對于雙向鏈表的一個(gè)設(shè)計(jì)和實(shí)現(xiàn),是不是就是傳說中的封裝呢?

typedefstructlistNode{

structlistNode*prev;

structlistNode*next;

void*value;

}listNode;

list*listCreate(){

structlist*list;

list=malloc(sizeof(structlist));

if(list==NULL)returnNULL;

list-head=list-tail=NULL;

list-len=0;

returnlist;

繼承

繼承也是為了代碼的重用設(shè)計(jì)的,比如多個(gè)子類都有一些共同的屬性和方法,那么就可以將這些共同點(diǎn)抽象成一個(gè)父類,讓子類去繼承他,子類也就擁有了父類的特性,更好的實(shí)現(xiàn)了代碼的重用性。但是繼承也有很多缺點(diǎn),比如:

1.java中不能多繼承

2.如果繼承了一個(gè)類,那么就繼承了這個(gè)類的所有public的屬性和方法,即使你不想繼承

3.如果有一天父類的邏輯做了修改,那么子類的邏輯也被迫做了修改

基于這些原因呢,很多時(shí)候是不建議使用繼承的,而是優(yōu)先用組合的方式來達(dá)到代碼的復(fù)用。在Go語言中也沒有extends關(guān)鍵字,也是通過組合實(shí)現(xiàn)代碼的復(fù)用。那么在C語言中,雖然沒有繼承,但是我們可以組合啊,實(shí)現(xiàn)的效果是大同小異的。例如:

structShape{

intarea;

Shape*crateShape();

structRectangle{

structShapeshape;

intwidth;

intheight;

structSquare{

structShapeshape;

intside;

多態(tài)

函數(shù)的指針

這里要回顧一下C語言基礎(chǔ)語法了,先來看看C語言中關(guān)于函數(shù)的指針。如果我們在C語言中定義了一個(gè)函數(shù),那么在編譯的時(shí)候會(huì)把函數(shù)的源代碼編譯成可執(zhí)行的的指令,然后分配一塊內(nèi)存去存放。這段空間的的地址就是函數(shù)的入口地址,每次調(diào)用的時(shí)候會(huì)從該地址入口開始執(zhí)行,函數(shù)名就代表了這個(gè)地址,因此函數(shù)名就是函數(shù)的指針。

那么我們就可以定義一個(gè)用來指向函數(shù)的指針變量,用來存放某一函數(shù)的入口地址。例如:int(*add)(int,int)。這行代碼中第一個(gè)int表示的是返回值類型;(*add)表示add是一個(gè)指針變量,括號不能省略;后面的(int,int)表示參數(shù)類型是兩個(gè)int類型,括號不能省略,括號表示指針變量不是指向其他類型的,而是函數(shù)類型的。之前對函數(shù)的調(diào)用都是通過函數(shù)名,那么現(xiàn)在有了指針變量,我們也可以通過指針變量來對函數(shù)進(jìn)行調(diào)用。

intmain(){

//通過函數(shù)名調(diào)用

max(9,2);

//通過函數(shù)的指針變量調(diào)用

int(*p)(int,int);

//將函數(shù)max的入口地址賦值給指針變量p

p=max();

(*p)(a,b)

intadd(intx,inty){

returnx+y;

通過函數(shù)的指針實(shí)現(xiàn)多態(tài)

我們看下面代碼,ShapeVtbl這個(gè)結(jié)構(gòu)體中定義了一個(gè)計(jì)算面積的函數(shù),沒有實(shí)現(xiàn),我們叫做虛函數(shù)表。Shape這個(gè)結(jié)構(gòu)體中定義了兩個(gè)屬性,一個(gè)vtbl,一個(gè)area。

structShape{

structShapeVtbl*vtbl;

intarea;

structShapeVtbl{

float(*area)(structShape*self);

structRectangle{

structShapeshape;

intwidth;

intheight;

structSquare{

structShapeshape;

intside;

這里我們分別定義了計(jì)算矩形面積的方法rectangle_area和計(jì)算正方形面積的方法square_area。也初始化了兩個(gè)ShapeVtbl,讓他們的函數(shù)指針分別指向不同的函數(shù)入口。那么在實(shí)際運(yùn)行的時(shí)候代碼就會(huì)根據(jù)我們的選擇調(diào)用不同的函數(shù),呈現(xiàn)出多態(tài)的效果。

//計(jì)算矩形面積的方法

floatrectangle_area(structShape*self){

structRectangle*r=(structRectangle*)self;

self-area=r-width*r-height;

returnself-area;

//計(jì)算正方形面積的方法

floatsquare_area(structShape*self){

structSquare*r=(structSquare*)self;

self-area=r-side*r-side;

returnself-area;

//初始化了兩個(gè)ShapeVtbl,讓area函數(shù)分別指向了rectangle_area、square_area

structShapeVtblvtbl1={

rectangle_area,

structShapeVtblvtbl2={

square_area,

//計(jì)算面積的方法,這里的area函數(shù)的邏輯是在運(yùn)行時(shí)期動(dòng)態(tài)綁定的,也就是有self中函數(shù)指針指向的實(shí)際函數(shù)決定的

floatshape_area(structShape*self){

structShapeVtbl*v=self-vtbl;

returnv-area(self);

structSquare*createSquare(){

structSquare*s=malloc(sizeof(structSquare));

s-side=5;

s-shape.vtbl=vtbl2;

intmain(){

structSquare*s=createSquare();

printf("area=%f\n",shape_area((structShape*)s));

更多可以參考圖例:

這樣的設(shè)計(jì)在redis源碼中有很多應(yīng)用,比如redis中的字典,dict結(jié)構(gòu)體定義了字典的基本屬性以及屬于dict的一些特定函數(shù),代碼在dict.h中。

/*

*字典類型特定函數(shù),定義了計(jì)算哈希值、復(fù)制鍵、比較鍵等函數(shù)

typedefstructdictType{

unsignedint(*hashFunction)(constvoid*key);

void*(*keyDup)(void*privdata,constvoid*key);

void*(*valDup)(void*privdata,constvoid*obj);

int(*keyCompare)(void*privdata,constvoid*key1,constvoid*key2);

void(*keyDestructor)(void*privdata,void*key);

void(*valDestructor)(void*privdata,void*obj);

}dictType;

*字典

typedefstructdict{

//類型特定函數(shù)

dictType*type;

void*privdata;

dicththt[2];

intrehashidx;

intiterators;

}dict;

而在redis中,dict的應(yīng)用比較多,鍵的類型也可能有sds、redisObject等多種類型,他們的鍵比較函數(shù),hash函數(shù)等都是不同的,因此有了下面的代碼,分別定義了適應(yīng)于各種鍵的對比、hash等函數(shù),并封裝在了不同的dictType,代碼在redis.c中。那么在實(shí)際應(yīng)用中,只需要為不同的類型選擇不同的dictType即可。

dictTypeclusterNodesBlackListDictType={

dictSdsCaseHash,/*hashfunction*/

NULL,/*keydup*/

NULL,/*valdup*/

dictSdsKeyCaseCompare,/*keycompare*/

dictSdsDestructor,/*keydestructor*/

NULL/*valdestructor*/

/*Migratecachedicttype.*/

dictTypemigrateCacheDictType={

dictSdsHash,/*hashfunction*/

NULL,/*keydup*/

NULL,/*valdup*/

dictSdsKeyCompare,/*keycompare*/

dictSdsDestructor,/*keydestructor*/

NULL/*valdestructor*/

/*Replicationcachedscriptdict(server.repl_scriptcache_dict).

*KeysaresdsSHA1strings,whilevaluesarenotusedatallinthecurrent

*implementation.*/

dictTypereplScriptCacheDictType={

dictSdsCaseHash,/*hashfunction*/

NULL,/*keydup*/

NULL,/*valdup*/

dictSdsKeyCaseCompare,/*keycompare*/

dictSdsDestructor,/*keydestructor*/

NULL/*valdestructor*/

intdictSdsKeyCompare(void*privdat

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(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)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論