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

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

  • 相关阅读:
    PowerShell美化
    Open Live Writer
    Docker学习笔记
    [Caliburn.Micro专题][1]快速入门
    Git
    自助报表是什么东西?能不能利用自助报表把报表事务推给业务人员?
    什么是报表工具?和 EXCEL 有什么区别?
    报表为什么会没完没了?怎么解决这个问题?
    How Python Handles Big Files
    完成一个预测建模项目一般需要多长时间?
  • 原文地址:https://www.cnblogs.com/findumars/p/6350092.html
Copyright © 2020-2023  润新知