函式設計

老司機的詩和遠方發表於2020-04-06

函式介面的兩個要素是引數和返回值。對於C++而言,引數和返回值的傳遞方式有三種,值傳遞,指標傳遞和引用傳遞。

引數規則
(1)引數的書寫要完整,不要貪圖省事只寫引數的型別而省略引數名字。如果沒有引數,則用void填充。
(2)引數命名要恰當,順序要合理。
(3)如果引數是指標,且僅作輸入用,則應在型別前加const,以防止該指標在函式體內被意外修改。
(4)如果輸入引數以值傳遞的方式傳遞物件,則宜改用“const &”方式來傳遞,這樣可以省略臨時物件的構造和析構過程,從而提高效率。
(5)避免函式有太多的引數,引數個數儘量控制在5個以內,如果引數太多,在使用時容易將引數型別或順序搞錯!
(6)儘量不要使用型別和數目不確定的引數。這種風格函式在編譯時喪失了嚴格的型別安全檢查。

返回值規則
(1)不要省略返回值的型別。C語言中凡不加型別說明的函式,一律自動按整形處理,這樣做不會有什麼好處,卻容易被誤解為void型別。C++語言是一門對型別極其苛刻,會有安全的檢查,不允許上述情況發生,由於C++程式可以呼叫C函式,為了避免混亂,規定任何C++/C函式都必須有型別。如果函式沒有返回值,那就應申明為void型別。
(2)函式名字與返回值型別在語義上不可衝突。
(3)不要將正常值和錯誤標誌混在一起返回,正常值用輸出引數獲得,而錯誤標誌用return語句返回。一般最好的處理方式將錯誤標誌位作為返回,而傳值用引數(指標形式)傳出。
(4)有時候函式原本不需要返回值,但為了增加靈活性如支援鏈式表達,可以附加返回值。
(5)如果函式返回值是一個物件,有些場合用引用傳遞替換值傳遞可以提高效率,而有些場合只能用值傳遞而不能用引用傳遞,否則會出錯。值傳遞需要額外的拷貝開銷。

函式內部實現的規則
不同功能的函式其內部實現各不相同,看起來似乎無法就“內部實現”達成一致的觀點,但根據經驗,我們可以在函式體的入口處和出口處從嚴把關,從而提高函式的質量。
(1)在函式體的入口處,對引數的有效性進行檢查。很多程式錯誤都是由非法引數引起的,我們應該充分理解並正確使用“斷言”assert來防止此類錯誤。
(2)在函式的出口處,對return語句的正確性和效率進行檢查。對於有返回值的函式,return寫得好與壞會影響函式的執行或效率。
<1>return語句不可返回指向“棧記憶體”的指標或者引用,因為該記憶體在函式體結束時被自動銷燬。
<2>要搞清楚返回的是值還是指標,還是引用。
<3>函式返回值是一個物件要考慮return語句的效率,最好採用引用返回,因為效率問題!!物件需要構造和析構,都需要時間!
(3)函式的功能要單一,不要設計多用途的函式。
(4)函式體的規模要小盡量控制在50行程式碼之內。
(5)儘量避免函式帶有記憶功能,相同的輸入產生相同的輸出。在C/C++語言中,函式static的區域性變數是函式的記憶儲存器,建議儘量少用static區域性變數,除非必須!
(6)不僅要檢查輸入引數的有效性,還要檢查通過其它用途進入函式體內的變數的有效性,如全域性變數、檔案控制程式碼等
(7)用於出錯處理的返回值一定要清楚,讓使用者不容易忽視或誤解錯誤情況。

使用斷言
程式一般分為debug和release版本,debug版本用於內部除錯,release版本發行給使用者使用。斷言assert是僅在debug版本起作用的巨集,它用於檢查不應該發生的情況。

assert不是一個倉促拼湊起來的巨集,為了不在程式的debug和release版本引起差別,assert不應該產生任何副作用。所以assert不是函式,而是巨集。程式設計師可以把assert看成一個在任何系統狀態下都可以安全使用的無害測試手段,如果程式在assert處終止了,並不是說含有該assert的函式有錯誤,而是呼叫者出了差錯,assert可以幫助我們找到發生錯誤的原因。
(1)使用斷言捕捉不應該發生的非法情況,不要混淆非法情況與錯誤情況之間的區別,後者是必然存在的並且是一定要作出處理的。
(2)函式入口處,使用斷言檢查引數的有效性(合法性)。
(3)編寫函式時,要進行反覆的考查,並且自問:我打算做哪些假定?一旦確定了的假定,就要使用斷言對假定進行檢查。
(4)當進行犯錯設計時,如果不可能發生的事情的確發生了,則要使用斷言進行報警!

相關文章