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

一、  概述html

 

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

#ifndef Interfaceweb

#define Interface structsql

#endif數據庫

C++在語言級別上添加了不少新機制(繼承,多態等),而在C語言中,咱們也可使用這樣的機制,前提是咱們不得不本身實現。編程

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

二、  基本知識app

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

(1)    結構體編程語言

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

1

2

3

4

5

6

7

strcut Point{

 

int x;

 

int y;

 

};

結構體能夠嵌套。於是能夠把一個結構體當成另外一個結構體的成員,如:

1

2

3

4

5

6

7

struct Circle {

 

struct Point point_;

 

int radius;

 

};

該結構體與如下定義徹底同樣(包括內存佈置都同樣):

1

2

3

4

5

6

7

8

9

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文件中真正定義它。

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

//頭文件: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或者結構體的真正定義。如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

#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…..

四、  繼承

在C語言中,能夠利用「結構在內存中的佈局與結構的聲明具備一致的順序」這一事實實現繼承。

好比咱們要設計一個做圖工具,其中可能涉及到的對象有Point(點),Circle(圓),因爲圓是由點組成的,全部能夠當作Circle繼承自Point。另外,Point和Circle都須要空間申請,空間釋放等操做,全部他們有共同的基類Base。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

//內存管理類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,則能夠這樣:

1

2

3

4

5

6

7

8

9

struct Circle

 

{

 

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

 

int radius;

 

};

五、  多態

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

1

2

3

4

5

6

7

8

9

10

11

12

13

//測試main.c

 

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

 

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

 

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

 

draw(pp);

 

delete(p);

 

delete(pp);

六、  總結

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

七、  參考資料

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

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

(2)        《3、使用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》

八、  代碼下載

本文中的代碼能夠在此處下載:代碼下載

分類: 編程語言

相關文章
相關標籤/搜索