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

8

strcut Point{

 

int x;

 

int y;

 

};

结构体可以嵌套。因而可以把一个结构体当成另一个结构体的成员,如:


1

2

3

4

5

6

7

8

struct Circle {

 

struct Point point_;

 

int radius;

 

};

该结构体与以下定义完全一样(包括内存布置都一样):


1

2

3

4

5

6

7

8

9

10

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

52

//头文件: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

58

#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

186

//内存管理类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

10

struct Circle

 

{

 

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

 

int radius;

 

};

5、  多态

可以是用C语言中的万能指针void* 实现多态,接上面的例子:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

//测试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、  代码下载

本文中的代码可以在此处下载:代码下载

时间: 2024-10-11 02:53:46

c语言实现封装、继承和多态的相关文章

day33 序列类型,绑定方法,类方法,静态方法,封装继承和多态

Python之路,Day20 = 序列类型,绑定方法,类方法,静态方法,封装继承和多态 序列是指有序的队列,重点在"有序". 一.Python中序列的分类 Python中的序列主要以下几种类型: 3种基本序列类型(Basic Sequence Types):list.tuple.range 专门处理文本的附加序列类型(Text Sequence Types):str 专门处理二进制数据的附加序列类型(Binary Sequence Types): bytes.bytearray.mem

Mysql数据库大量删除操作及谈面向对象中的封装继承和多态原理(图)

Mysql数据库大量删除操作及谈面向对象中的封装继承和多态原理(图)最近进行数据库操作,遇到一个问题,就是大量删除一个数据表中的数据后,由于设定了id是自增的,导致再插入时,默认生成的id会很大,这个时候想要再次插入新的数据,应该怎么办呢?1.明确目前最后一个id的大小select id from tags order by id DESC limit 0,1; 假设返回的是9,则设置数据表从10开始自增 2.修改这个数据表从10开始自增alter table tags auto_increme

C++之封装继承和多态

C++中非常重要的概念,尤其是相对于C语言而言,也是其具有如此高的工程使用性的重要原因. 封装 所谓封装是将某些东西隐藏起来,让外界无法直接使用,而必须通过某些特定的方式才能访问.也即是,将抽象得到的数据和行为(类似于属性和方法)结合构成一个有机整体,将数据与操作数据的函数构成类,其中数据和函数都是类的成员. 其目的是将对象的使用者和设计者隔离开来,提高软件的可维护性和可修改性,使用者不必了解具体的实现细节而只是通过外部接口及特定的访问权限使用类成员,从而增强了安全性且简化了编程,也使得不同类之

面向对象:封装继承和多态、接口

1.多态 定义:不同对象对于同一个方法(Cut)调用表现出不同行为多态性主要是靠重写和隐藏来实现 a.(父类引用指向子类对象)b.(父类类型做参数,传递子类对象)自定义方法参数是父类类型 传递的实参是子类对象c.(父类类型作返回类型,return子类对象)自定义方法的返回类型是父类类型,return是类对象注:都是把子类对象赋给父类类型 举例: 添加乐器类using System;using System.Collections.Generic;using System.Linq;using S

黑马程序员--oc 类的封装 继承 和多态

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 成员变量的命名规范: 成员变量名要以 _开头 作用: 1.可以让成员变量和get方法的名称区分开 2.可以和局部变量区分开,一看到以_开头的变量一般都是成员变量 封装: 在定义成员变量时,尽量不要用@public 这种写法 ,而是使用set方法和get方法 @interface Student : NSObject { int _age; } /* set方法: 作用:提供一个方法给外界设置成

OC语言学习 (六) 继承、多态,构造方法,description方法

声明父类Animal继承自NSObject Animal.h #ifndef oc_Animal_h #define oc_Animal_h @interface Animal : NSObject { @public int weight; } - (void)eat; //重写默认构造方法 - (id) init; //自定义构造方法 - (id) initWithWeight:(int)newWeight; @end #endif Animal.m #import <Foundation/

Java中封装继承和多态

封装: 封装实际上使用方法将类的数据隐藏起来,控制用户对类的修改和访问数据的程度. 适当的封装可以让程式码更容易理解和维护,也加强了程式码的安全性. 访问修饰符有public,private,protected,默认四种. 通过private将类封装起来,防止被随意更改,通过写public的get,set方法让别人调用.

类的封装 继承和多态

package 银行账户管理;import java.text.SimpleDateFormat;   //常用日期操作工具类import java.util.Calendar;     //使用默认时区和语言环境获得一个日历import java.util.Date;public class yinhang {  private String acount;     //账户  private String name;   //姓名  private String time;   //时间  

2、C#面向对象:封装、继承、多态、String、集合、文件(上)

面向对象封装 一.面向对象概念 面向过程:面向的是完成一件事情的过程,强调的是完成这件事情的动作. 面向对象:找个对象帮你完成这件事情. 二.面向对象封装 把方法进行封装,隐藏实现细节,外部直接调用. 打包,便于管理,为了解决大型项目的维护与管理. 三.什么是类? 将相同的属性和相同方法的对象进行封装,抽象出 “类”,用来确定对象具有的属性和方法. 类.对象关系:人是类,张三是人类的对象. 类是抽象的,对象是具体的.对象可以叫做类的实例,类是不站内存的,对象才占内存. 字段是类的状态,方法是类执