關于C語言中繼承和多態的實現

以下的內容綜合了多篇文章,加上一點自己的理解而成。目的為了給自己閱讀他們文章后做一個筆記。在末尾給出了這些文章的地址。

??? 多態的實現可以采用以下幾種方式:
??? (1)使用 vod * (萬能指針)來實現“編譯時多態”。
??? (2)使用函數指針來實現“運行時多態”。
??? (3)使用型如struct struct_name{

????????????? ...............................
????????????? char temp[0]; //或者char *temp;
????????? };
這種形式。

對于(1)舉例如下:

void HandleMsg(unsinged int id, void *p)
{
??? Msg1 *p1;
??? Msg2 *p2;

??? switch(id)
??? {
??? case key1:
??????? p1 = (Msg1*)p;
??????? //do something
??????? break;
??? case key2:
??????? p2 = (Msg2*)p;
??????? //do something
??????? break;
??? default:
??????? break;
??? }
}

??? 這個例子也許不能說明函數的編譯時多態,因為沒有使用函數指針與vod * 之間轉換來轉換去。沒有舉這類例子,是因為想避免這種用法。
??? 對于(2)舉例如下:
#ifndef C_Class
?????? #define C_Class struct
#endif

C_Class A {

?????? C_Class A *A_this;
?????? void (*Foo)(C_Class A *A_this);
?????? int a;

?????? int b;

};

C_Class B{?????????????? //B繼承了A

?????? C_Class B *B_this;? //順序很重要
?????? void (*Foo)(C_Class B *Bthis); //虛函數
?????? int a;
?????? int b;
?????? int c;
};?

void B_F2(C_Class B *Bthis)
{
?????? printf("It is B_Fun/n");
}

void A_Foo(C_Class A *Athis)
{

?????? printf("It is A.a=%d/n",Athis->a);//或者這里
}

void B_Foo(C_Class B *Bthis)
{
??? printf("It is B.c=%d/n",Bthis->c);
}

void A_Creat(struct A* p)
{
?? p->Foo=A_Foo;
?? p->a=1;
?? p->b=2;
?? p->A_this=p;
}?

void B_Creat(struct B* p)
{
?? p->Foo=B_Foo;
?? p->a=11;
?? p->b=12;???
?? p->c=13;
?? p->B_this=p;

}

int main(int argc, char* argv[])
{

?????? C_Class A *ma,a;
?????? C_Class B *mb,b;

?????? A_Creat(&a);//實例化
?????? B_Creat(&b);

?????? mb=&b;
?????? ma=&a;

?????? ma=(C_Class A*)mb;//引入多態指針

?????? printf("%d/n",ma->a);//可惜的就是 函數變量沒有private

?????? ma->Foo(ma);//多態
?????? a.Foo(&a);//不是多態了
?????? B_F2(&b);//成員函數,因為效率問題不使用函數指針
?????? return 0;
}


??? 在C中實現繼承。
??? 對于例(1)中,有個重大的缺點,那就是缺乏類型安全。那么下面就可以使用繼承來實現保證類型安全。

typedef struct tagT_MsgHeader {
??? int id;
??? //...
}MsgHeader;

typedef struct tagT_Msg1 {
??? MsgHeader?????????? h;
??? int???????????????? a;
??? int???????????????? b;
}Msg1;

typedef struct tagT_Msg2 {
??? MsgHeader h;
??? int?????? c;
??? int?????? d;
}Msg2;

然后再重新定義消息處理函數:

void HandleMsg(MsgHeader *ph)
{
??? Msg1 *p1;
??? Msg2 *p2;

??? switch(ph->id)
??? {
??? case key1:
??????? p1 = (Msg1*)(ph);
??????? //do something
??????? break;
??? case key2:
??????? p2 = (Msg2*)ph;
??????? //do something
??????? break;
??? default:
??????? break;
??? }
}

通過繼承保證了類型安全,但是為了保證繼承后的結構體靈活性,繼承的變量MsgHeader h不能作為第一個成員變量,那么p1 =

(Msg1*)(ph)這種強制轉換就無法得到正確的p1的地址,不過通過定義一個宏來實現這個:

#define CONTAINING_RECORD(address, type, field) ((type *)( /
????????????????????????????????????????????????? (PCHAR)(address) - /
????????????????????????????????????????????????? (UINT_PTR)(&((type *)0)->field)))
??? 這個類似的宏定義在linux中最常見。這樣傳入的MsgHeader 的指針經過宏處理,無論MsgHeader h在結構體的那個位置,均能獲得繼承后的結構體的首地址,這樣再來強制轉換就沒有問題了。

順便解釋一下這個宏:
??? #define CONTAINING_RECORD(address, type, field) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

??? 先看&((type *)0)->field,它把“0”強制轉化為指針類型,則該指針指向“0”(數據段基址)。因為指針是“type *”型的,所以可取到以“0”為基地址的一個type型變量field域的地址,這個地址也就等于field域到結構體基地址的偏移字節數。當前地址減去偏移地址便得出該結構體的地址。轉換為(type *)型的指針。


