C實現類封裝、繼承、多態


1、  概述

 

C語言是一種面向過程的程序設計語言,而C++是在C語言基礎上衍生來了的面向對象的語言,實際上,很多C++實現的底層是用C語言實現的,如在Visual C++中的Interface其實就是struct,查找Interface的定義,你可以發現有這樣的宏定義:

#ifndef Interface

#define Interface struct

#endif

C++在語言級別上添加了很多新機制(繼承,多態等),而在C語言中,我們也可以使用這樣的機制,前提是我們不得不自己實現。

本文介紹了用C語言實現封裝,繼承和多態的方法。

2、  基本知識

在正式介紹C語言實現封裝,繼承和多態事前,先介紹一下C語言中的幾個概念和語法。

(1)    結構體

在C語言中,常把一個對象用結構體進行封裝,這樣便於對對象進行操作,比如:

strcut Point{

int x;

int y;

};

結構體可以嵌套。因而可以把一個結構體當成另一個結構體的成員,如:

struct Circle {

struct Point point_;

int radius;

};

該結構體與以下定義完全一樣(包括內存布置都一樣):

struct Circle {

int x;

int y;

int radius;

};

(2)    函數指針

函數指針是指針的一種,它指向函數的首地址(函數的函數名即為函數的首地址),可以通過函數指針來調用函數。

如函數:

int func(int a[], int n);

可以這樣聲明函數指針:

int (*pFunc)(int a[], int n);

這樣使用:

pFunc = func;

(*pFunc)(a, n);【或者PFunc(a, n)】

可以用typedef定義一個函數指針類型,如:

typdef int (*FUNC)(int a[], int n)

可以這樣使用:

int cal_a(FUNC fptr, int a[], int n)

{

//實現體

}

(3)    extern與static

extern和static是C語言中的兩個修飾符,extern可用於修飾函數或者變量,表示該變量或者函數在其他文件中進行了定義;static也可用於修飾函數或者變量,表示該函數或者變量只能在該文件中使用。可利用它們對數據或者函數進行隱藏或者限制訪問權限。

3、  封裝

在C語言中,可以用結構+函數指針來模擬類的實現,而用這種結構定義的變量就是對象。

封裝的主要含義是隱藏內部的行為和信息,使用者只用看到對外提供的接口和公開的信息。有兩種方法實現封裝:

(1)    利用C語言語法。在頭文件中聲明,在C文件中真正定義它。

這樣可以隱藏內部信息,因為外部不知道對象所占內存的大小,所以不能靜態的創建該類的對象,只能調用類提供的創建函數才能創建。這種方法的缺陷是不支持繼承,因為子類中得不到任何關於父類的信息。如:

//頭文件:point.h

#ifndef POINT_H

#define POINT_H

struct Point;

typedef struct Point point;

point * new_point(); //newer a point object

void free_point(point *point_);// free the allocated space

#endif

//C文件:point.c

#include”point.h”

strcut Point

{

int x;

int y;

};

point * new_point()

{

point * new_point_ = (point *) malloc(sizeof(point));

return new_point_;

}

void free_point(point *point_)

{

if(point_ == NULL)

return;

free(point_);

}

(2)    把私有數據信息放在一個不透明的priv變量或者結構體中。只有類的實現代碼才知道priv或者結構體的真正定義。如:

#ifndef POINT _H

#define POINT_H

typedef struct Point point;

typedef struct pointPrivate pointPrivate;

strcut Point

{

Struct pointPrivate *pp;

};

int get_x(point *point_);

int get_y(point *point_);

point * new_point(); //newer a point object

void free_point(point *point_);// free the allocated space

#endif

//C文件:point.c

#include”point.h”

struct pointPrivate

{

int x;

int y;

}

int get_x(point *point_)

{

return point_->pp->x;

}

int get_y(point *point_)

{

return point_->pp->y;

}

//others…..

4、  繼承

在C語言中,可以利用“結構在內存中的布局與結構的聲明具有一致的順序”這一事實實現繼承。

比如我們要設計一個作圖工具,其中可能涉及到的對象有Point(點),Circle(圓),由於圓是由點組成的,所有可以看成Circle繼承自Point。另外,Point和Circle都需要空間申請,空間釋放等操作,所有他們有共同的基類Base。

//內存管理類new.h

#ifndef NEW_H

#define NEW_H

void * new (const void * class, ...);

void delete (void * item);

void draw (const void * self);

#endif

//內存管理類的C文件:new.c

#include “new.h”

#include “base.h”

void * new (const void * _base, ...)

{

const struct Base * base = _base;

void * p = calloc(1, base->size);

assert(p);

* (const struct Base **) p = base;

if (base ->ctor)

{

va_list ap;

va_start(ap, _base);

p = base ->ctor(p, &ap);

va_end(ap);

}

return p;

}

void delete (void * self)

{

const struct Base ** cp = self;

if (self && * cp && (* cp) —> dtor)

self = (* cp) —>dtor(self);

free(self);

}

void draw (const void * self)

{

const struct Base * const * cp = self;

assert(self &&* cp && (* cp)->draw);

(* cp) ->draw(self);

}

//基類:base.h

#ifndef BASE_H

#define BASE_H

struct Base

{

size_t size; //類所占空間

void * (* ctor) (void * self, va_list * app); //構造函數

void * (* dtor) (void * self); //析構函數

void (* draw) (const void * self); //作圖

};

#endif

//Point頭文件(對外提供的接口):point.h

#ifndef   POINT_H

#define  POINT_H

extern const void * Point;                /* 使用方法:new (Point, x, y); */

#endif

//Point內部頭文件(外面看不到):point.r

#ifndef POINT_R

#define POINT_R

struct Point

{

const void * base; //繼承,基類指針,放在第一個位置,const是防止修改

int x, y;        //坐標

};

#endif

//Point的C文件:point.c

#include “point.h”

#include “new.h”

#include “point.h”

#include “point.r”

static void * Point_ctor (void * _self, va_list * app)

{

struct Point * self = _self;

self ->x = va_arg(* app, int);

self ->y = va_arg(* app, int);

return self;

}

static void Point_draw (const void * _self)

{

const struct Point * self = _self;

printf(“draw (%d,%d)”, self -> x, self -> y);

}

static const struct Base _Point = {

sizeof(struct Point), Point_ctor, 0, Point_draw

};

const void * Point = & _Point;

//測試程序:main.c

#include “point.h”

#include “new.h”

int main (int argc, char ** argv)

{

void * p = new(Point, 1, 2);

draw(p);

delete(p);

}

同樣,Circle要繼承Point,則可以這樣:

struct Circle

{

const struct Point point; //放在第一位,可表繼承

int radius;

};

5、  多態

可以是用C語言中的萬能指針void* 實現多態,接上面的例子:

//測試main.c

void * p = new(Point, 1, 2);

void * pp = new(Circle, 1, 2);

draw(p); //draw函數實現了多態

draw(pp);

delete(p);

delete(pp);

6、  總結

C語言能夠模擬實現面向對象語言具有的特性,包括:多態,繼承,封裝等,現在很多開源軟件都了用C語言實現了這幾個特性,包括大型開源數據庫系統postgreSQL,可移植的C語言面向對象框架GObject,無線二進制運行環境BREW。采用C語言實現多態,繼承,封裝,能夠讓軟件有更好的可讀性,可擴展性。

7、  參考資料

(1)        《C語言中extern和static用法》:

http://www.cnblogs.com/hishope/archive/2008/08/28/1278822.html

(2)        《三、使用GObject——私有成員和靜態變量》:

http://blog.csdn.net/wormsun/archive/2009/11/25/4874465.aspx

(3)        《技巧:用 C 語言實現程序的多態性》:

http://www.ibm.com/developerworks/cn/linux/l-cn-cpolym/index.html?ca=drs-

(4)        書籍《Object-Oriented Programming With ANSI-C》

8、  代碼下載

本文中的代碼可以在此處下載:代碼下載


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM