用C实现OOP面向对象编程(1)

简介:

如摘要所说,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++干得漂亮。

目录
相关文章
|
SQL 算法 前端开发
【MybatisPlus】MP解决四种表与实体的映射问题,以及id自增策略
MP解决四种表与实体的映射问题,以及id自增策略
3970 0
【MybatisPlus】MP解决四种表与实体的映射问题,以及id自增策略
|
数据采集 存储 运维
DAMA数据管理知识体系指南(3):数据治理
DAMA:国际数据管理协会,是一个全球性数据管理和业务专业志愿人士组成的非营利协会,是当前国际上在数据治理领域最权威的机构。 DMBOK2则是DAMA组织众多数据管理领域的国际级资深专家编著,深入阐述数据管理各领域的完整知识体系。它是市场上唯一综合了数据管理方方面面的一部权威性著作。 本系列文章,将针对DMBOK中的核心内容进行解读。
DAMA数据管理知识体系指南(3):数据治理
|
人工智能 数据可视化 数据库
低代码平台:技术复杂性的系统简化
低代码平台通过模块化和自动化技术重新定义开发流程,显著缩短应用构建时间并提高协作效率。其核心特性如“一键编程”和“快速迭代”降低了开发复杂度,提供了敏捷开发能力。可视化开发技术通过组件化设计、实时渲染、分布式协作等功能,实现了高效开发和跨平台适配。引擎优化方面,对SQL、功能、模板、图表和切面五大引擎进行了系统性升级,提升了性能和灵活性。此外,低代码平台还融合了AI技术,提供智能代码助手、故障排查、场景化推荐等智能化工具,进一步提升开发体验和效率。插件生态覆盖多行业场景,支持实时数据处理、AI模型训练、图像处理等多种扩展功能,满足不同业务需求。
|
SQL 数据库
INTO SELECT
【11月更文挑战第10天】
621 3
|
存储 人工智能 编解码
在Data-Driven时代下,如何打造下一代智能数据体系?
本文源自2024外滩大会“Data+AI”论坛,由蚂蚁集团数据平台与服务部负责人骆骥演讲整理。文章回顾了数据技术发展历程,指出生成式AI正推动数据技术从成本效率中心向价值中心转变。
|
存储 JSON JavaScript
protobuf抓包,读包
protobuf抓包,读包
470 4
|
存储 缓存 算法
C++从入门到精通:4.6性能优化——深入理解算法与内存优化
C++从入门到精通:4.6性能优化——深入理解算法与内存优化
959 1
|
JavaScript 前端开发 API
Vue 3 中的 Composition API 是什么?它的优势是什么?
Vue 3 中的 Composition API 是什么?它的优势是什么?
|
XML SQL Java
springboot 项目启动报Has been loaded by XML or SqlProvider, ignoring the injection of the SQL的错误的解决方案
springboot 项目启动报Has been loaded by XML or SqlProvider, ignoring the injection of the SQL的错误的解决方案
1472 0
|
JSON JavaScript 数据格式
jquery拼接数据循环一个数据列表
jquery拼接数据循环一个数据列表
176 0