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语言中,常把一个对象用结构体进行封装,这样便于对对象进行操作,比如:
strcut Point{ int x; int y; };
结构体可以嵌套。因而可以把一个结构体当成另一个结构体的成员,如:
struct Circle { struct Point point_; int radius; };
该结构体与以下定义完全一样(包括内存布置都一样):
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文件中真正定义它。
这样可以隐藏内部信息,因为外部不知道对象所占内存的大小,所以不能静态的创建该类的对象,只能调用类提供的创建函数才能创建。这种方法的缺陷是不支持继承,因为子类中得不到任何关于父类的信息。如:
//头文件: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或者结构体的真正定义。如:
#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。
//内存管理类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,则可以这样:
struct Circle { const struct Point point; //放在第一位,可表继承 int radius; };
5、 多态
可以是用C语言中的万能指针void* 实现多态,接上面的例子:
//测试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实现类封装、继承、多态的更多相关文章
- php面向对象 封装继承多态 接口、重载、抽象类、最终类总结
1.面向对象 封装继承多态 接口.重载.抽象类.最终类 面向对象 封装继承多态 首先,在解释面向对象之前先解释下什么是面向对象? [面向对象]1.什么是类? 具有相同属性(特征)和方法(行为)的一 ...
- java面向对象(封装-继承-多态)
框架图 理解面向对象 面向对象是相对面向过程而言 面向对象和面向过程都是一种思想 面向过程强调的是功能行为 面向对象将功能封装进对象,强调具备了功能的对象. 面向对象是基于面向过程的. 面向对象的特点 ...
- 浅谈学习C++时用到的【封装继承多态】三个概念
封装继承多态这三个概念不是C++特有的,而是所有OOP具有的特性. 由于C++语言支持这三个特性,所以学习C++时不可避免的要理解这些概念. 而在大部分C++教材中这些概念是作为铺垫,接下来就花大部分 ...
- Java三大特性(封装,继承,多态)
Java中有三大特性,分别是封装继承多态,其理念十分抽象,并且是层层深入式的. 一.封装 概念:封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别:将抽象得到的数据 ...
- Java基础——面向对象(封装——继承——多态 )
对象 对象: 是类的实例(实现世界中 真 实存在的一切事物 可以称为对象) 类: 类是对象的抽象描述 步骤: 1.定义一个类 (用于 描述人:) ( * 人:有特征和行为) 2.根据类 创建对象 -- ...
- Python 入门 之 面向对象的三大特性(封装 / 继承 / 多态)
Python 入门 之 面向对象的三大特性(封装 / 继承 / 多态) 1.面向对象的三大特性: (1)继承 继承是一种创建新类的方式,在Python中,新建的类可以继承一个或多个父类,父类又可以 ...
- OOP三大核心封装继承多态
OOP支柱 3 个核心:封装 继承 多态 封装就是将实现细节隐藏起来,也起到了数据保护的作用. 继承就是基于已有类来创建新类可以继承基类的核心功能. 在继承中 另外一种代码重用是:包含/委托,这种重用 ...
- python面向对象(封装,继承,多态)
python面向对象(封装,继承,多态) 学习完本篇,你将会深入掌握 如何封装一个优雅的借口 python是如何实现继承 python的多态 封装 含义: 1.把对象的属性和方法结合成一个独立的单位, ...
- JAVA的三大特征 封装继承多态- 简单总结
简单总结一下 封装-即从很多类的抽取相同的代码 写在一个类里. 好处是 代码的重用,安全. 继承-减少代码的书写. 其好处也是 代码的重用. 多态- 把不同的子类对象都当作父类来看,可以屏蔽不同子类对 ...
- C语言设计模式-封装-继承-多态
快过年了,手头的工作慢慢也就少了,所以,研究技术的时间就多了很多时间,前些天在CSDN一博客看到有大牛在讨论C的设计模式,正好看到了,我也有兴趣转发,修改,研究一下. 记得读大学的时候,老师就告诉我们 ...
随机推荐
- 结合NGUI做的手机拍照(可自定义相框)
原地址:http://www.unity蛮牛.com/thread-18220-1-1.html 在次此之前我们先要了解一下下面的我要讲的几个内容: 一.为什么要用NGUI,因为NGUI的可以做屏幕自 ...
- 设置VMWARE通过桥接方式使用主机无线网卡上网
原文:http://www.cnblogs.com/liongis/p/3265458.html 环境:WIN7旗舰版,台式机,U盘无线上网卡. 虚拟软件:VMware9.0,虚拟系统:CentOS6 ...
- ZOJ 2677 Oil Deal(最大生成树)
题意:破坏石油管道,现一直破坏各个管道所要付出的代价,问在有一定money并且要保证剩余的管道为生成树的情况下, 最多能破坏多少个管道,并将他们的编号从小到大输出来 思路:将边从大到小排序,构造生成树 ...
- GCD初步认识
//(1)用异步函数往并发队列中添加任务, //总结:同时开启三个子线程 - (void)test1 { //1.获得全局的并发队列 dispatch_queue_t queue = dispatch ...
- border-radius几种写法的原理剖析
border-radius:40px; border-radius:40px/20px; border-radius:40px 20px; border-radius:40px 20px 10px 5 ...
- lintcode:买卖股票的最佳时机 IV
买卖股票的最佳时机 IV 假设你有一个数组,它的第i个元素是一支给定的股票在第i天的价格. 设计一个算法来找到最大的利润.你最多可以完成 k 笔交易. 注意事项 你不可以同时参与多笔交易(你必须在再次 ...
- 网络安装之Redhat衍生版
GNU/Linux开源,这个意义实在是非常的广泛,目前在distrowatch上表现活跃的300个发行版代表了GNU/Linux的主流,然而细心的Linux爱好者会发现CentOS-based dis ...
- Java-在线聊天系统-线程
一.概述 1.目标:在上一个版本非线程的聊天系统中,出于要不断监听接收新client和接收client发出的信息,把accept()和DataInputStream.readUTF()分别写在了whi ...
- Git教程之安装配置(1)
1.Git是什么? Git是目前世界上最先进的分布式版本控制系统. 2.SVN与Git的最主要的区别? SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以 ...
- 275. H-Index II
题目: Follow up for H-Index: What if the citations array is sorted in ascending order? Could you optim ...