輕量級的面向對象C語言編程框架介紹.doc_第1頁
輕量級的面向對象C語言編程框架介紹.doc_第2頁
輕量級的面向對象C語言編程框架介紹.doc_第3頁
輕量級的面向對象C語言編程框架介紹.doc_第4頁
輕量級的面向對象C語言編程框架介紹.doc_第5頁
已閱讀5頁,還剩12頁未讀 繼續(xù)免費閱讀

VIP免費下載

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

輕量級的面向對象C語言編程框架LW_OOPC介紹金永華、陳國棟2010/03/02摘要:本文介紹一種輕量級的面向對象的C語言編程框架:LW_OOPC。LW_OOPC是Light-Weight Object-Oriented Programming in(with) C的縮寫,總共一個.h文件,20個宏,約130行代碼,非常的輕量級,但卻很好的支持了很多面向對象的特性,比如繼承、多態(tài),可以優(yōu)美的實現(xiàn)面向接口編程。這個框架系由臺灣的高煥堂先生以及他的MISOO團隊首創(chuàng),之后由我繼續(xù)改進優(yōu)化,最后,經(jīng)高煥堂同意以LGPL協(xié)議開源(開源網(wǎng)址參見后文)。用C語言實現(xiàn)OO?我沒聽錯嗎?這聽起來真是太瘋狂了! 大家都知道,C+支持了面向對象和面向泛型編程,比C要更強大些。那么,為什么要在C語言中實踐面向對象呢?為什么不直接使用C+呢?為什么要用面向對象?面向過程方式開發(fā)的系統(tǒng),代碼復雜,耦合性強,難以維護,隨著我們所要解決的問題越來越復雜,代碼也變得越來越復雜,越來越難以掌控,而面向對象改變了程序員的思維方式,以更加符合客觀世界的方式來認識世界,通過合理的運用抽象、封裝、繼承和多態(tài),更好的組織程序,從而很好地應對這種復雜性。 為什么不直接使用C+?C和C+之爭由來已久,可能要持續(xù)到它們中的一種去世_。C語言以其簡潔明快,功能強大的特點,深得開發(fā)人員的喜愛,尤其是在嵌入式開發(fā)領域,C語言更是占據(jù)了絕對老大的地位。在我看來,語言只是工具,作為程序員,我們要做的是:選擇合適的語言,解決恰當?shù)膯栴}。我們要尊重事實,考慮開發(fā)環(huán)境(軟硬件環(huán)境),考慮團隊成員的水平,從商用工程的角度講,選擇團隊成員擅長的語言進行開發(fā),風險要小很多。一些從Java/C#轉到C的程序員們,無法從面向對象切換到面向過程,但又必須與C語言同事們在遺留的C系統(tǒng)上開發(fā)軟件,他們有時會非常困惑:C語言是面向過程的編程語言,如何實踐面向對象,甚至面向接口編程呢?此時,就非常需要在C語言中實現(xiàn)面向對象的手段,而LW_OOPC正是應對這一難題的解決之道。LW_OOPC是什么?簡而言之:LW_OOPC是一套C語言的宏,總共1個.h文件(如果需要內存泄漏檢測支持以及調試打印支持,那么還需要1個.c文件(lw_oopc.c,約145行),20個宏,約130行代碼。LW_OOPC是一種C語言編程框架,用于支持在C語言中進行面向對象編程。LW_OOPC宏介紹下面,先通過一個簡單的示例來展示LW_OOPC這套宏的使用方法。我們要創(chuàng)建這樣一些對象:動物(Animal),魚(Fish),狗(Dog),車子(Car)。顯然,魚和狗都屬于動物,都會動,車子也會動,但是車子不是動物。會動是這些對象的共同特征,但是,顯然它們不屬于一個家族。因此,我們首先考慮抽象出一個接口(IMoveable),以描述會動這一行為特征:INTERFACE(IMoveable) void (*move)(IMoveable* t); / Move行為;INTERFACE宏用于定義接口,其成員(方法)均是函數(shù)指針類型。然后,我們分析Animal,它應該是抽象類還是接口呢?動物都會吃,都需要呼吸,如果僅僅考慮這兩個特征,顯然可以把Animal定為接口。不過,這里,為了展示抽象類在LW_OOPC中如何應用。我們讓Animal擁有昵稱和年齡屬性,并且,讓動物和我們打招呼(sayHello方法),但,我們不允許用戶直接創(chuàng)建Animal對象,所以,這里把Animal定為抽象類:ABS_CLASS(Animal) char name128; / 動物的昵稱(假設小于128個字符) int age; / 動物的年齡 void (*setName)(Animal* t, const char* name); / 設置動物的昵稱 void (*setAge)(Animal* t, int age); / 設置動物的年齡 void (*sayHello)(Animal* t); / 動物打招呼 void (*eat)(Animal* t); / 動物都會吃(抽象方法,由子類實現(xiàn)) void (*breathe)(Animal* t); / 動物都會呼吸(抽象方法,由子類實現(xiàn)) void (*init)(Animal* t, const char* name, int age);/ 初始化昵稱和年齡;ABS_CLASS宏用于定義抽象類,允許有成員屬性。代碼的含義參見代碼注釋。緊接著,我們來定義Fish和Dog類,它們都繼承動物,然后還實現(xiàn)了IMoveable接口:CLASS(Fish) EXTENDS(Animal); / 繼承Animal抽象類 IMPLEMENTS(IMoveable);/ 實現(xiàn)IMoveable接口 void (*init)(Fish* t, const char* name, int age);/ 初始化昵稱和年齡;CLASS(Dog) EXTENDS(Animal);/ 繼承Animal抽象類 IMPLEMENTS(IMoveable);/ 實現(xiàn)IMoveable接口 void(*init)(Dog* t, const char* name, int age);/ 初始化昵稱和年齡;為了讓Fish對象或Dog對象在創(chuàng)建之后,能夠很方便地初始化昵稱和年齡,F(xiàn)ish和Dog類均提供了init方法。下面,我們來定義Car,車子不是動物,但可以Move,因此,讓Car實現(xiàn)IMoveable 接口即可:CLASS(Car) IMPLEMENTS(IMoveable); / 實現(xiàn)IMoveable接口(車子不是動物,但可以Move);接口,抽象類,具體類的定義都已經(jīng)完成了。下面,我們開始實現(xiàn)它們。接口是不需要實現(xiàn)的,所以IMoveable沒有對應的實現(xiàn)代碼。Animal是抽象動物接口,是半成品,所以需要提供半成品的實現(xiàn):/* 設置動物的昵稱*/void Animal_setName(Animal* t, const char* name) / 這里假定name不會超過128個字符,為簡化示例代碼,不做保護(產(chǎn)品代碼中不要這樣寫) strcpy(t-name, name);/* 設置動物的年齡*/void Animal_setAge(Animal* t, int age) t-age = age;/* 動物和我們打招呼*/void Animal_sayHello(Animal* t) printf(Hello! 我是%s,今年%d歲了!n, t-name, t-age);/* 初始化動物的昵稱和年齡*/void Animal_init(Animal* t, const char* name, int age) t-setName(t, name); t-setAge(t, age);ABS_CTOR(Animal)FUNCTION_SETTING(setName, Animal_setName);FUNCTION_SETTING(setAge, Animal_setAge);FUNCTION_SETTING(sayHello, Animal_sayHello);FUNCTION_SETTING(init, Animal_init);END_ABS_CTOR這里出現(xiàn)了幾個新的宏,我們逐個進行講解。ABS_CTOR表示抽象類的定義開始,ABS_CTOR(Animal)的含義是Animal抽象類的“構造函數(shù)”開始。在C語言里邊其實是沒有C+中的構造函數(shù)的概念的。LW_OOPC中的CTOR系列宏(CTOR/END_CTOR,ABS_CTOR/END_ABS_CTOR)除了給對象(在C語言中是struct實例)分配內存,然后,緊接著要為結構體中的函數(shù)指針成員賦值,這一過程,也可以稱為函數(shù)綁定(有點類似C+中的動態(tài)聯(lián)編)。函數(shù)綁定的過程由FUNCTION_SETTING宏來完成。對于Fish和Dog類的實現(xiàn),與Animal基本上是類似的,除了將ABS_CTOR換成了CTOR,直接參見代碼:/* 魚的吃行為 */void Fish_eat(Animal* t) printf(魚吃水草!n);/* 魚的呼吸行為 */void Fish_breathe(Animal* t) printf(魚用鰓呼吸!n);/* 魚的移動行為 */void Fish_move(IMoveable* t) printf(魚在水里游!n);/* 初始化魚的昵稱和年齡 */void Fish_init(Fish* t, const char* name, int age) Animal* animal = SUPER_PTR(t, Animal); animal-setName(animal, name); animal-setAge(animal, age);CTOR(Fish)SUPER_CTOR(Animal);FUNCTION_SETTING(Animal.eat, Fish_eat);FUNCTION_SETTING(Animal.breathe, Fish_breathe);FUNCTION_SETTING(IMoveable.move, Fish_move);FUNCTION_SETTING(init, Fish_init);END_CTOR上面是Fish的實現(xiàn),下面看Dog的實現(xiàn):/* 狗的吃行為 */void Dog_eat(Animal* t) printf(狗吃骨頭!n);/* 狗的呼吸行為 */void Dog_breathe(Animal* t) printf(狗用肺呼吸!n);/* 狗的移動行為 */void Dog_move(IMoveable* t) printf(狗在地上跑!n);/* 初始化狗的昵稱和年齡 */void Dog_init(Dog* t, const char* name, int age) Animal* animal = SUPER_PTR(t, Animal); animal-setName(animal, name); animal-setAge(animal, age);CTOR(Dog)SUPER_CTOR(Animal);FUNCTION_SETTING(Animal.eat, Dog_eat);FUNCTION_SETTING(Animal.breathe, Dog_breathe);FUNCTION_SETTING(IMoveable.move, Dog_move);FUNCTION_SETTING(init, Dog_init);END_CTOR細心的朋友可能已經(jīng)注意到了,這里又有一個陌生的宏:SUPER_CTOR未介紹。這個宏是提供給子類用的,用于調用其直接父類的構造函數(shù)(類似Java語言中的super()調用,在這里,其實質是要先調用父類的函數(shù)綁定過程,再調用自身的函數(shù)綁定過程),類似Java那樣,SUPER_CTOR如果要出現(xiàn),需要是ABS_CTOR或者CTOR下面緊跟的第一條語句。最后,我們把Car類也實現(xiàn)了:void Car_move(IMoveable* t) printf(汽車在開動!n);CTOR(Car)FUNCTION_SETTING(IMoveable.move, Car_move);END_CTOR下面,我們實現(xiàn)main方法,以展示LW_OOPC的威力J:#include animal.hint main() Fish* fish = Fish_new(); / 創(chuàng)建魚對象 Dog* dog = Dog_new(); / 創(chuàng)建狗對象 Car* car = Car_new(); / 創(chuàng)建車子對象 Animal* animals2 = 0 ; / 初始化動物容器(這里是Animal指針數(shù)組) IMoveable* moveObjs3 = 0 ; / 初始化可移動物體容器(這里是IMoveable指針數(shù)組) int i = 0; / i和j是循環(huán)變量 int j = 0; / 初始化魚對象的昵稱為:小鯉魚,年齡為:1歲 fish-init(fish, 小鯉魚, 1); / 將fish指針轉型為Animal類型指針,并賦值給animals數(shù)組的第一個成員 animals0 = SUPER_PTR(fish, Animal); / 初始化狗對象的昵稱為:牧羊犬,年齡為:2歲 dog-init(dog, 牧羊犬, 2); / 將dog指針轉型為Animal類型指針,并賦值給animals數(shù)組的第二個成員 animals1 = SUPER_PTR(dog, Animal); / 將fish指針轉型為IMoveable接口類型指針,并賦值給moveOjbs數(shù)組的第一個成員 moveObjs0 = SUPER_PTR(fish, IMoveable); / 將dog指針轉型為IMoveable接口類型指針,并賦值給moveOjbs數(shù)組的第二個成員 moveObjs1 = SUPER_PTR(dog, IMoveable); / 將car指針轉型為IMoveable接口類型指針,并賦值給moveOjbs數(shù)組的第三個成員 moveObjs2 = SUPER_PTR(car, IMoveable); / 循環(huán)打印動物容器內的動物信息 for(i=0; ieat(animal); animal-breathe(animal); animal-sayHello(animal); / 循環(huán)打印可移動物體容器內的可移動物體移動方式的信息 for(j=0; jmove(moveObj); lw_oopc_delete(fish); lw_oopc_delete(dog); lw_oopc_delete(car); return 0;從上邊的代碼中,我們驚喜地發(fā)現(xiàn),在C語言中,借助LW_OOPC,我們實現(xiàn)了將不同的動物(Fish和Dog對象)裝入Animal容器,然后可以用完全相同的方式調用Animal的方法(比如eat和breathe方法),而實際調用的是具體的實現(xiàn)類(Fish和Dog)的對應方法。這正是面向對象中的多態(tài)的概念。同樣,我們可以將Fish對象,Dog對象,以及Car對象均視為可移動物體,均裝入IMoveable容器,然后用完全相同的方式調用IMoveable接口的move方法??吹搅藛幔拷柚鶯W_OOPC,在C語言下我們竟然可以輕松地實現(xiàn)面向對象和面向接口編程! 下面,再舉一個稍微復雜的例子,它的覆蓋面是足夠全面的,足以一瞥面向對象編程的3個要素:數(shù)據(jù)抽象、繼承和多態(tài)。通過這個例子,我們期望展現(xiàn)出LW_OOPC在遭遇問題本身比較復雜的情形下,是如何從容應對的,以加深讀者對LW_OOPC的認識。(備注:該問題來自C+沉思錄第八章的例子,有興趣的讀者可以對照參閱)問題描述:此程序涉及的內容是用來表示算術表達式的樹。例如,表達式(-5)*(3+4)對應的樹為:一個表達式樹包括代表常數(shù)、一元運算符和二元運算符的節(jié)點。這樣的樹結構在編譯器和計算器程序中都可能用到。我們希望能通過調用合適的函數(shù)來創(chuàng)建這樣的樹,然后打印該樹的完整括號化形式。例如,我們希望#include stdio.h#include Expr.hint main()Expr* expr1 = Expr_new();Expr* expr2 = Expr_new();Expr* expr3 = Expr_new();Expr* expr = Expr_new();expr1-initUnaryX(expr1, -, 5);expr2-initBinaryX(expr2, +, 3, 4);expr3-initBinary(expr3, *, expr1, expr2);expr-initBinary(expr, *, expr3, expr3);expr3-print(expr3);printf(n);expr-print(expr);printf(n); Expr_delete(expr);Expr_delete(expr3);Expr_delete(expr2);Expr_delete(expr1);return 0;打印(-5)*(3+4)(-5)*(3+4)*(-5)*(3+4)作為輸出。此外,我們不想為這些表達式的表示形式操心,更不想關心有關它們內存分配和回收的事宜。這個程序所做的事情在很多需要處理復雜輸入的大型程序中是很典型的,例如編譯器、編輯器、CAD/CAM系統(tǒng)等。此類程序中通常要花費很大的精力來處理類似樹、圖和類似的數(shù)據(jù)結構。這些程序的開發(fā)者永遠需要面對諸如內存分配、靈活性和效率之類的問題。面向對象技術可以把這些問題局部化,從而確保今后發(fā)生的一系列變化不會要求整個程序中的其他各個部分隨之做相應調整。解決方案:通過考查這個樹結構,會發(fā)現(xiàn)這里有3種節(jié)點。一種表示整數(shù)表達式,包含一個整數(shù)值,無子節(jié)點。另外兩個分別表示一元表達式和二元表達式,包含一個操作符,分別有一個或兩個子節(jié)點。我們希望打印各種節(jié)點,但是具體方式需要視要打印節(jié)點的類型而定。這就是動態(tài)綁定的用武之地了:我們可以定義一個虛函數(shù)(print)來指明應當如何打印各種節(jié)點。動態(tài)綁定將會負責在運行時基于打印節(jié)點的實際類型調用正確的函數(shù)。首先,我們抽象出“節(jié)點”的概念,抽象類的名字定為Expr_node,它提供了打印的抽象接口,所有的實際節(jié)點類型均從它派生:ABS_CLASS(Expr_node) void (*print)(Expr_node* t);具體類的情形怎樣?這些具體類型中最簡單的一類是包含一個整數(shù),沒有子節(jié)點的節(jié)點:CLASS(Int_node) EXTENDS(Expr_node); int n; void (*init)(Int_node* t, int k);其他類型又如何呢?每個類中都必須存儲一個操作符(這倒簡單,本文中假定操作符最長不超過2個字符,所以,可以用長度為3的字符數(shù)組來保存),但是如何存儲子節(jié)點呢?在運行時之前,我們并不知道子節(jié)點的類型會是什么,所以我們不能按值存儲子節(jié)點,必須存儲指針。這樣,一元和二元節(jié)點類如下所示:CLASS(Unary_node) EXTENDS(Expr_node);char op3;/假設操作符最長不超過2個字符 Expr_node* opnd; void (*init)(Unary_node* t, const char* a, Expr_node* b);CLASS(Binary_node)EXTENDS(Expr_node); char op3;/假設操作符最長不超過2個字符 Expr_node* left; Expr_node* right;void (*init)(Binary_node* t, const char* a, Expr_node* b, Expr_node * c);這個設計方案可以用,不過有一個問題。用戶要處理的不是值,而是指針,所以必須記住分配和釋放對象。例如,我們需要這么創(chuàng)建表達式樹:Int_node* int_node1 = Int_node_new();Int_node* int_node2 = Int_node_new();Int_node* int_node3 = Int_node_new();Unary_node* unary_node = Unary_node_new();Binary_node* binary_node1 = Binary_node_new();Binary_node* binary_node = Binary_node_new();int_node1-init(int_node1, 5);int_node2-init(int_node2, 3);int_node3-init(int_node3, 4);unary_node-init(unary_node, -, int_node1);binary_node1-init(binary_node1, +, int_node2, int_node3);binary_node-init(binary_node, *, unary_node, binary_node1);lw_oopc_delete(int_node1);/ 刪除創(chuàng)建的其他節(jié)點也就是說,我們需要去關心每一個節(jié)點的創(chuàng)建和釋放。我們不僅把內存管理這類煩心事推給了用戶,而且對用戶來說也沒有什么方便的辦法來處理這些事情。我們得好好想想辦法了。這里,提供一種解決內存管理問題的思路:引用計數(shù),這里是針對指針,對指針的狀況進行計數(shù),對象創(chuàng)建的時候,引用計數(shù)為1,凡是指針被賦值了,該指針所指對象的引用計數(shù)就自增一,每次指針要釋放,都先檢查對象的引用計數(shù),讓引用計數(shù)自減一,如果引用計數(shù)為0,則釋放該對象。另外,原先的設計不夠高層,用戶只能直接針對節(jié)點進行操作,沒有提供操作子樹的概念(這也是用戶代碼之所以復雜的原因之一),我們發(fā)現(xiàn),通過提供子樹的概念,我們不但能夠隱藏Expr_node繼承層次,而且,對于每一個節(jié)點,我們具備了操縱左子樹和右子樹的能力(原來只能操作左子節(jié)點和右子節(jié)點)。而這種功能增強完全是建立在面向對象的機制之上,我們并沒有引入耦合,在非常自然和輕松的情形下,我們獲得了更好的軟件組件之間協(xié)作的能力,這正是面向對象的魅力所在。這里,我們把子樹的概念用類Expr來表示,由于子樹此時成了Expr_node具體類的成員,同樣,左右子樹在Expr_node中同樣是以指針的方式保存,所以,對Expr也需要進行引用計數(shù),代碼直接貼上來,細節(jié)解說參見注釋:/ expr.h#ifndef EXPR_H_INCLUDED_#define EXPR_H_INCLUDED_#include lw_oopc.h/ 表達式節(jié)點ABS_CLASS(Expr_node)int use; / 引用計數(shù) void (*print)(Expr_node* t); / 打印表達式節(jié)點void (*finalize)(Expr_node* t); / 子類通過覆寫finalize方法,實現(xiàn)對資源清理行為的定制;/ 表達式(子樹的概念),其中,init*方法族提供了構建子樹的高層API,方便用戶使用CLASS(Expr)int use; / 引用計數(shù)Expr_node* p; / 子樹的根節(jié)點 / 構建整數(shù)表達式(包含一個整數(shù)值,無子表達式)void (*initInt)(Expr* t, int); / 構建一元表達式(包含一個操作符,一個子表達式)void (*initUnary)(Expr* t, const char*, Expr*); / 構建一元表達式的重載形式(通過傳入個整型值參數(shù),構造一個子表達式為整數(shù)表達式的一元表達式)void (*initUnaryX)(Expr* t, const char*, int); / 構建二元表達式(包含一個操作符,二個子表達式)void (*initBinary)(Expr* t, const char*, Expr*, Expr*); / 構建二元表達式的重載形式(通過傳入個整型值參數(shù),構造兩個子表達式均為整數(shù)表達式的二元表達式)void (*initBinaryX)(Expr* t, const char*, int, int);void (*print)(Expr* t); / 打印子樹;/ 整數(shù)表達式節(jié)點CLASS(Int_node) EXTENDS(Expr_node); / 繼承Expr_nodeint n; / 整數(shù)值 / 初始化整數(shù)表達式節(jié)點(傳入整數(shù)值)void (*init)(Int_node* t, int k); ;/ 一元表達式節(jié)點CLASS(Unary_node) EXTENDS(Expr_node); / 繼承Expr_nodechar op3; / 假設操作符最長不超過2個字符 Expr* opnd; / 子表達式 / 初始化一元表達式節(jié)點(傳入一個操作符和一個子表達式)void (*init)(Unary_node* t, const char* a, Expr* b);/ 二元表達式節(jié)點CLASS(Binary_node)EXTENDS(Expr_node); / 繼承Expr_node char op3; / 假設操作符最長不超過2個字符 Expr* left; / 左子表達式 Expr* right; / 右子表達式 / 初始化二元表達式節(jié)點(傳入一個操作符和兩個子表達式)void (*init)(Binary_node* t, const char* a, Expr* b, Expr* c);#endif/expr.c/ 包含所需頭文件ABS_CTOR(Expr_node)cthis-use = 1; / 構造函數(shù)中,將引用計數(shù)初始化為END_ABS_CTOR/ Expr_node的析構函數(shù)(DTOR/END_DTOR用于實現(xiàn)析構函數(shù)語義)DTOR(Expr_node)if (-cthis-use = 0) / 遞減引用計數(shù),如果計數(shù)為,釋放自己cthis-finalize(cthis); / 釋放內存之前先清理資源(其他需要釋放的對象)return lw_oopc_true;/ 返回true,表示析構成功,可以釋放內存return lw_oopc_false;/ 返回false,表示析構失敗,不能釋放內存END_DTOR/ 構建整數(shù)表達式(包含一個整數(shù)值,無子表達式),n為整數(shù)值void Expr_initInt(Expr* expr, int n)Int_node* intNode = Int_node_new(lw_oopc_file_line);intNode-init(intNode, n);expr-p = SUPER_PTR(intNode, Expr_node);/ 因篇幅所限,構建一元表達式、二元表達式以及對應的重載形式的函數(shù)實現(xiàn)代碼省略/ 打印表達式(子樹)void Expr_print(Expr* t)Expr_node* p = t-p;p-print(p);CTOR(Expr)FUNCTION_SETTING(initInt, Expr_initInt);FUNCTION_SETTING(initUnary, Expr_initUnary);FUNCTION_SETTING(initUnaryX, Expr_initUnaryX);FUNCTION_SETTING(initBinary, Expr_initBinary);FUNCTION_SETTING(initBinaryX, Expr_initBinaryX);FUNCTION_SETTING(print, Expr_print);cthis-use = 1; / 構造函數(shù)中,將引用計數(shù)初始化為END_CTOR/ Expr的析構函數(shù)(DTOR/END_DTOR用于實現(xiàn)析構函數(shù)語義)DTOR(Expr)if (-cthis-use = 0) / 遞減引用計數(shù),如果計數(shù)為,釋放自己Expr_node_delete(cthis-p);return lw_oopc_true;return lw_oopc_false;END_DTOR/ 整數(shù)表達式節(jié)點的初始化void Int_node_init(Int_node* t, int k)t-n = k;/ 整數(shù)表達式節(jié)點的打印void Int_node_print(Expr_node* t) Int_node* cthis = SUB_PTR(t, Expr_node, Int_node);printf(%d, cthis-n); / 整數(shù)表達式節(jié)點的資源清理void Int_node_finalize(Expr_node* t)/ 什么都不需要做CTOR(Int_node)SUPER_CTOR(Expr_node);FUNCTION_SETTING(init, Int_node_init);FUNCTION_SETTING(Expr_node.print, Int_node_print);FUNCTION_SETTING(Expr_node.finalize, Int_node_finalize);END_CTOR/ 因篇幅所限,一(二)元表達式節(jié)點的初始化、打印、資源清理、構造等函數(shù)的實現(xiàn)代碼省略/main.c#include stdio.h#include Expr.hint main()Expr* expr = Expr_new();/ 創(chuàng)建expr1、expr2、expr3的代碼expr1-initUnaryX(expr1, -, 5);expr2-initBinaryX(expr2, +, 3, 4);expr3-initBinary(expr3, *, expr1, expr2);expr-initBinary(expr, *, expr3, expr3);expr3-print(expr3);printf(n);expr-print(expr);printf(n);Expr_delete(expr);/ 刪除expr3、expr2、expr1的代碼return 0;程序運行的效果如下:怎么樣?效果還不錯吧,最重要的是,我們的C語言代碼現(xiàn)在已經(jīng)完全是面向對象的。方案的可擴展性如何?假設我們希望添加一種Ternary_node類型來表示三元操作符,如?:(也就是if-then-else操作符),看看,難度有多大?事實上,正是因為前面的設計是面向對象的,要增加一種節(jié)點類型易如反掌:/ 三元表達式節(jié)點CLASS(Ternary_node)EXTENDS(Expr_node);char op3; / 假設操作符最長不超過2個字符Expr* left;Expr* middle;Expr* right; / 初始化三元表達式節(jié)點(傳入一個操作符和三個子表達式)void (*init)(Ternary_node* t, const char* op, Expr* left, Expr* middle, Expr* right);在Expr中添加創(chuàng)建三元表達式的方法:/ 表達式(子樹的概念),其中,init*方法族提供了構建子樹的高層API,方便用戶使用CLASS(Expr)int use; / 引用計數(shù)Expr_node* p; / 子樹的根節(jié)點/ 既有實現(xiàn) / 構建三元表達式(包含一個操作符,三個子表達式)void (*initTernary)(Expr* t, const char*, Expr*, Expr*, Expr*); / 構建三元表達式的重載形式(通過傳入一個整型值參數(shù),構造三個子表達式均為整數(shù)表達式的三元表達式)void (*initTernaryX)(Expr* t, const char*, int, int, int);/ 既有實現(xiàn);請讀者參照Binary_node的現(xiàn)有實現(xiàn),實現(xiàn)出Ternary_node,這里不再贅述。一旦實現(xiàn)出Ternary_node,我們就可以這樣創(chuàng)建表達式樹并打?。? 創(chuàng)建expr1、expr2、expr3、expr對象(指針)expr1-initUnaryX(expr1, -, 0);expr2-initUnaryX(expr2, -, 5);expr3-initBinaryX(expr3, +, 3, 4);expr-initTernary(expr, ?:, expr1, expr2, expr3);expr-print(expr);printf(n);為了支持新的節(jié)點類型,對原有代碼的更動很少(僅對Expr類有增加方法),而且只有新增操作(新增類,新增方法),但沒有修改操作(指修改原有方法),面向對象的設計賦予了系統(tǒng)極大的彈性,讓程序在應對變化時,更加從容。在這個例子中,LW_OOPC幫助我們在C語言的世界里營造出OO的天地,帶領我們再一次領略了面向對象的風采。LW_OOPC最佳實踐說得簡單一點,要想使用好LW_OOPC這套宏,還得首先懂面向對象,要遵循面向對象設計的那些大原則,比如開閉原則等。在C語言中使用面向對象,根據(jù)實際

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論