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

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

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

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

    Python之路,Day20 = 序列类型,绑定方法,类方法,静态方法,封装继承和多态 序列是指有序的队列,重点在"有序". 一.Python中序列的分类 Python中的序列主要 ...

  2. objective-c自学总结(三)---面向对象的封装,继承与多态

    面向对象的三大特性 封装 继承 多态 1.封装: 隐藏属性,方法或实现细节的过程称为封装 信息隐藏,隐藏对象的实现细节,不允许用户看到 将东西包装在一 然后以新的完整形式呈现出来 例如,两种或多种化学 ...

  3. C语言设计模式-封装-继承-多态

    快过年了,手头的工作慢慢也就少了,所以,研究技术的时间就多了很多时间,前些天在CSDN一博客看到有大牛在讨论C的设计模式,正好看到了,我也有兴趣转发,修改,研究一下. 记得读大学的时候,老师就告诉我们 ...

  4. C++之封装继承和多态

    C++中非常重要的概念,尤其是相对于C语言而言,也是其具有如此高的工程使用性的重要原因. 封装 所谓封装是将某些东西隐藏起来,让外界无法直接使用,而必须通过某些特定的方式才能访问.也即是,将抽象得到的 ...

  5. python 面向对象及封装继承和多态

    ######装饰器######装饰器的概念 - 装饰器的实现是函数里面嵌套函数;- 装饰器的本质是一个函数, 它可以让其他函数在不需要做任何代码改动的前提下增加额外的功能;- 装饰器需要传递一个函数, ...

  6. Java的封装继承和多态

    封装 定义:属性私有private:get/set 目的 提高程序的安全性,保护数据 隐藏代码的实现细节 统一接口 提高系统的可维护性 代码 public class Student { //名字 p ...

  7. java封装继承以及多态(含代码)

    封装 该露的露,该藏的藏 我们常需设计要追求,"高内聚,低耦合".高内聚就是类的内部数据操作细节自己完成.不允许外部干涉:低耦合:仅暴漏少量的方法给外部使用. 封装(数据的隐藏) ...

  8. 《Java语言程序设计》继承与多态

    一.动手实验:继承条件下的构造方法 调用运行 TestInherits.java 示例,观察输出,注意总结父类与子类之间构造方法的调用关系修改Parent构造方法的代码,显式调用GrandParent ...

  9. Java语言简介、基础组成、封装、继承、多态、抽象类、内部类、接口

    目录 Java简介 Java语言基础组成 面向对象 对象 封装 构造函数 this关键字 static(静态关键字) 主函数 静态什么时候用呢? 面向对象(数组工具对象建立) 设计模式 继承 成员变量 ...

随机推荐

  1. k-近邻算法实例

    1. 简单例子 步骤 1.1 计算已知点和被求点的距离 1.2 按距离递增排序 1.3 求出距离最近的前k个点的类别最大值作为目标分类 from numpy import * import opera ...

  2. iView的使用【小白向】

    首先看这篇:构建Vue本地开发环境(现阶段还不知道怎么用CDN的方式做...) 安装iView(WindowsPowershell或cmd下用cnpm) 编辑上一篇博客创建的Vue工程 先到main. ...

  3. P神的SDFZ考试题 C题

                                                                      探险[问题描述]          探险家小 T 好高兴! X 国要 ...

  4. jQuery中$(function(){})与(function($){})(jQuery)、$(document).ready(function(){})等的区别详细讲解 ----转载

    1.(function($) {-})(jQuery); 1).原理: 这实际上是匿名函数,如下: function(arg){-} 这就定义了一个匿名函数,参数为arg 而调用函数时,是在函数后面写 ...

  5. 使用Linux 安装MySQL

    文章  link 在安装mysql数据库服务器前,确保你的linux系统是可以连接网络的,下面我们将通过源码方式来安装mysql首先通过putty登入进你的Linux系统,确保系统中已经安装的gcc ...

  6. pstree 命令详解

    作用: 以命令树状图的方式展现进程之间的派生关系, 显示效果比较直观. 选项: -a 显示每个程序的完整指令, 包含路径, 参数或者是常驻服务的标志 -c 不使用精简标示法 -h 列出树状图,特别标明 ...

  7. readAsDataURL(file) & readAsText(file, encoding)

      readAsDataURL(file)会把文件内容转换为data类型的URL: data:text/plain;base64,b3JkZXItaWQJb3JkZXItaXRlbS1p... 这种d ...

  8. python csv模块的reader是一个迭代器,无法多次迭代

    在一个项目中,我需要多次遍历一个文本,该文本我是用csv.reader读取的.但后来发现,本文只对第一次循环有用,而之后的循环均为空白.经过排错后,我确定问题就出现在csv.reader()这一步.之 ...

  9. 微信小程序红包开发 小程序发红包 开发过程中遇到的坑 微信小程序红包接口的

    最近公司在开发一个小程序红包系统,客户抢到红包需要提现.也就是通过小程序来给用户发红包. 小程序如何来发红包呢?于是我想到两个方法. 之前公众号开发一直用了的.一个是红包接口,一个是企业支付接口.一开 ...

  10. linux编译php gd扩展

    1 安装gd的依赖包 yum -y install gd gd2 gd-devel gd2-devel zlib freetype 2 安装jpeg: wget http://www.ijg.org/ ...