1. 程式人生 > >C實現類封裝、繼承、多型

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語言中,常把一個物件用結構體進行封裝,這樣便於對物件進行操作,比如:

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也可用於修飾函式或者變數,表示該函式或者變數只能在該檔案中使用。可利用它們對資料或者函式進行隱藏或者限制訪問許可權。

3、  封裝

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

4、  繼承

在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;

 

};

5、  多型

可以是用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);

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、  程式碼下載

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

分類: 

相關推薦

黑馬程式設計師Objective-C筆記:封裝繼承

例子: #import @interface Animal : NSObject - (void)eat; @end @implementation Animal - (void)eat { NSLog(@"Animal---Eating something!!"); } @end

C實現封裝繼承

開發十年,就只剩下這套架構體系了! >>>   

C語言實現封裝繼承

雖然C語言一直被稱為面向過程的設計語言,但是通過使用C語言函式指標和結構體的特性,還是能夠使用C語言實現類似面向物件的封裝、繼承和多型的概念。 下面對原始碼進行分析: validator.h檔案 首先提取了所有校驗器都有的校驗方法,定義了一個校驗器抽象類Validato

C#入門基礎08(封裝繼承)

一、封裝 C#中可使用類來達到資料封裝的效果,這樣可以使資料與方法封裝成單一元素,以便於通過方法存取資料。除此之外,還可以控制資料的存取方式。 在面向物件程式設計中,大多數都是以類作為資料封裝的基本單位。類將資料和操作資料的方法結合成一個單位。設計類時,不希望直接存取類中的資料,而是希

Java之路:封裝繼承

面向物件有三大特點:封裝性、繼承性和多型性。 一、封裝 1、封裝的含義 封裝 (Encapsulation)是將描述某類事物的資料與處理這些資料的函式封裝在一起,形成一個有機整體,稱為類。 類所具有的封裝性可使程式模組具有良好的獨立性與可維護性,這對大型程式的開發是特別重要的

C#】之 封裝繼承

我們知道封裝、繼承和多型是面向物件方法設計中的三大基本特性,下面將具體講解這三個特性的具體表現及意義。 #一、封裝 ##1、說明   從字面意思上看,封裝就是打包的意思,將什麼包裝起來,專業一點就是資訊的隱藏,將物件的屬性和方法打包成一個相對獨立的單位,儘可能隱蔽物件的內部細

php封裝繼承的簡單理解

面象對向的三大特點:封裝性、繼承性、多型性 首先簡單理解一下抽象: 我們在前面定義一個類的時候,實際上就是把一類事物共有的屬性和行為提取出來,形成一個物理模型(模版),這種研究問題的方法稱為抽象 一、封裝性  封裝就是把抽取出來的資料和對資料的操作封裝在一起,資料被保護在內

java之 ------ 封裝繼承(二)

問題:宣告銀行賬戶類,成員變數包括賬號、儲戶姓名、開啟時間、身份證號碼、存款餘額等賬戶資訊,成員方法包括開戶、存款、取款、查詢(餘額、明細)、銷戶等操作。 主要成員描述如下 public class Account { //

Objective-c封裝繼承

面向物件的三個基本特徵是:封裝、繼承、多型。 封裝 簡介 封裝是實現面向物件程式設計的第一步,封裝就是將資料或函式等集合在一個個的單元中(我們稱之為類)。被封裝的物件通常被成為抽象資料型

再談面向物件中的封裝繼承

封裝 封裝說的是把資料封裝起來,對外暴露一個可以訪問的介面,不能讓外界直接訪問內部的資料。 從上面的描述可以抽取出兩種型別:介面和類。 從現在比較火的微服務的觀點上來看,一個類就是一個服務,一個物件就是一個服務的例項,通過這個服務暴露的介面來訪問這個服務。從這個意義上來講,面向

java初學 面向物件二 static關鍵字 封裝繼承

static關鍵字 1:如果沒有static會怎樣? 1:定義Person類 1:姓名、年齡、國籍,說話行為 2:多個構造,過載形式體現 2:中國人的國籍都是確定的 1:國籍可以進行顯示初始化 class Person {

Java面向物件與執行緒綜合實驗(一)之封裝繼承

編寫一個程式,實現檔案管理系統中的使用者管理模組。要求模組中實現使用者的模擬登入過程。通過使用者輸入,獲取使用者名稱和口令;與事先記錄在程式中的使用者資訊進行對比,通過口令驗證後才能使用系統。使用者分為系統管理人員、檔案錄入人員,檔案瀏覽人員三類,相關類圖如下所示。 (1)要求在使用者類中

封裝繼承的具體例項

封裝 public class Demo_7 { public static void main(String []args) { Stu1 stu1=new Stu1(); stu1.get_age(6);

python中封裝繼承——python學習筆記

1. 準備 封裝、繼承和多型在程式語言中專指面向物件程式設計的特性,下面先給出一個python中建立類的例子: class Student(object): def __init__(self, name, score): self.name = name

JAVA基礎之封裝繼承

封裝、繼承、多型 1、封裝:隱藏實現細節,明確標識出允許外部使用的所有成員函式和資料項,從而防止程式碼或資料被破壞。 2、繼承:子類繼承父類,擁有父類的所有功能,並且可以在父類的基礎上進行擴充套件。實現了程式碼的重用性。子類和父類是相容的。 3、多型:一個介面有多個子類或實現類,

c++的封裝繼承的簡單介紹

 本篇文章僅僅從很表層來介紹一個C++語言中的類,包括什麼是類,類的封裝性/繼承性和多型性。高手直接跳過吧,看了浪費時間,新手或者想溫習一下的可以瀏覽看看。 1. 什麼是類? 到底什麼是類(class)??類就是一種型別,是使用者自己定義的一個型別,和內建型別如int/float/double類似,  用一

js:面向物件程式設計,帶你認識封裝繼承

週末的時候深入的瞭解了下javascript的面向物件程式設計思想,收穫頗豐,感覺對面向物件程式設計有了那麼一丟丟的瞭解了~很開森 什麼是面向物件程式設計 先上一張圖,可以對面向物件有一個大致的瞭解,然而什麼是面向物件呢,用java中的一句經典語

對Java面向物件程式設計的知識點(封裝繼承)進行總結

一 封裝 1.面向物件的高階:優化程式設計    封裝:      類中如何定義成員變數?       private   int  num;      概念      語法        3步驟    繼承:減少冗餘程式碼       繼承:一個類(子類)繼承另一個類(父

第10章 介面繼承

10.1 package tex; public abstract class one { abstract void test(); public static void main(String args[]) { new one(); } } //無法例項化 10.2 p

OC學習篇之---的三大特性 封裝繼承

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!