用C實現物件導向

來源:互聯網
上載者:User

標籤:

    很多開發工程師都是從學習C語言的"Hello world!"開始的,都知道C語言中的指標是一把利劍,一不留意就傷了自個。但其C語言絕對是一個宗師級語言,這是不可否認的。
    由於我們開發的需要在多個平台上運行且需要物件導向的一些特性、所以特寫此文章。權當拋磚引玉。
    
一、概述
     C語言是一種面向過程的程式設計語言、而C++在語言層級上添加了很多新機制(繼承,多態等)
     因此這裡說說使用C語言實現封裝,繼承和多態的方法。


二、基本知識


1、結構體


在C語言中,常把一個對象用結構體進行封裝,這樣便於對對象進行操作,比如:1
  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也可用於修飾函數或者變數,表示該函數或者變數只能在該檔案中使用。可利用它們對資料或者函數進行隱藏或者限制存取權限。


三、封裝


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


封裝的主要含義是隱藏內部的行為和資訊,使用者只用看到對外提供的介面和公開的資訊。


有兩種方法實現封裝:


1、利用C語言文法。在標頭檔中聲明,在C檔案中真正定義它。


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


/**
 * Point的標頭檔Point.h(對外提供介面)
 */
#ifndef POINT_H
#define POINT_H


extern const void * Point; /* new(Point, x, y); */


void move(void * point, int dx, int dy);


struct Point {
const void * base;  //繼承Base類,基類指標,放在第一個位置,const是防止修改
int x, y; //座標
};


#define point_x(p)(((const struct Point *)(p)) -> x)
#define point_y(p)(((const struct Point *)(p)) -> y)


#endif


 
//Point的源檔案Point.c
#include <stdio.h>
#include "Point.h"
#include "cnew.h"
#include "Base.h"


/**********Point類自己的建構函式***********/
static void * Point_New(void * _self, va_list * app) {
struct Point * self = _self;
self->x = va_arg(*app, int);
self->y = va_arg(*app, int);
printf("Point_New self = %p \n", self);
return self;
}


/**********Point類自己的解構函式***********/
static void* Point_Delete(void * _self) {
printf("call Point_Delete self =%p \n", _self);
printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@ \n\n");
return NULL;
}


/**********Point類自己的繪圖函數***********/
static void Point_Draw(const void * _self) {
const struct Point * self = _self;
printf("Point_Draw at %d,%d \n", self->x, self->y);
}


void move(void * _self, int dx, int dy) {
struct Point * self = _self;
printf("call move self =%p \n", _self);
self->x += dx;
self->y += dy;
}


static const struct Base _Point = { sizeof(struct Point), Point_New, Point_Delete, Point_Draw };
const void * Point = &_Point;


四、繼承


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


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


#ifndef C_NEW_H
#define C_NEW_H


/**
 * 記憶體管理類標頭檔cnew.h(對外提供介面)
 */


void * cnew(const void * base, ...);


void cdelete(void * item);


void draw(const void * self);


#endif /* C_NEW_H */


 
/**
 * 記憶體管理類的源檔案:cnew.c
 */
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdarg.h>
#include "cnew.h"
#include "Base.h"


void * cnew(const void * _class, ...) {
const struct Base * base = _class;
void * p = calloc(1, base->size);
assert(p);
*(const struct Base **) p = base;
if (base->constructor) {
va_list ap;
va_start(ap, _class);
p = base->constructor(p, &ap);
va_end(ap);
}
return p;
}


void cdelete(void * self) {
const struct Base ** cp = self;
if (self && *cp && (*cp)->destroy)
self = (*cp)->destroy(self);
free(self);
}


void draw(const void * self) {
const struct Base * const * cp = self;
assert(self && *cp && (*cp)->draw);
(*cp)->draw(self);
}
 
/**
 *  基類Base的內部標頭檔Base.r,對外隱藏
 */
#ifndef BASE_R
#define BASE_R


#include <stdarg.h>


struct Base {
size_t size;
void * (*constructor)(void * self, va_list * app); //建構函式
void * (*destroy)(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); */


void move(void * point, int dx, int dy);


struct Point {
const void * base;  //繼承Base類,基類指標,放在第一個位置,const是防止修改
int x, y; //座標
};


#define point_x(p)(((const struct Point *)(p)) -> x)
#define point_y(p)(((const struct Point *)(p)) -> y)


#endif


//Point的源檔案Point.c
#include <stdio.h>
#include "Point.h"
#include "cnew.h"
#include "Base.h"


/**********Point類自己的建構函式***********/
static void * Point_New(void * _self, va_list * app) {
struct Point * self = _self;
self->x = va_arg(*app, int);
self->y = va_arg(*app, int);
printf("Point_New self = %p \n", self);
return self;
}


/**********Point類自己的解構函式***********/
static void* Point_Delete(void * _self) {
printf("call Point_Delete self =%p \n", _self);
printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@ \n\n");
return NULL;
}


/**********Point類自己的繪圖函數***********/
static void Point_Draw(const void * _self) {
const struct Point * self = _self;
printf("Point_Draw at %d,%d \n", self->x, self->y);
}


void move(void * _self, int dx, int dy) {
struct Point * self = _self;
printf("call move self =%p \n", _self);
self->x += dx;
self->y += dy;
}


static const struct Base _Point = { sizeof(struct Point), Point_New, Point_Delete, Point_Draw };
const void * Point = &_Point;


/**
 * Circle的標頭檔Circle.h(對外提供介面)
 */


#ifndef CIRCLE_H
#define CIRCLE_H


#include "Point.h"


extern const void * Circle; /* new(Circle, x, y, rad) */


struct Circle {
const struct Point pbase;  //繼承Point類,需放在第一位
int radius;
int (*area)(void *self);// 面積,擴充方法
};


#define circle_area(p) (((const struct Circle *)(p)) -> area(p))


#endif




/**
 * Circle的源檔案Circle.c
 */
#include <stdio.h>
#include "Circle.h"
#include "cnew.h"
#include "Base.h"


/**********Circle類自己的擴充函數***********/
static int Circle_Area(void * _self) {
const struct Circle * self = _self;
printf("call Circle_Area self =%p \n", _self);
return self->radius * self->radius;
}


/**********Circle類自己的建構函式***********/
static void * Circle_New(void * _self, va_list * app) {
struct Circle * self = ((const struct Base *) Point)->constructor(_self, app);
self->radius = va_arg(*app, int);
self->area = Circle_Area;
printf("call Circle_New self =%p \n", _self);
return self;
}


/**********Circle類自己的建構函式***********/
static void* Circle_Delete(void * _self) {
printf("call Circle_Delete self =%p \n", _self);
printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@ \n\n");
return NULL;
}


/**********Circle類自己的繪圖函數***********/
static void Circle_Draw(const void * _self) {
const struct Circle * self = _self;
int x = point_x(self);
int y = point_y(self);
printf("Circle_Draw at %d,%d rad %d \n", x, y, self->radius);
}


static const struct Base _Circle = { sizeof(struct Circle), Circle_New, Circle_Delete, Circle_Draw };
const void * Circle = &_Circle;






/**
 * 測試函數
 */


#include "Circle.h"
#include "cnew.h"


int oo_main(int argc, char ** argv) {
void * p;
int i;
for (i = 0; i < 2; i++) {
if (i == 0) {
p = cnew(Circle, 1, 2, 3);
circle_area(p);
} else {
p = cnew(Point, 1, 2);
}
draw(p);
move(p, 10, 20);
draw(p);
cdelete(p);
}
return 0;
}


/***********************************
 * 測試結果:
 *
 * Point_New self = 0x50a1d8
 * call Circle_New self =0x50a1d8
 * Circle_Draw at 1,2 rad 3
 * call move self =0x50a1d8
 * Circle_Draw at 11,22 rad 3
 * call Circle_Delete self =0x50a1d8
 *
 * Point_New self = 0x5096a0
 * Point_Draw at 1,2
 * call move self =0x5096a0
 * Point_Draw at 11,22
 * call Point_Delete self =0x5096a0
 *
 ************************************/


五、多態


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


 //測試main.c
 
int oo_main(int argc, char ** argv) {
void * p;
int i;
for (i = 0; i < 2; i++) {
if (i == 0) {
p = cnew(Circle, 1, 2, 3);
circle_area(p);
} else {
p = cnew(Point, 1, 2);
}
draw(p);
move(p, 10, 20);
draw(p);
cdelete(p);
}
return 0;
}


六、總結


C語言能夠類比實現物件導向語言具有的特性,包括:多態,繼承,封裝等,現在很多開源軟體都了用C語言實現了這幾個特性,包括大型開來源資料庫系統postgreSQL,可移植的C語言物件導向架構GObject,無線二進位運行環境BREW。採用C語言實現多態,繼承,封裝,能夠讓軟體有更好的可讀性,可擴充性。


全部的測試代碼:


http://download.csdn.net/detail/andyhuabing/8475335


用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.