如摘要所说,C语言不支持OOP(面向对象的编程)。并这不意味着我们就不能对C进行面向对象的开发,只是过程要复杂许多。原来以C++的许多工作,在C语言中需我们手动去完成。

博主将与大家一起研究一下如下用C语言实现面象对象的编程。

面向对象的三大特性:封装、继承、多态

我们要达到的目的如下:

Animal是动物,有两个方法:Eat()吃,Breed()繁衍。

Bird与Mammal都是Animal,Mammal是哺乳动物。

Penguin是企鹅,企鹅是Bird,企鹅不会飞。

Swallow是燕子,是Bird,会飞。

Bat是蝙蝠,是Mammal,会飞。

Tiger是老虎,是Mammal,不会飞。

Plane是飞机,会飞。它不是动物。

从上面的类继承关系来看,由于Swallow, Bat, Plane会飞,所以它们都继承了IFly接口。

首先我们用C++的类来实现上面的关系:

class Animal {
public:
virtual void Eat() = 0;
virtual void Breed() = 0;
}; class Bird : public Animal {
public:
virtual void Breed() {
cout << "蛋生" << endl;
}
}; class Mammal : public Animal {
public:
virtual void Breed() {
cout << "胎生" << endl;
}
}; class IFly {
public:
virtual void Fly() = 0;
}; class Penguin : public Bird {
public:
virtual void Eat() {
cout << "企鹅吃鱼" << endl;
}
}; class Swallow : public Bird , public IFly {
public:
virtual void Eat() {
cout << "燕子吃虫子" << endl;
}
virtual void Fly() {
cout << "燕子飞呀飞" << endl;
}
}; class Bat : public Mammal, public IFly {
public:
virtual void Eat() {
cout << "蝙蝠吃飞虫" << endl;
}
virtual void Fly() {
cout << "蝙蝠飞呀飞" << endl;
}
}; class Tiger : public Mammal {
virtual void Eat() {
cout << "老虎吃肉" << endl;
}
}; class Plane : public IFly {
public:
virtual void Fly() {
cout << "飞机飞过天空" << endl;
}
};

用下面的main.cpp来测试它们的继承效果:

int main()
{
Penguin *penguin = new Penguin;
Swallow *swallow = new Swallow;
Bat *bat = new Bat;
Tiger *tiger = new Tiger;
Plane *plane = new Plane; Animal* animals[4] = {penguin, swallow, bat, tiger};
IFly* flies[3] = {swallow, bat, plane}; for (int i = 0; i < 4; ++i) {
animals[i]->Eat();
animals[i]->Breed();
} cout << "-------------" << endl;
for (int i = 0; i < 3; ++i)
flies[i]->Fly(); delete penguin;
delete swallow;
delete bat;
delete tiger;
delete plane; return 0;
}

执行的效果是:

企鹅吃鱼
蛋生
燕子吃虫子
蛋生
蝙蝠吃飞虫
胎生
老虎吃肉
胎生
-------------
燕子飞呀飞
蝙蝠飞呀飞
飞机飞过天空

上面演示的就是C++的多态功能。


多态这个特性给我们软件灵活性带来了很大的便利。由于某此限制,如硬件资源不够充裕、开发环境不支持C++等原理,我们不能使用C++。

那么我们下面要讨论的是用C来重新实现上面的多态功能。

main.c大致是这样子的:

int main()
{
Object* penguin = Penguin_New();
Object* swallow = Swallow_New();
Object* bat = Bat_New();
Object* tiger = Tiger_New();
Object* plane = Plane_New(); Object* animal[4] = {penguin, swallow, bat, tiger}; IFly* flies[3] = {NULL};
flies[0] = Swallow_AsIFly(swallow);
flies[1] = Bat_AsIFly(bat);
flies[2] = Plane_AsIFly(plane); for (int i = 0; i < 4; ++i) {
Animal_Eat(animal[i]);
Animal_Breed(animal[i]);
} for (int i = 0; i < 4; ++i) {
IFly_Fly(flies[i]);
} Penguin_Delete(penguin);
Swallow_Delete(swallow);
Bat_Delete(bat);
Tiger_Delete(tiger);
Plane_Delete(plane); return 0;
}

上面编译时需要加 "-std=c99" 才能通过编译。


博主已实现了上面的Demo,代码已提交到:http://git.oschina.net/hevake_lcj/C_OOP_DEMO

该Demo实现了OOP的类继承、多态的特性。继承只支持单继承,还没有实现接口功能。

每个对象由三部分组成:info, data, func

  • info,类信息,存储该对象的:类型ID、虚函数表地址

  • data,对象的数据

  • func,虚函数指针

如下为 info 的定义:

typedef struct {
uint32_t tag; //! 高16位为TAG,低16位为class_id
void* vfun; //! 虚函数结构体地址
} class_info_t;

例如 Animal 类的定义,见 animal_def.h :

typedef struct {
int health;
} Animal_Data; typedef struct {
void (*Eat)();
void (*Breed)();
} Animal_Func; typedef struct {
class_info_t info;
Animal_Data data;
Animal_Func func;
} Animal;

结构图:

<明天再写>

即将讨论话题:

- 如何表述类的继承关系?

- 为什么要将data与func分开?

博主自己测试了一下,结果是:

$ ./c_oop_demo
start
企鹅吃鱼
蛋生
燕子吃虫子
蛋生
蝙蝠吃飞虫
胎生
老虎要吃肉
胎生
end

从上看来,已达到了预期的多态效果。

C++与C的比较

居说C++编译出来的可执行文件远多于C。于是博主对比了一下c_oop_demo与C++编译的同功能的可执行文件cpp_demo。博主惊讶地发现 c_oop_demo 的文件大小既还比 cpp_demo大一点。况且上面的 c_oop_demo 还没有实现接口功能呢,要是实现了,那不更大?这不由令博主对用C实现OOP,以为可以节省空间的想法大为失望。

看来,在实现同样的oop功能下,C++编译出的输出文件比自己手把手写的c_oop_demo要小,说明C++在这方便做了不少的优化。相比之下,C++用50多行的代码实现的功能,用C(博主亲自统计的)居然要写近1000行代码。代码的可维护性远不及C++。说C++生成的文件庞大,真是冤枉了C++。用C完成同等功还不如C++干得漂亮。

https://yq.aliyun.com/articles/33326?spm=5176.100239.blogrightarea33100.17.9NBJrk

用C实现OOP面向对象编程(1)的更多相关文章

  1. Java实现OOP(面向对象编程)

    一.对象的综述 面向对象编程(OOP)具有多方面的吸引力.对管理人员,它实现了更快和更廉价的开发与维护过程.对分析与设计人员,建模处理变得更加简单,能生成清晰.易于维护的设计方案.对程序员,对象模型显 ...

  2. 一百零六、SAP的OOP面向对象编程,OO-ALV的简介

    面向对象编程,如图 基本概念: 1.对象(Object)是一个现实实体的抽象.一个对象可被认为是一个把数据(属性)和程序(方法)封装在一起的实体,这个程序产生该对象的动作或对它接受到的外界信号的反应. ...

  3. Swift -POP( 面向协议编程)与OOP(面向对象编程)

    面向协议编程(Protocol Oriented Programming,简称POP),是Swift的一种编程范式,Apple于2015年WWDC提出的,如果大家看Swift的标准库,就会看到大量PO ...

  4. (转)OOP(面向对象编程)的几大原则

    文章转载自:http://blog.csdn.net/anders_zhuo/article/details/8949566 设计模式遵循的一般原则: 1.开-闭原则(Open-Closed Prin ...

  5. OOP(面向对象编程)的一些特性

    接口:接口是把公共实例(非静态)方法和属性结合起来,以封装特定功能的一个集合.一旦定义了接口,就可以在类中实现它.接口注意事项:接口不能单独存在.不能像实例化一个类那样实例化接口.另外,接口不能包含实 ...

  6. OOP面向对象编程(下)

    我们怎么去模拟重载,在javasceipr中我们可以通过参数的类型区别或者数量的区别,来去让同样一个函数名字,可以根据不同的参数列表的情况来去调用相应的函数. javascript中函数类型是不确定的 ...

  7. javascript OOP 面向对象编程

    Pseudo-class declaration 原文地址:http://javascript.info/tutorial/pseudo-classical-pattern#pseudo-class- ...

  8. 重学C++ (十一) OOP面向对象编程(2)

    转换与继承 本节主要须要区分的是: 基类和派生类的转换: 引用(指针)的转换和对象的转换. 1.每一个派生类对象包括一个基类部分.因此.能够像使用基类对象一样在派生类对象上执行操作. 基于这一点,能够 ...

  9. 洗礼灵魂,修炼python(31)--面向对象编程(1)—面向对象,对象,类的了解

    面向对象 1.什么是面向对象 (图片来自网络) 哈哈,当然不是图中的意思. 1).面向对象(Object Oriented,OO)是软件开发方法.利用各大搜索引擎得到的解释都太官方,完全看不懂啥意思对 ...

