用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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM