相信很多人都看過(guò)設計模式方面的書(shū),大家有什么體會(huì )呢?Bridge,Proxy,Factory這些設計模式都是基于抽象類(lèi)的。使用抽象對象是這里的一個(gè)核心。
其實(shí)我覺(jué)得框架化編程的一個(gè)核心問(wèn)題是抽象,用抽象的對象構建程序的主體框架,這是面向對象編程的普遍思想。用抽象構建骨架,再加上多態(tài)就形成了一個(gè)完整的程序。由于C++語(yǔ)言本身實(shí)現了繼承和多態(tài),使用這樣的編程理念(理念啥意思?跟個(gè)風(fēng),嘿嘿)在C++中是十分普遍的現象,可以說(shuō)Virtual(多態(tài))是VC的靈魂。
但是,使用C語(yǔ)言的我們都快把這個(gè)多態(tài)忘光光了。我常聽(tīng)見(jiàn)前輩說(shuō),類(lèi)?多態(tài)?我們用的是C,把這些忘了吧。很不幸的是,我是一個(gè)固執的人。這么好的東西,為啥不用呢。很高興的,在最近的一些純C代碼中,我看見(jiàn)了C中的多態(tài)!下面且聽(tīng)我慢慢道來(lái)。
1. VC中的Interface是什么
Interface:中文解釋是接口,其實(shí)它表示的是一個(gè)純虛類(lèi)。不過(guò)我所要說(shuō)的是,在VC中的Interface其實(shí)就是struct,查找Interface的定義,你可以發(fā)現有這樣的宏定義:
#Ifndef Interface
#define Interface struct
#endif
而且,實(shí)際上在VC中,如果一個(gè)類(lèi)有Virtual的函數,則類(lèi)里面會(huì )有vtable,它實(shí)際上是一個(gè)虛函數列表。實(shí)際上C++是從C發(fā)展而來(lái)的,它不過(guò)是在語(yǔ)言級別上支持了很多新功能,在C語(yǔ)言中,我們也可以使用這樣的功能,前提是我們不得不自己實(shí)現。
2.C中如何實(shí)現純虛類(lèi)(我稱(chēng)它為純虛結構)
比較前面,相信大家已經(jīng)豁然開(kāi)朗了。使用struct組合函數指針就可以實(shí)現純虛類(lèi)。
例子: typedef struct {
void (*Foo1)();
char (*Foo2)();
char* (*Foo3)(char* st);
}MyVirtualInterface;
這樣假設我們在主體框架中要使用橋模式。(我們的主類(lèi)是DoMyAct,接口具體實(shí)現類(lèi)是Act1,Act2)下面我將依次介紹這些“類(lèi)”。(C中的“類(lèi)”在前面有說(shuō)明,這里換了一個(gè),是使用早期的數組的辦法)
主類(lèi)DoMyAct: 主類(lèi)中含有MyVirtualInterface* m_pInterface; 主類(lèi)有下函數:
DoMyAct_SetInterface(MyVirtualInterface* pInterface)
{
m_pInterface= pInterface;
}
DoMyAct_Do()
{
if(m_pInterface==NULL) return;
m_pInterface->Foo1();
c=m_pInterface->Foo2();
}
子類(lèi)Act1:實(shí)現虛結構,含有MyVirtualInterface st[MAX]; 有以下函數:
MyVirtualInterface* Act1_CreatInterface()
{
index=FindValid() //對象池或者使用Malloc !應該留在外面申請,實(shí)例化
if(index==-1) return NULL;
St[index].Foo1=Act1_Foo1; // Act1_Foo1要在下面具體實(shí)現
St[index].Foo2=Act1_Foo2;
St[index].Foo3=Act1_Foo3;
Return &st [index];
}
子類(lèi)Act2同上。
在main中,假設有一個(gè)對象List。List中存貯的是MyVirtualInterface指針,則有:
if( (p= Act1_CreatInterface()) != NULL)
List_AddObject(&List, p); //Add All
While(p=List_GetObject()){
DoMyAct_SetInterface(p);//使用Interface代替了原來(lái)大篇幅的Switch Case
DoMyAct_Do();//不要理會(huì )具體的什么樣的動(dòng)作,just do it
}
FREE ALL。
在微系統里面,比如嵌入式,通常使用對象池的技術(shù),這個(gè)時(shí)候可以不用考慮釋放的問(wèn)題(對象池預先沒(méi)有空間,使用Attach,在某個(gè)函數中申請一個(gè)數組并臨時(shí)為對象池分配空間,這樣函數結束,對象池就釋放了)
但是在Pc環(huán)境下,由于程序規模比較大,更重要的是一些特殊的要求,使得對象的生命周期必須延續到申請的那個(gè)函數體以外,就不得不使用malloc,實(shí)際上即使在C++中,new對象的自動(dòng)釋放始終是一個(gè)令人頭疼的問(wèn)題,新的標準引入了智能指針。但是就我個(gè)人而言,我覺(jué)得將內存釋放的問(wèn)題完全的交給機器是不可信任的,它只能達到準最佳。
你知道設計Java的垃圾回收算法有多困難嗎?現實(shí)世界是錯綜復雜的,在沒(méi)有先驗條件下,要想得到精確的結果及其困難。所以我說(shuō)程序員要時(shí)刻將free記在心上,有關(guān)程序的健壯性和自我防御將在另外一篇文章中講述。
3.純虛結構的退化
下面我們來(lái)看看如果struct里面僅僅有一個(gè)函數是什么? 這個(gè)時(shí)候如果我們不使用struct,僅僅使用函數指針又是什么? 我們發(fā)現,這樣就退化為普通的函數指針的使用了。
所以說(shuō),有的時(shí)候我覺(jué)得面向對象僅僅是一種形式,而不是一種技術(shù)。是一種觀(guān)點(diǎn),而不是一種算法。但是,正如炭,石墨和鉆石的關(guān)系一樣,雖然分子式都是C,但是組成方法不一樣,表現就完全不一樣了!
有的時(shí)候,我們經(jīng)常被編程中瑣碎的事情所煩惱,而偏離了重心,其實(shí)程序可進(jìn)化的特性是很重要的。有可能,第一次是不成功的,但是只要可進(jìn)化,就可以發(fā)展。
4.進(jìn)階――類(lèi)結構樹(shù),父類(lèi)不是純虛類(lèi)的類(lèi)
前面僅僅講的是父類(lèi)是純虛結構的情況 (面向對象建議的是所有類(lèi)的基類(lèi)都是從純虛類(lèi)開(kāi)始的), 那么當類(lèi)層次比較多的情況下,出現父類(lèi)不是純虛結構怎么辦呢。嘿嘿,其實(shí)在C中的實(shí)現比C++要簡(jiǎn)單多了。因為C中各個(gè)函數是分散的。
在這里使用宏定義是一個(gè)很好的辦法:比如兩個(gè)類(lèi)Act1,ActByOther1“繼承”Act1:
MyVirtualInterface* ActByOther1_CreatInterface()
{
index=FindValid() //對象池或者使用Malloc
if(index==-1) return NULL;
St[index].Foo1= ActByOther1_Foo1; // Act1_Foo1要在下面具體實(shí)現
St[index].Foo2= ActByOther1_Foo2;
St[index].Foo3= ActByOther1_Foo3;
Return &st [index];
}
#define ActByOther1_Foo1 Act1_Foo1 //這就是繼承 嘿嘿
ActByOther1_Foo2(){} // 可以修改其實(shí)現
ActByOther1_DoByOther() {} //當然就可以添加新的實(shí)現咯
5.實(shí)例――可以參見(jiàn)H264的源碼,其中NalTool就是這樣的一個(gè)純虛結構。
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請
點(diǎn)擊舉報。