【正文】
, expr2)。 exprinitBinary(expr, *, expr3, expr3)。 expr3print(expr3)。 printf(\n)。 exprprint(expr)。 printf(\n)。 Expr_delete(expr)。 …… // 刪除 expr expr expr1的代碼 return 0。 } 程序運(yùn)行的效果如下: 怎么樣?效果還不錯(cuò)吧,最重要的是,我們的 C 語(yǔ)言代碼 現(xiàn)在已經(jīng) 完全是面向?qū)ο蟮摹? 方案的可擴(kuò)展性 如何? 假設(shè)我們 希望添加一種 Ternary_node類型來表示三元操作符,如 ?:(也就是 ifthenelse操作符),看看,難度有多大? 事實(shí)上,正是因?yàn)榍懊娴脑O(shè)計(jì)是面向?qū)ο蟮?,要增加一種節(jié)點(diǎn)類型易如反掌: // 三 元表達(dá)式節(jié)點(diǎn) CLASS(Ternary_node) { EXTENDS(Expr_node)。 char op[3]。 // 假設(shè)操作符最長(zhǎng)不超過 2個(gè)字符 Expr* left。 Expr* middle。 Expr* right。 // 初始化三元表達(dá)式節(jié)點(diǎn)(傳入 一 個(gè) 操作符和 三 個(gè)子表達(dá)式) void (*init)(Ternary_node* t, const char* op, Expr* left, Expr* middle, Expr* right)。 }。 在 Expr中添加創(chuàng)建三元表達(dá)式的方法: // 表達(dá)式(子樹的概念),其中, init*方法族提供了構(gòu)建子 樹的高層 API,方便用戶使用 CLASS(Expr) { int use。 // 引用計(jì)數(shù) Expr_node* p。 // 子樹的根節(jié)點(diǎn) …… // 既有實(shí)現(xiàn) // 構(gòu)建三元表達(dá)式(包含一個(gè)操作符,三個(gè)子表達(dá)式) void (*initTernary)(Expr* t, const char*, Expr*, Expr*, Expr*)。 // 構(gòu)建三元表達(dá)式的重載形式 (通過傳入 一 個(gè)整型值參數(shù),構(gòu)造三個(gè)子表達(dá)式均為整數(shù)表達(dá)式的三元表達(dá)式) void (*initTernaryX)(Expr* t, const char*, int, int, int)。 …… // 既有實(shí)現(xiàn) }。 請(qǐng) 讀者 參 照 Binary_node的現(xiàn)有實(shí)現(xiàn),實(shí)現(xiàn)出 Ternary_node,這里不再贅述。 一旦實(shí)現(xiàn)出Ternary_node, 我們 就 可以這樣創(chuàng)建表達(dá)式樹 并打印 : …… // 創(chuàng)建 expr expr expr expr對(duì)象(指針) expr1initUnaryX(expr1, , 0)。 expr2initUnaryX(expr2, , 5)。 expr3initBinaryX(expr3, +, 3, 4)。 exprinitTernary(expr, ?:, expr1, expr2, expr3)。 exprprint(expr)。 printf(\n)。 為了支持新的節(jié)點(diǎn)類型,對(duì)原有代碼的更動(dòng)很少(僅對(duì) Expr類有增加方法),而且只有新增操作(新增類,新增方法),但沒有修改操作(指修改原有方法),面向?qū)ο蟮脑O(shè)計(jì)賦予了系統(tǒng)極大的彈性,讓程序在應(yīng)對(duì)變化時(shí),更加從容。 在這個(gè)例子中, LW_OOPC幫助我們?cè)?C語(yǔ)言的世界 里 營(yíng)造出 OO的天地,帶領(lǐng) 我們 再一次領(lǐng)略了面向?qū)ο蟮娘L(fēng)采。 LW_OOPC 最佳實(shí)踐 說得簡(jiǎn)單一點(diǎn),要想 使用好 LW_OOPC 這套宏,還得首先懂面向?qū)ο?,要遵循面向?qū)ο笤O(shè)計(jì)的那些大原則,比如開閉原則等。 在 C 語(yǔ)言中使用面向?qū)ο?,根?jù)實(shí)際使用的情況,給出如下建議: 1) 繼承層次不宜過深,建議最多三層(接口、抽象類、具體類 ,參見 圖 1 和 圖 2) 繼承層次過深,在 Java/C/C++中均不推崇,在 C 語(yǔ)言中 實(shí)踐面向?qū)ο蟮臅r(shí)候,尤其要遵循這一點(diǎn),只有這樣,代碼才能簡(jiǎn)單清爽。 2) 盡量避免多重繼承 盡可能使用單線繼承,但可實(shí)現(xiàn)多個(gè)接口(與 Java 中的單根繼承類似)。 3) 盡量避免具體類繼承具體類 具體類繼承具體類,不符合抽象的原則,要盡量避免。 4) 各繼承層次分別維護(hù)好自己的數(shù)據(jù) 子類盡量不要直接訪問祖先類的數(shù)據(jù),如果確實(shí)需要訪問,應(yīng)當(dāng)通過祖先類提供的函數(shù),以函數(shù)調(diào)用的方式間接訪問。 I n t e r f a c e 2 in t e rf a c e I n t e r f a c e 3 in t e rf a c e I n t e r f a c e 1 in t e rf a c e C o n c r e t e C l a s s 圖 1 I n t e r f a c e 1 in t e rf a c e I n t e r f a c e 2 in t e rf a c e I n t e r f a c e 3 in t e rf a c e I n t e r f a c e 4 in t e rf a c e A b s t r a c t C l a s sC o n c r e t e C l a s s 圖 2 LW_OOPC 的優(yōu)點(diǎn): 1) 輕量級(jí) 2) 廣泛 的適應(yīng)性,能夠適應(yīng)各種平臺(tái),各種編譯器(能支持 C 的地方,基本上都能支持) 3) 幫助懂 OO 的 Java/C++程序員寫出面向?qū)ο蟮?C 程序 。 4) 使用 C,也能引入 OO 的設(shè)計(jì)思想和方法,在團(tuán)隊(duì)的 C/C++分歧嚴(yán)重時(shí)可能非常有用。 LW_OOPC 的缺點(diǎn): 1) 無(wú)法支持重載( C 語(yǔ)言不支持所致) 2) 不完全的封裝(無(wú)法區(qū)分私有、保護(hù)和公有) LW_OOPC 的 INTERFACE/ABS_CLASS/CLASS 三個(gè)宏展開后都是 C 語(yǔ)言的 struct,其成員全是公有的,宏本身并無(wú)能力提供良好地封裝層次的支持,所以,只能從編程規(guī)范和編程風(fēng)格上 進(jìn)行引導(dǎo)。 3) 不支持 RTTI 既然不支持 RTTI,那么顯然也無(wú)法支持安全的向下轉(zhuǎn)型( C++中的 dynamic_cast的轉(zhuǎn)型功能) 4) 不 支持拷貝構(gòu)造以及賦值語(yǔ)義 5) 轉(zhuǎn)換成接口的表述有點(diǎn)麻煩,表達(dá)形式相比 C++要啰嗦很多。 6) 有學(xué)習(xí)成本,需要用戶學(xué)習(xí)并習(xí)慣這套宏 前四 條缺點(diǎn),實(shí)質(zhì)上并非是 LW_OOPC 的缺點(diǎn),而是 C 相對(duì) C++而言的缺點(diǎn),在這里,之所以也一并列上, 是希 望用戶不要 對(duì) LW_OOPC 抱 太高的期望,畢竟它 也 只是一套 C 語(yǔ)言的宏而已, C 語(yǔ)言有的缺點(diǎn), LW_OOPC 并不能 夠 解決。 總結(jié): 盡管如此,在使用 C 語(yǔ)言編 程的時(shí)候,在某些 情形 下,你可能想要通過面向?qū)ο髞砀玫慕M織代碼 。 偶爾,你也想要用用某個(gè)設(shè)計(jì)模式 , 此時(shí),這套宏能夠幫得上忙 , 使用它,有助于寫出相對(duì)易于理解和維護(hù)的面向?qū)ο蟮拇a。 因篇幅所限, 本文中沒有介紹LW_OOPC 的高級(jí)特性, 譬如 對(duì)內(nèi)存泄漏檢測(cè)的支持 。示例的完整代碼、 LW_OOPC 的最新版本以及關(guān)于這套宏的更加詳細(xì)的使用指南, 請(qǐng) 讀者 訪問 獲取 。最后,期望有興 趣的讀者,發(fā)揮聰明才智,提出改進(jìn)建議,讓 LW_OOPC 變得 越來越好! 幕后花絮 : 在完善 LW_OOPC 宏的過程中,我也認(rèn)真研究了 參考資料中列出的材料 。最初 版本有將近 25 個(gè)宏,后來,收到一些同事的反饋,認(rèn)為少量宏晦澀難記,而且不易理解,經(jīng)過認(rèn)真考慮,刪除掉 5 個(gè)宏,形成現(xiàn)在的 20 個(gè)宏。相比其他用 C 實(shí)現(xiàn)面向?qū)ο蟮姆桨?,LW_OOPC 簡(jiǎn)潔優(yōu)雅,每個(gè)宏的命名都經(jīng)過仔細(xì)地推敲,盡可能做到簡(jiǎn)單易懂,便于記憶。 但愿 LW_OOPC 真的能夠幫助到奮斗在一線的 C 程序員們。 參考資料: [1] 高煥堂。 UML+OOPC 嵌入式 C 語(yǔ)言開發(fā)精講,電子工業(yè)出版社, 2022 年 9 月。 [2] Objectoriented Programming with ANSIC,下載地址: ts/pdfs/。 [3] C 實(shí)現(xiàn)面向?qū)ο蟮姆椒ǎ?