日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

c语言如何实现水平和垂直镜像_如何用C语言实现OOP

發布時間:2024/10/6 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c语言如何实现水平和垂直镜像_如何用C语言实现OOP 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

點擊上方藍字關注我們

我們知道面向對象的三大特性分別是:封裝、繼承、多態。很多語言例如:C++和Java等都是面向對象的編程語言,而我們通常說C是面向過程的語言,那么是否可以用C實現簡單的面向對象呢?答案是肯定的!C有一種數據結構叫做結構體(struct)和函數指針,使用結構體和函數指針便可實現面向對象的三大特性。

C語言實現封裝

首先我們先簡單了解一下什么是封裝,簡單的說封裝就是類將屬性和屬性操作封裝在一個不可分割的獨立實體,只提供對外訪問屬性的操作方法。用戶無需知道對象的內部實現細節,但能通過對外提供的接口訪問內部屬性數據。

由于C沒有像C++一樣可以設置類內部數據的訪問權限,所以C的屬性和操作都是公有的,但是我們可以用C的函數指針模仿C++實現簡單的封裝。后續的多態實現也用到C的函數指針。我們知道C++所有的非靜態成員函數會有一個this指針,通過this指針可以訪問所有的成員變量和成員函數。而C可以通過傳入成員變量所在的結構體指針,達到C++ this指針的效果。現在我們構建一個簡單的Bird類,Bird有名稱(Name),顏色(Color),重量(Weight),棲居地(Addr)屬性和對應的操作方法。

enum{
????INVALID_COLOR = 0,
????RED = 1,
????GREEN = 2,
};

struct?Bird{
????char?*Name;
????char?*Addr;
????int?Color;
????int?Weight;

????void?(*SetName)(struct?Bird *Bird, char?*Name);
????void?(*SetAddr)(struct?Bird *Bird, char?*Addr);
????void?(*SetColor)(struct?Bird *Bird, const?int?Color);
????void?(*SetWeight)(struct?Bird *Bird, const?int?Weight);

????char?*(*GetName)(struct?Bird *Bird);
????int?(*GetColor)(struct?Bird *Bird);
};代碼中SetName, SetAddr, SetColor, SetWeight函數指針相當于C++類的成員函數,是Bird類內部數據與外部交互的接口。在C++中this指針是在編譯的時候由編譯器自己加上去的,所以每個接口都有一個struct Bird* 類型形參,該指針的作用相當于C++的this指針,通過該指針可以訪問類內部的所有成員變量和成員函數。接下來就需要實現具體的函數,再在執行構造函數時手動將函數指針指向最終的實現函數。具體成員函數實現源碼如下:void?SetBirdName(struct Bird *Bird, const?char?* const?Name){
????if(Bird == NULL){
????????return;
????}
????Bird->Name = Name;
}

void?SetBirdAddr(struct Bird *Bird, const?char?* const?Addr){
????if(Bird == NULL){
????????return;
????}
????Bird->Addr = Addr;
}

void?SetBirdColor(struct Bird *Bird, const?int?Color){
????if(Bird == NULL){
????????return;
????}
????Bird->Color = Color;
}

void?SetBirdWeight(struct Bird *Bird, const?int?Weight){
????if(Bird == NULL){
????????return;
????}
????Bird->Weight = Weight;
}

char?*GetName(struct Bird *Bird){
????if(Bird == NULL){
????????return?NULL;
????}
????
????return?Bird->Name;
}

int?GetColor(struct Bird *Bird){
????if(Bird == NULL){
????????return?INVALID_COLOR;
????}

????return?Bird->Color;
}那么C++的構造函數和析構函數如何使用C來實現呢?構造函數在創建一個對象實例時自動調用,析構函數則在銷毀對象實例時自動調用,實際上C++的構造函數和析構函數在編譯期間由編譯器插入到源碼中。但是編譯C源碼時,編譯器沒有這種操作,需要我們手動去調用構造函數和析構函數。而且在調用C的構造函數時,需要我們手動將函數指針指向最終的實現函數。在調用C的析構函數時,需要我們手動的釋放資源。

構造函數源碼如下:

void BirdInit(struct Bird *Bird)
{
????if(Bird == NULL){
????????return;
????}
????Bird->SetAddr = SetBirdAddr;
????Bird->SetColor = SetBirdColor;
????Bird->SetName = SetBirdName;
????Bird->SetWeight = SetBirdWeight;

????Bird->GetColor = GetColor;
????Bird->GetName = GetName;

????Bird->SetAddr(Bird, "Guangzhou");
????Bird->SetColor(Bird, RED);
????Bird->SetWeight(Bird, 10);
????Bird->SetName(Bird, "Xiaoming");
}

析構函數源碼如下:

void?BirdDeinit(struct Bird *Bird){
????if(Bird == NULL){
????????return;
????}

????memset(Bird, 0, sizeof(struct Bird));
}至此,C如何實現面向對象的封裝特性已講完,下面看看我們實際運用的效果。int?main(int?argc, char?*argv[]){
????struct?Bird?*Bird?= (struct?Bird?*)malloc(sizeof(struct?Bird));

????BirdInit(Bird); //調用構造函數
????Bird->SetName(Bird, "Lihua"); //更改Bird的名稱
????Bird->SetColor(Bird, GREEN); //更改Bird的顏色
????printf("Bird name: %s, color: %d\n", Bird->GetName(Bird), Bird->GetColor(Bird));
????BirdDeinit(Bird); //調用析構函數
????free(Bird);
????Bird = NULL;

????return?0;
}

在mac上編譯執行結果如下:

C語言實現繼承

我們繼續簡單了解一下什么是繼承,繼承就是使用已存在的類的定義基礎建立新類的技術。新類可以增加新的數據和方法,但不能選擇性的繼承父類。而且繼承是“is a”的關系,比如老鷹是鳥,但是你不能說鳥就是老鷹,因為還有其他鳥類動物也是鳥。因為C語言本身的限制,只能用C實現C++的公有繼承(除非使用C開發新的計算機語言)。在C++使用公有繼承(沒有虛函數),編譯器會在編譯期間將父類的成員變量插入到子類中,通常是按照順序插入(具體視編譯器決定)。說到這里,我們很容易就能想到如何使用C語言實現C++的公有繼承了(不帶虛函數),就是在子類中定義一個父類的成員變量,而且父類的成員變量只能放在最開始的位置。依舊使用上面建立的Bird類作為父類,我們建立一個新的子類Eagle(老鷹),老鷹可以飛翔也吃肉(其他鳥類不一定會飛和吃肉),所以我們建立的子類如下:struct?Eagle
{
????struct?Bird Bird;
????BOOL?Fly;
????BOOL?EateMeat;

????void?(*CanFly)(struct?Bird *Bird, const?BOOL?Fly);
????void?(*CanEateMeat)(struct?Bird *Bird, const?BOOL?EateMeat);
????BOOL?(*IsFly)(struct?Bird *Bird);
????BOOL?(*IsEateMeat)(struct?Bird *Bird);
};
extern?void?EagleInit(struct?Eagle *Eagle);
extern?void?EagleDeinit(struct?Eagle *Eagle);在C++中new一個子類對象,構造函數的調用順序則是從繼承鏈的最頂端到最底端,依次調用構造函數。而delete一個子類對象時,析構函數的調用順序則是從繼承鏈的最底端到最頂端依次調用。按照這個模式,我們子類(Eagle)的構造函數和析構函數就很容易寫了,構造函數和析構函數源碼如下所示:void EagleInit(struct Eagle *Eagle)
{
????if(Eagle == NULL){
????????return;
????}
????BirdInit(&Eagle->Bird);
????Eagle->CanFly = CanFly;
????Eagle->CanEateMeat = CanEateMeat;
????Eagle->IsFly = IsFly;
????Eagle->IsEateMeat = IsEateMeat;

????Eagle->CanFly((struct Bird *)Eagle, TRUE);
????Eagle->CanEateMeat((struct Bird *)Eagle, TRUE);
}

void EagleDeinit(struct Eagle *Eagle)
{
????if(Eagle == NULL){
????????return;
????}
????memset(Eagle, 0, sizeof(struct Eagle));
????BirdDeinit(&Eagle->Bird);
}在子類的構造函數EagleInit中先調用父類的構造函數BirdInit,在子類的析構函數中先釋放子類的資源再調用父類的析構函數BirdDeinit。至此,我們完成了C語言實現C++的公有繼承(不帶虛函數)。

C語言實現多態

所謂多態就是指程序中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在編程時并不確定,而是在程序運行期間才確定,即一個引用變量倒底會指向哪個類的實例對象,該引用變量發出的方法調用到底是哪個類中實現的方法,必須在由程序運行期間才能決定。因為在程序運行時才確定具體的類,這樣,不用修改源程序代碼,就可以讓引用變量綁定到各種不同的類實現上,從而導致該引用調用的具體方法隨之改變,即不修改程序代碼就可以改變程序運行時所綁定的具體代碼,讓程序可以選擇多個運行狀態,這就是多態性。老慣例,我們來看一下C++是如何實現運行時多態的。C++的運行時多態是用虛函數實現的。在C++中有虛函數的類存在一個虛函數表指針vptr指向一個虛函數表。而虛函數表則存放著,虛函數對應的實現函數。我們用C語言實現類似于C++的多態性,可以模仿C++用創建虛函數表和在類中定義一個虛函數表指針實現。但是我們一般不用這樣實現,因為這種實現方式有幾個缺點:

1、添加和刪除一個虛函數時,虛函數表大小要隨著改變,函數在虛函數表里面存放的位置也要隨著改變。

2、會增加類的內存占用空間。

3、多層間接訪問虛函數,增加了運行開銷和系統復雜度。

通過仔細觀察C語言實現繼承我們可以知道,父類的成員變量會全部放入到子類內存空間中。那么我們是否可以把虛函數表直接放在類中呢?這個時候函數指針又發揮作用了!我們可以把多個函數指針放在父類中,就可以在之類構造函數中直接將父類里的函數指針重新指向新的實現函數,這就實現了我們想要的多態性!因為鳥類都會下蛋,所以我們定義一個下蛋的函數LayEggs。

Bird類源碼如下:

struct?Bird{
????char?*Name;
????char?*Addr;
????int?Color;
????int?Weight;

????void?(*SetName)(struct?Bird *Bird, char?*Name);
????void?(*SetAddr)(struct?Bird *Bird, char?*Addr);
????void?(*SetColor)(struct?Bird *Bird, const?int?Color);
????void?(*SetWeight)(struct?Bird *Bird, const?int?Weight);

????char?*(*GetName)(struct?Bird *Bird);
????int?(*GetColor)(struct?Bird *Bird);

????void?(*LayEggs)(struct?Bird *Bird);
};
extern?void?BirdInit(struct?Bird *Bird);
extern?void?BirdDeinit(struct?Bird *Bird);

Bird類構造函數源碼如下:

static?void LayEggs(struct Bird *Bird)
{
????if(Bird == NULL){
????????return;
????}

????printf("bird lay eggs\n");
}

void BirdInit(struct Bird *Bird)
{
????if(Bird == NULL){
????????return;
????}
????Bird->SetAddr = SetBirdAddr;
????Bird->SetColor = SetBirdColor;
????Bird->SetName = SetBirdName;
????Bird->SetWeight = SetBirdWeight;

????Bird->GetColor = GetColor;
????Bird->GetName = GetName;

????Bird->LayEggs = LayEggs;

????Bird->SetAddr(Bird, "Guangzhou");
????Bird->SetColor(Bird, RED);
????Bird->SetWeight(Bird, 10);
????Bird->SetName(Bird, "Xiaoming");
}

Eagle類構造函數源碼如下:

static?void LayEggs(struct Bird *Bird)
{
????if(Bird == NULL){
????????return;
????}

????printf("Eagle lay eggs\n");
}

void EagleInit(struct Eagle *Eagle)
{
????if(Eagle == NULL){
????????return;
????}
????BirdInit(&Eagle->Bird);
????Eagle->CanFly = CanFly;
????Eagle->CanEateMeat = CanEateMeat;
????Eagle->IsFly = IsFly;
????Eagle->IsEateMeat = IsEateMeat;

????Eagle->Bird.LayEggs = LayEggs;

????Eagle->CanFly((struct Bird *)Eagle, TRUE);
????Eagle->CanEateMeat((struct Bird *)Eagle, TRUE);
}在Eagle構造函數中,我們將父類的函數指針指向了新的LayEggs函數,在程序運行期間就會調用新的LayEggs函數。我們修改main函數,觀察運行結果。

main函數修改如下:

int?main(int?argc, char?*argv[])
{
????struct?Bird *Bird = (struct?Bird *)malloc(sizeof(struct?Bird));

????BirdInit(Bird); //調用構造函數
????Bird->SetName(Bird, "Lihua"); //更改Bird的名稱
????Bird->SetColor(Bird, GREEN); //更改Bird的顏色
????printf("Bird name: %s, color: %d\n", Bird->GetName(Bird), Bird->GetColor(Bird));
????Bird->LayEggs(Bird);
????BirdDeinit(Bird); //調用析構函數
????free(Bird);
????Bird = NULL;

????Bird = (struct?Bird *)malloc(sizeof(struct?Eagle));
????struct?Eagle *Eagle = (struct?Eagle *)Bird;
????EagleInit((struct?Eagle *)Bird);
????Bird->SetName(Bird, "Tanmeimei");
????Bird->SetAddr(Bird, "Shanghai");
????Bird->SetColor(Bird, RED);
????printf("Eagle is fly: %d, is eate meat: %d\n", Eagle->IsFly((struct?Bird *)Eagle), Eagle->IsEateMeat((struct?Bird *)Eagle));
????printf("Eagle name is: %s,\n", Bird->GetName(Bird));
????Bird->LayEggs(Bird);
????EagleDeinit((struct?Eagle *)Bird);
????free(Bird);
????Bird = NULL;

????return?0;
}

運行結果如下:

到目前為止,我們已經用C語言實現了封裝、繼承和多態三大面向對象特性!項目源碼:https://gitee.com/C-Cplusplusyiyezhiqiu/wechat-official-account.git

總結

以上是生活随笔為你收集整理的c语言如何实现水平和垂直镜像_如何用C语言实现OOP的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。