C語言下的封裝、繼承與多態

來源:互聯網
上載者:User

     上次課,錢SIR提到,Liux下面也有很多用C實現的物件導向的結構。比較感覺興趣,就在網上查了一些資料,原來C語言類比實現物件導向語言所具有的特性:多態,繼承,封裝,也是一件很簡單的事兒。並且現在很多開源軟體都了用C語言實現了這幾個特性,包括大型開來源資料庫系統postgreSQL,可移植的C語言物件導向架構GObject。

    在自己機器上實踐了下,感歎C語言的靈活與強大!總結一下,以便交流:

 

一、基礎知識


(1)結構體

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

struct Point{  int x;  int y;};
struct Circle { struct Point point_; int radius; }; 

該結構體與以下定義完全一樣(包括記憶體布置都一樣

struct Circle {  int x;  int y;  int radius; };

(2)void *

指標是整個 C 語言的精髓所在。而你也一直敬畏著指標,又愛又恨地使用著它。許多教材都告訴你,int *叫做指向整型的指標,而 char *是指向字元型的指標,等等等等不一而足。然而這裡有一個另類的指標家族成員——void *。不要按照通常的命名方式叫它做指向void 類型的指標,它的正式的名字叫做:可以指向任意類型的指標。

(3)C中的參數個數可變函數

可變參數函數的原型聲明:

type VAFunction(type arg1, type arg2, … );

參數可以分為兩部分:個數確定的固定參數和個數可變的選擇性參數。函數至少需要一個固定參數,固定參數的聲明和普通函數一樣;選擇性參數由於個數不確定,聲明時用"..."表示。固定參數和選擇性參數公同構成一個函數的參數列表。

標準C/C++包含標頭檔stdarg.h,該標頭檔中定義了操作不定變數的相關宏:

void va_start ( va_list arg_ptr, prev_param ); /* ANSI version */type va_arg ( va_list arg_ptr, type ); void va_end ( va_list arg_ptr ); 

在這些宏中,va就是variable argument(可變參數)的意思;
arg_ptr    是指向可變參數表的指標;
prev_param 指可變參數表的前一個固定參數;
type       為可變參數的類型。
va_list    也是一個宏,其定義為typedef char * va_list,實質上是一char型指標。

二、封裝

封裝的主要含義是隱藏內部的行為和資訊,使用者只用看到對外提供的介面和公開的資訊。
在C語言中的實現方法:把私人資料資訊放在一個不透明的priv變數或者結構體中,只有類的實現代碼才知道priv或者結構體的真正定義。
例如:

//========標頭檔:Point.h檔案========#ifndef POINT_H#define POINT_Htypedef struct Point point;typedef struct pointPrivate pointPrivate;struct Point{struct pointPrivate *pp;};int get_x(point *point_);int get_y(point *point_);point * new_point(int x,int y);}#endif

源檔案

//=======C檔案:Point.c檔案========#include "Point.h"#include<stdlib.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;}point* new_point(int x,int y){ point* p=(point*)malloc(sizeof(point)); p->pp=(pointPrivate*)malloc(sizeof(pointPrivate)); p->pp->x=x; p->pp->y=y; return p;}

測試檔案:

int main(){point* p = new_point(1,2);//printf("x:%d,y:%d\n",p->pp->x,p->pp->y);printf("x:%d,y:%d\n",get_x(p),get_y(p));}

在測試代碼中,注釋掉的一部分是編譯不過的,因為我們已經把pointPrivate結構體的定義隱藏了。而且必須使用new_point來建立point結構對象,否則無法初始化point結構體中的pp成員變數。
有意思的是:這段代碼產生的exe檔案可能會被360誤認為病毒。

三、繼承
 
在C語言中,可以利用“結構在記憶體中的布局與結構的聲明具有一致的順序”這一事實實現繼承。   比如我們要設計一個作圖工具,其中可能涉及到的對象有Point(點),Circle(圓),由於圓是由點組成的,所有可以看成Circle繼承自Point。另外,Point和Circle都需要空間申請,空間釋放等操作,所有他們有共同的基類Base。

//基類Base的內部標頭檔Base.r,對外隱藏#ifndef BASE_R#define BASE_R#include struct Base {size_t size;void * (* ctor) (void * self, va_list * app);//建構函式void * (* dtor) (void * self);   //解構函式void (* draw) (const void * self);//作圖函數};#endif
//Point的內部標頭檔Point.r,對外隱藏#ifndef POINT_R#define POINT_Rstruct Point {const void * base;  //繼承Base類,基類指標,放在第一個位置,const是防止修改int x, y;   //座標};#define x(p) (((const struct Point *)(p)) -> x)#define y(p) (((const struct Point *)(p)) -> y)#endif

//Point的標頭檔Point.h(對外提供介面)#ifndef POINT_H#define POINT_Hextern const void * Point;   /* new(Point, x, y); */void move (void * point, int dx, int dy);#endif

//Point的源檔案Point.c#include #include "Point.h"#include "Point.r"#include "new.h"#include "Base.r"/**********Point類自己的建構函式***********/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;}/**********Point類自己的繪圖函數***********/static void Point_draw (const void * _self){ const struct Point * self = _self;printf("Point at %d,%d\n", self -> x, self -> y);}static const struct Base _Point = {sizeof(struct Point), Point_ctor, 0, Point_draw};const void * Point = & _Point;void move (void * _self, int dx, int dy){ struct Point * self = _self;self -> x += dx, self -> y += dy;}

//Circle內部標頭檔Circle.r,對外隱藏#ifndef CIRCLE_R#define CIRCLE_R#include "Point.r"struct Circle { const struct Point _;  //繼承Point類,需放在第一位int rad; };#endif

//Circle的標頭檔Circle.h(對外提供介面)#ifndef CIRCLE_H#define CIRCLE_H#include "Point.h"extern const void * Circle;  /* new(Circle, x, y, rad) */#endif

//Circle的源檔案Circle.c#include #include "Circle.h"#include "Circle.r"#include "new.h"#include "Base.r"/**********Circle類自己的建構函式***********/static void * Circle_ctor (void * _self, va_list * app){ struct Circle * self = ((const struct Base *) Point) -> ctor(_self, app);self -> rad = va_arg(* app, int);return self;}/**********Circle類自己的繪圖函數***********/static void Circle_draw (const void * _self){ const struct Circle * self = _self;printf("circle at %d,%d rad %d\n",x(self), y(self), self -> rad);}static const struct Base _Circle = {sizeof(struct Circle), Circle_ctor, 0, Circle_draw};const void * Circle = & _Circle;

//記憶體管理類標頭檔new.h(對外提供介面)#ifndef NEW_H#define NEW_Hvoid * new (const void * base, ...);void delete (void * item);void draw (const void * self);#endif

//記憶體管理類的源檔案:new.c#include #include #include #include "Base.r"void * new (const void * _class, ...){ const struct Base * base = _class;void * p = calloc(1, base -> size);assert(p);* (const struct Base **) p = base;if (base -> ctor){ va_list ap;va_start(ap, _class);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);}

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

#include "Circle.h"#include "new.h"int main (int argc, char ** argv){void * p;int i;for(i=0; i<2; i++){if(i==0)p = new(Circle, 1, 2, 3);elsep = new(Point, 1, 2);draw(p);move(p, 10, 20);draw(p);delete(p);}return 0;}

輸出結果:
circle at 1,2 rad 3
circle at 11,22 rad 3
Point at 1,2
Point at 11,22

五、總結
 
物件導向是一種程式設計思想,而 C 語言則是一種程式設計語言。也許它並不是專門為了物件導向編程而設計,但是這絕不意味著它不能實現物件導向的程式設計。當然以上所展示的這幾個操作,如果是用別的程式設計語言,可能只要寥寥幾行就可以完成,但是 C 語言想告訴我們的是:也許我不擅長,但是並不意味著我做不到。

參考書籍《Object-Oriented Programming With ANSI-C》

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.