??? 在c中實現純虛類,可以通過在結構體使用函數指針成員來實現。

//------------------結構體中的函數指針類似于聲明子類中必須實現的虛函數-------------
typedef struct
?{
? void? (*Foo1)();
? char? (*Foo2)();
? char*? (*Foo3)(char* st);
?}MyVirtualInterface;
//---------------------------------------------------------------------------------

//------------------類似于純虛類的定義---------------------------------------------
?MyVirtualInterface* m_pInterface;
?
?DoMyAct_SetInterface(MyVirtualInterface* pInterface)
?{
? m_pInterface= pInterface;
?}

?void oMyAct_Do()
?{
? if(m_pInterface == NULL) return;
? m_pInterface->Foo1();
? c = m_pInterface->Foo2();
?}
//---------------------------------------------------------------------------------

//--------------------------子類一-------------------------------------------------
MyVirtualInterface? st[MAX];

//接著定義一些需要實現的函數 Act1_Foo1,Act1_Foo2,Act1_Foo3

MyVirtualInterface* Act1_CreatInterface()
{
?index = FindValid() //對象池或者使用Malloc!應該留在外面申請,實例化
?if(index == -1) return NULL;
?st[index].Foo1 = Act1_Foo1; // Act1_Foo1要在下面具體實現
?st[index].Foo2 = Act1_Foo2;
?st[index].Foo3 = Act1_Foo3;
?Return &st[index];
}
//-----------------------------------------------------------------------------------

//--------------------------主函數---------------------------------------------------
if((p = Act1_CreatInterface()) != NULL)
{
?List_AddObject(&List, p); //Add All
?While(p = List_GetObject())
?{
??DoMyAct_SetInterface(p);//使用Interface代替了原來大篇幅的Switch Case
??DoMyAct_Do();//不要理會具體的什么樣的動作,just do it
??? }
}
//-----------------------------------------------------------------------------------

如果父類不為純虛類的類,那么在子類中通過宏來實現虛函數

MyVirtualInterface* ActByOther1_CreatInterface()
{
?index = FindValid() //對象池或者使用Malloc
?if(index == -1) return NULL;
?St[index].Foo1 = ActByOther1_Foo1; // Act1_Foo1要在下面具體實現
?St[index].Foo2 = ActByOther1_Foo2; //父類中已經實現了的
?St[index].Foo3 = ActByOther1_Foo3; //父類中已經實現了的
?Return &st [index];
}

#define ActByOther1_Foo1 Act1_Foo1? //這就是繼承
ActByOther1_DoByOther() {}?? //當然就可以添加新的實現

引用:

[1]http://blog.csdn.net/baoxingbo/articles/56406.aspx

[2]http://hi.baidu.com/blue_never_died/blog/item/b05f242d389bb734349bf7dd.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/452409.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/452409.shtml
英文地址,請注明出處:http://en.pswp.cn/news/452409.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

邊界測試

邊界條件邊界測試是單元測試中最后的也可能是最重要的任務。 軟件常常在它的邊界上失效,例如,處理n元數組的第n個元素時,或做到i次循環中的第i次重復時,往往會發生錯誤。 使用剛好小于、剛好等于和剛好大于最大值或最小值的數據結…

阿里云上Kubernetes集群聯邦

摘要: kubernetes集群讓您能夠方便的部署管理運維容器化的應用。但是實際情況中經常遇到的一些問題,就是單個集群通常無法跨單個云廠商的多個Region,更不用說支持跨跨域不同的云廠商。這樣會給企業帶來一些擔憂,如何應對可用區級別…

缺氧游戲計算機,缺氧PC最低什么配置一覽 你覺得高嗎

缺氧PC最低什么配置一覽,你覺得高嗎。游戲對于電腦有不同程度的要求,缺氧這款游戲也有著自己的配置要求,看看下面的缺氧PC最低什么配置一覽,你的硬件夠得上嗎。缺氧最低配置:首先公布的是官 方配置需求,目前…

Diango博客--14.使用 Django 項目中的 ORM 編寫偽造測試數據腳本

文章目錄0.思路引導1.腳本目錄結構2.使用 Faker 快速生成測試數據3.批量生成測試數據4.執行腳本5.效果展示0.思路引導 1)為了防止博客首頁展示的文章過多以及提升加載速度,可以對文章列表進行分頁展示。 2)不過這需要比較多的文章才能達到分…

基于Sql Server 2008的分布式數據庫的實踐

配置Sql Server 2008(Win7) 1.打開SQL server2012,使用windows身份登錄 2.登錄后,右鍵選擇“屬性”。左側選擇“安全性”,選中右側的“SQL Server 和 Windows 身份驗證模式”以啟用混合登錄模式 3.選擇“連接”&#x…

橫向技術分析C#、C++和Java優劣

本文將從技術人員的角度橫向分析C#、C和Java優劣,其實選擇Java陣營還是.NET陣營,大家可以根據自己的實際需要來確定。 C#誕生之日起,關于C#與Java之間的論戰便此起彼伏,至今不輟。拋卻Microsoft與Sun之間的恩怨與口角,…

軟件測試中的存根程序

存根程序用來代替被測試的模塊所調用的模塊,因此存根程序也稱為“虛擬子程序”,它利用被它代替的模塊的接口,只做盡可能少的數據操作。

計算機網絡應用云計算,計算機網絡云計算的類型

原標題:計算機網絡云計算的類型隨著現代計算機網絡技術的不斷發展,越來越多的與計算機網絡有關的現代化技術得以出現,并且有著廣泛的應用,其中云計算技術就是比較常見的一種,在實際應用中發揮著較高的價值。在信息時代…

sublime_text快捷鍵

1、注釋:選中文本后,CTRL / 2、CTRL N,CTRLS,保存成.html文件后,只需要輸入感嘆號!,然后tab鍵,即可打印出基本的html格式!轉載于:https://www.cnblogs.com/JAVA-STUDYER/p/855040…

Diango博客--15.通過 Django Pagination 實現簡單分頁(一)

文章目錄0.思路引導1.Paginator 類的常用方法2.用 Paginator 給文章列表分頁3.在模板中設置分頁導航4.效果展示0.思路引導 1)當博客上發布的文章越來越多時,通常需要進行分頁顯示,以免所有的文章都堆積在一個頁面,影響用戶體驗。…

SpringMVC 測試 mockMVC

SpringMVC測試框架 基于RESTful風格的SpringMVC的測試,我們可以測試完整的Spring MVC流程,即從URL請求到控制器處理,再到視圖渲染都可以測試。 一 MockMvcBuilder MockMvcBuilder是用來構造MockMvc的構造器,其主要有兩個實現&…

自頂向下和自底向上測試的優缺點

自頂向下測試方法的主要優點是不需要測試驅動程序,能夠在測試階段的早期實現并驗證系統的主要功能,而且能在早期發現上層模塊的接口錯誤。 自頂向下測試方法的主要缺點是需要存根程序,可能遇到與此相聯系的測試困難,低層關鍵模塊中…

C++ class中的靜態(static)成員

C class中的靜態(static)成員 (1) 靜態數據成員 ①一般地靜態數據成員在該類定義之外被初始化,如同一個成員函數被定義在類定義之外一樣。在這種定義中的靜態成員的名字必須被其類名限定修飾,例如下面是_interestRate的初始…

用計算機彈可惜不是你,可惜不是你 還是幸虧不是你

一、 你沒有再挽留 我也沒有再回頭 就這樣 無風無雨也無晴 無疾而終二、 是我孤陋寡聞不知你心有人三、 如果作業有葬禮,全體學生定當盛裝出席.四、 縱使我有千般好 你也看不到 因為你沒有一雙愛我的眼睛五、 原來暫時共你沒緣分 來年先會變得更合襯六、 真的別回頭 你有未來 你…

PHP 完整實戰23種設計模式

PHP實戰創建型模式 單例模式 工廠模式 抽象工廠模式 原型模式 建造者模式 PHP實戰結構型模式 橋接模式 享元模式 外觀模式 適配器模式 裝飾器模式 組合模式 代理模式 過濾器模式 PHP實戰行為型模式 模板模式 策略模式 狀態模式 觀察者模式 責任鏈模式 訪問者模…

Diango博客--16.穩定易用的 Django 分頁庫,完善分頁功能(二)

文章目錄0.思路引導1.分頁效果概述2.分頁思路3.Django 第三方拓展:django-pure-pagination4.自定義模板0.思路引導 1)在前面我們通過 Django Pagination 實現簡單分頁 中,我們實現了一個簡單的分頁導航。但效果有點差強人意,我們…

回歸測試

在集成測試過程中,每當一個新模塊結合進來時,程序就發生了變化:建立了新的數據流路徑,可能出現了新的I/O操作,激活了新的控制邏輯。在集成測試的范疇中,回歸測試是指重新執行已經做過的測試的某個子集&…

不同的寫法 其中 1 2 (試了下 沒有效果 ,先記載這里把)

轉載于:https://www.cnblogs.com/kaibindirver/p/9145455.html

美國西北大學 計算機工程專業排名,[轉載]美國西北大學計算機工程研究生最新專業排名...

對于打算去美國西北大學讀研究生的學生來講,美國西北大學研究生申請要求及美國西北大學研究生專業介紹是學生最關心的問題。本文香港介紹美國西北大學研究生申請要求及美國西北大學研究生的專業介紹,幫助更多的學生更好的了解美國西北大學。2016年西北大…

析構函數virtual與非virtual區別

作為通常的原則,如果一個類定義了虛函數,那么它的析構函數就應當是virtual的。因為定義了虛函數則隱含著:這個類會被繼承,并且會通過基類的指針指向子類對象,從而得到多態性。 這個類可能會被繼承,并且會…