很多開發(fā)工程師都是從學習C語言的"Hello world!"開始的,都知道C語言中的指針是1把利劍,1不留意就傷了自個。但其C語言絕對是1個宗師級語言,這是不可否認的。
由于我們開發(fā)的需要在多個平臺上運行且需要面向?qū)ο蟮?些特性、所以特寫此文章。權當拋磚引玉。
1、概述
C語言是1種面向進程的程序設計語言、而C++在語言級別上添加了很多新機制(繼承,多態(tài)等)
因此這里說說使用C語言實現(xiàn)封裝,繼承和多態(tài)的方法。
2、基本知識
1、結構體
在C語言中,常把1個對象用結構體進行封裝,這樣便于對對象進行操作,比如:1
strcut Point{
int x;
int y;
};
結構體可以嵌套。因此可以把1個結構體當做另外一個結構體的成員:
struct Circle {
struct Point point_;
int radius;
};
該結構體與以下定義完全1樣(包括內(nèi)存布置都1樣):
struct Circle {
int x;
int y;
int radius;
};
2、函數(shù)指針
函數(shù)指針是指針的1種,它指向函數(shù)的首地址(函數(shù)的函數(shù)名即為函數(shù)的首地址),可以通過函數(shù)指針來調(diào)用函數(shù)。
如函數(shù):
int func(int a[], int n);
可以這樣聲明函數(shù)指針:
int (*pFunc)(int a[], int n);
這樣使用:
pFunc = func;
(*pFunc)(a, n);【或PFunc(a, n)】
可以用typedef定義1個函數(shù)指針類型,如:
typdef int (*FUNC)(int a[], int n)
可以這樣使用:
int cal_a(FUNC fptr, int a[], int n)
{
//實現(xiàn)體...
}
3、extern與static
extern和static是C語言中的兩個修飾符,extern可用于修飾函數(shù)或變量,表示該變量或函數(shù)在其他文件中進行了定義;
static也可用于修飾函數(shù)或變量,表示該函數(shù)或變量只能在該文件中使用。可利用它們對數(shù)據(jù)或函數(shù)進行隱藏或限制訪問權限。
3、封裝
在C語言中,可以用結構+函數(shù)指針來摹擬類的實現(xiàn),而用這類結構定義的變量就是對象。
封裝的主要含義是隱藏內(nèi)部的行動和信息,使用者只用看到對外提供的接口和公然的信息。
有兩種方法實現(xiàn)封裝:
1、利用C語言語法。在頭文件中聲明,在C文件中真正定義它。
這樣可以隱藏內(nèi)部信息,由于外部不知道對象所占內(nèi)存的大小,所以不能靜態(tài)的創(chuàng)建該類的對象,只能調(diào)用類提供的創(chuàng)建函數(shù)才能創(chuàng)建。這類方法的缺點是不支持繼承,由于子類中得不到任何關于父類的信息。如:
/**
* 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類,基類指針,放在第1個位置,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類自己的構造函數(shù)***********/
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
", self);
return self;
}
/**********Point類自己的析構函數(shù)***********/
static void* Point_Delete(void * _self) {
printf("call Point_Delete self =%p
", _self);
printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@
");
return NULL;
}
/**********Point類自己的繪圖函數(shù)***********/
static void Point_Draw(const void * _self) {
const struct Point * self = _self;
printf("Point_Draw at %d,%d
", self->x, self->y);
}
void move(void * _self, int dx, int dy) {
struct Point * self = _self;
printf("call move self =%p
", _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;
4、繼承
在C語言中,可以利用“結構在內(nèi)存中的布局與結構的聲明具有1致的順序”這1事實實現(xiàn)繼承。
比如我們要設計1個作圖工具,其中可能觸及到的對象有Point(點),Circle(圓),由于圓是由點組成的,所有可以看成Circle繼承自Point。另外,Point和Circle都需要空間申請,空間釋放等操作,所有他們有共同的基類Base。
#ifndef C_NEW_H
#define C_NEW_H
/**
* 內(nèi)存管理類頭文件cnew.h(對外提供接口)
*/
void * cnew(const void * base, ...);
void cdelete(void * item);
void draw(const void * self);
#endif /* C_NEW_H */
/**
* 內(nèi)存管理類的源文件: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的內(nèi)部頭文件Base.r,對外隱藏
*/
#ifndef BASE_R
#define BASE_R
#include <stdarg.h>
struct Base {
size_t size;
void * (*constructor)(void * self, va_list * app); //構造函數(shù)
void * (*destroy)(void * self);
//析構函數(shù)
void (*draw)(const void * self);//作圖函數(shù)
};
#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類,基類指針,放在第1個位置,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類自己的構造函數(shù)***********/
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
", self);
return self;
}
/**********Point類自己的析構函數(shù)***********/
static void* Point_Delete(void * _self) {
printf("call Point_Delete self =%p
", _self);
printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@
");
return NULL;
}
/**********Point類自己的繪圖函數(shù)***********/
static void Point_Draw(const void * _self) {
const struct Point * self = _self;
printf("Point_Draw at %d,%d
", self->x, self->y);
}
void move(void * _self, int dx, int dy) {
struct Point * self = _self;
printf("call move self =%p
", _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類,需放在第1位
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類自己的擴大函數(shù)***********/
static int Circle_Area(void * _self) {
const struct Circle * self = _self;
printf("call Circle_Area self =%p
", _self);
return self->radius * self->radius;
}
/**********Circle類自己的構造函數(shù)***********/
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
", _self);
return self;
}
/**********Circle類自己的構造函數(shù)***********/
static void* Circle_Delete(void * _self) {
printf("call Circle_Delete self =%p
", _self);
printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@
");
return NULL;
}
/**********Circle類自己的繪圖函數(shù)***********/
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
", x, y, self->radius);
}
static const struct Base _Circle = { sizeof(struct Circle), Circle_New, Circle_Delete, Circle_Draw };
const void * Circle = &_Circle;
/**
* 測試函數(shù)
*/
#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
*
************************************/
5、多態(tài)
可以是用C語言中的萬能指針void* 實現(xiàn)多態(tài),接上面的例子:
//測試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;
}
6、總結
C語言能夠摹擬實現(xiàn)面向?qū)ο笳Z言具有的特性,包括:多態(tài),繼承,封裝等,現(xiàn)在很多開源軟件都了用C語言實現(xiàn)了這幾個特性,包括大型開源數(shù)據(jù)庫系統(tǒng)postgreSQL,可移植的C語言面向?qū)ο罂蚣蹽Object,無線2進制運行環(huán)境BREW。采取C語言實現(xiàn)多態(tài),繼承,封裝,能夠讓軟件有更好的可讀性,可擴大性。
全部的測試代碼:
http://download.csdn.net/detail/andyhuabing/8475335
上一篇 單臂路由與三層交換
下一篇 HTML常用標簽之格式標簽