用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++干得漂亮。

目录
相关文章
|
1月前
|
人工智能 安全 Linux
OpenClaw Skill开发保姆级指南:访谈式生成工具+阿里云/本地部署+大模型API完整配置教程
OpenClaw Skill开发并不需要编程基础,核心在于清晰的任务描述与标准化流程。通过访谈式生成工具,新手可以通过简单对话快速制作专属技能,完全避免第三方工具的安全风险,同时实现任务执行的高度个性化与稳定化。2026年全平台部署方案成熟,阿里云云端与本地三系统均可快速搭建环境,阿里云千问API与免费Coding Plan API提供灵活的AI能力支持。掌握Skill开发,意味着拥有完全可控、持续进化、高效稳定的个人AI工具集,让OpenClaw真正贴合个人需求,实现效率与安全性的双重提升。
1032 0
|
Linux C语言 C++
C/C++进程超详细详解【下部分】(系统性学习day8)
C/C++进程超详细详解【下部分】(系统性学习day8)
|
SQL 算法 前端开发
【MybatisPlus】MP解决四种表与实体的映射问题,以及id自增策略
MP解决四种表与实体的映射问题,以及id自增策略
4150 0
【MybatisPlus】MP解决四种表与实体的映射问题,以及id自增策略
|
数据采集 存储 运维
DAMA数据管理知识体系指南(3):数据治理
DAMA:国际数据管理协会,是一个全球性数据管理和业务专业志愿人士组成的非营利协会,是当前国际上在数据治理领域最权威的机构。 DMBOK2则是DAMA组织众多数据管理领域的国际级资深专家编著,深入阐述数据管理各领域的完整知识体系。它是市场上唯一综合了数据管理方方面面的一部权威性著作。 本系列文章,将针对DMBOK中的核心内容进行解读。
DAMA数据管理知识体系指南(3):数据治理
|
人工智能 数据可视化 数据库
低代码平台:技术复杂性的系统简化
低代码平台通过模块化和自动化技术重新定义开发流程,显著缩短应用构建时间并提高协作效率。其核心特性如“一键编程”和“快速迭代”降低了开发复杂度,提供了敏捷开发能力。可视化开发技术通过组件化设计、实时渲染、分布式协作等功能,实现了高效开发和跨平台适配。引擎优化方面,对SQL、功能、模板、图表和切面五大引擎进行了系统性升级,提升了性能和灵活性。此外,低代码平台还融合了AI技术,提供智能代码助手、故障排查、场景化推荐等智能化工具,进一步提升开发体验和效率。插件生态覆盖多行业场景,支持实时数据处理、AI模型训练、图像处理等多种扩展功能,满足不同业务需求。
|
SQL 数据库
INTO SELECT
【11月更文挑战第10天】
1058 3
|
存储 人工智能 编解码
在Data-Driven时代下,如何打造下一代智能数据体系?
本文源自2024外滩大会“Data+AI”论坛,由蚂蚁集团数据平台与服务部负责人骆骥演讲整理。文章回顾了数据技术发展历程,指出生成式AI正推动数据技术从成本效率中心向价值中心转变。
|
存储 JSON JavaScript
protobuf抓包,读包
protobuf抓包,读包
550 4
|
存储 缓存 算法
C++从入门到精通:4.6性能优化——深入理解算法与内存优化
C++从入门到精通:4.6性能优化——深入理解算法与内存优化
1039 1
|
算法 Java 索引
【算法系列篇】滑动窗口-2
【算法系列篇】滑动窗口-2