robot
最新文章(10)
Mqskit 和其它相關工具
CPython 的 GC 二、三事
寫 Mecurial Extension 是件快樂的事!
Mozilla 台灣辨公室徵人啟事
關於 Apple 的兩項專利
core dump 之前的 frame
怎麼發出 beep 聲?
先承認你要找的是奴才吧!
程式碼要清的多乾淨?
FreeBSD 的 Thread-Local Storage 實作
首頁
新編
最新留言
Entries RSS
重要關鍵字(10)
coding (122)
Python (93)
FreeBSD (71)
WEB (61)
URL (48)
hardware (46)
javascript (36)
Linux (34)
blog (30)
C++ (16)
所有關鍵字
新增 URL
資源管理與程式
by thinker
2 Columns
關鍵字:
coding
一般的程式,總是不停的在進行 resource 的配置和釋放,特別是 $C$/$C++$ 這一類沒有 GC 的程式語言,資源管理只是一場痛。這場痛,到了 $C++$ 的 template 被引進之後, smart pointer 解決了很多問題。然而,有不少資源卻不是 smart pointer 所能處理的,如 file descriptor 。專為這些 file descriptor 寫 smart pointer 固然是好事,然而往往要處理的 resource 太多樣化,或者 $C$ 語言根本不 支援。其實我要講的就是 $C$ 的環境啦。 $C$ 的 programmer 常會看到類似下面的 code : {{{#!cpp int foo() { char *p1, *p2, *p3; p1 = (char *)malloc(...); if(p1 == NULL) return ERR; if(sub1(p1) == ERR) { free(p1); return ERR; } p2 = (char *)malloc(...); if(p2 == NULL) { free(p1); return ERR; } if(sub2(p1, p2) == ERR) { free(p1); free(p2); return ERR; } p3 = (char *)malloc(...); if(p3 == NULL) { free(p1); free(p2); return ERR; } if(sub2(p1, p2) == ERR) { free(p1); free(p2); free(p3); return ERR; } free(p1); free(p2); free(p3); return OK; } }}} 看來就很可怕,一堆 conditional branch 。不但複雜,也不美觀。 於是,最常看到的 solution 是: {{{#!cpp int foo() { char *p1, *p2, *p3; p1 = p2 = p3 = NULL; p1 = (char *)malloc(...); if(p1 == NULL) goto error; if(sub1(p1) == ERR) goto error; p2 = (char *)malloc(...); if(p2 == NULL) goto error; if(sub2(p1, p2) == ERR) goto error; p3 = (char *)malloc(...); if(p3 == NULL) goto error; if(sub3(p1, p2, p3) == ERR) goto error; free(p1); free(p2); free(p3); return OK; error: if(p1) free(p1); if(p2) free(p2); if(p3) free(p3); return ERR; } }}} 這看起來簡單多了,但 goto 實在讓很多人不順眼,而且 contional branch 甚至沒減少,而還增加。於是你可以試試: {{{#!cpp int foo() { char *p1, *p2, *p3; p1 = p2 = p3 = NULL; do { p1 = (char *)malloc(...); if(p1 == NULL) break; if(sub1(p1) == ERR) break; p2 = (char *)malloc(...); if(p2 == NULL) break; if(sub2(p1, p2) == ERR) break; p3 = (char *)malloc(...); if(p3 == NULL) goto error; if(sub3(p1, p2, p3) == ERR) break; free(p1); free(p2); free(p3); return OK; } while(0); if(p1) free(p1); if(p2) free(p2); if(p3) free(p3); return ERR; } }}} 這看起來順眼多了,對 goto 有潔癖的人也大概能接受了。但,如果 while 裡面有其它 for 、 while 或 switch 時, break 就無法正確的進行跳脫了。 $C$ 無法指定 break 跳脫多少層。另外, conditional branch 還是依舊。 於是另一種發明出現了: {{{#!cpp static int _foo(char *p1, char *p2, char *p3) { if(sub1(p1) == ERR) return ERR; if(sub2(p1, p2) == ERR) return ERR; if(sub3(p1, p2, p3) == ERR) return ERR; return OK; } #define xfree(p) (void)((p)? free(p): NULL) int foo() { char *p1, *p2, *p3; int r; p1 = (char *)malloc(...); p2 = (char *)malloc(...); p3 = (char *)malloc(...); if(p1 && p2 && p3) r = _foo(p1, p2, p3); xfree(p1); xfree(p2); xfree(p3); } }}} 唔! 是否覺的程式邏輯突然變清楚了?! 沒錯,程式被拆解了,原本混雜在一起的邏輯被抽離成一個個獨立的 block 。 或許有人擔心 function call 讓效能變差了,而且當程式出錯時, allocate 多餘的資源。但這是多慮了。第一, 我們宣告成 static function 時, compiler 應該要能幫我們 optimize ,變成 inline function ,就算 $C$ 沒有 inline 。但這是 compiler 的責任,就交給 compiler 幫你處理吧! 而資源的問題,大部分時侯程式應該是順著正常邏輯執行,因此錯誤處理的流程幾乎很少執行。因此,這種考量其實也是不必要的。除非你有特殊的狀況,否則還是讓 code 美一點吧! 沒有一種是完美的,也不排除有其它更好的方式。所謂道無恆道,依據狀況靈活運用這些戰術上的技巧,才是正道。
最後更新時間: 2007-08-10 01:20:43 CST |
引用
查詢:
COMMENTS:
on 2007-08-10 20:41:12 CST
Lisa
said ..
good job!