随机推荐

  1. 微信小程序唤起其他微信小程序 / 移动应用App唤起小程序

    微信小程序唤起其他微信小程序 / 移动应用App唤起小程序 1. 微信小程序唤起微信小程序 小程序唤起其他小程序很简单 先上链接 小程序跳转小程序 Navigator组件 推荐使用 小程序跳转小程序 ...

  2. lucene 7.x 排序

    一.创建索引 @Test public void indexCreate() throws IOException { //创建分词器 Analyzer analyzer = new Standard ...

  3. git merge与rebase

    参考这篇文章 Git 之 merge 与 rebase 的区别  文章2 另外,使 rebase出现冲突后,先修改冲突,然后git add 某文件(我使用add  .经常有问题),然后git reba ...

  4. react持续记录零散笔记

    根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff 这些生命周期在深入项目开发阶段是非常重要的 ...

  5. gcc/g++ 的参数总结(二)

    gcc 参数总结 如果是 c++,直接将 gcc 改为 g++ 即可. 1. gcc 编译流程 预处理,Pre-Processing:gcc -E test.c -o test.i //.i文件 编译 ...

  6. Xbin-Store(分布式商城)项目所用Linux服务系列 FastDFS安装(五)

    系列 Xbin-Store(分布式商城)项目所用Linux服务系列 MySQL安装(一) Xbin-Store(分布式商城)项目所用Linux服务系列 Redis集群安装(二) Xbin-Store( ...

  7. WPF 获取鼠标屏幕位置、窗口位置、控件位置

    原文:WPF 获取鼠标屏幕位置.窗口位置.控件位置 public struct POINT { public int X; public int Y; public POINT(int x, int ...

  8. WPF中StringFormat的用法--显示特定位数的数字

    原文:WPF中StringFormat的用法--显示特定位数的数字 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/huangli321456/art ...

  9. Windows下程序打包发布时的小技巧(使用Dependency Walker侦测不理想,改用VS自带的dumpbin则万无一失,还可查看dll导出的函数)

    Windows下开发的应用程序在发布时,需要将其依赖的一些动态链接库一起打进安装包里面去.这个时候,快速确定这个程序到底依赖哪些动态链接库变得非常重要.很久以前写过一篇关于Qt程序安装包制作的博客,里 ...

  10. XDEBUG+PHPSTORM 开发 调试

    原文:XDEBUG+PHPSTORM 开发 调试 XDEBUG+PHPSTORM 开发 调试 在我们开发过程中.我们如果经常性的echo.dump效率很低.所以我们就可以使用xdebug进行断点调试. ...