iT邦幫忙

2025 iThome 鐵人賽

DAY 11
2

隨著章節內容逐漸深入,開始要面對更大規模的系統設計。如何在提供穩定 interface 的同時,隱藏內部實現並增加編譯和執行的效能呢?學無止境,讓我們繼續閱讀《Effective C++》的第五節 —— Implementation。

29. Strive for exception-safe code

為程式設計時務必考量例外處理,具有 exception-safe 的函式在異常時可以保證:

  • 不洩漏任何資源,記憶體或 handle 會妥善釋放。
  • 不導致資料損毀,物件狀態保持一致性。

其提供以下三種承諾之一,函式的 exception-safe 取決於其所呼叫的子函式。

  • 基本保證:即使函式發生異常,程式資料依然保持有效且一致。
  • 強烈保證:函式執行成功後為完全成功狀態;若失敗,程式恢復到呼叫函數前的狀態,不產生副作用。
  • nothrow 的保證:函式絕不拋出 exception,只要執行即能確保完成其功能。

知識點 11 中提到的「copy and swap」設計模式,能幫助實現「強烈保證」但不適用於所有函數。

30. Understand the ins and outs of inlining

知識點 2時已經介紹過過 inline 函式,將小型且頻繁呼叫的函式設為 inline 能提升性能。

  • 省略函式呼叫成本:將函數展開為嵌入式程式碼,避免執行期間的「跳躍陳述式」。
  • 便於語境相關最佳化:編譯器能根據上下文進行更進一步的性能優化。
    • 常數折疊:在編譯階段計算常數的結果,避免執行時的運算。
    • 消除冗餘:編譯器移除不必要的或無效的程式碼,避免浪費資源。

然而在使用 template 函式時需謹慎,inline 可能導致編譯時間激增並增加 bin 執行檔大小。且應避免將大型或複雜的函式設置 inline,否則可能導致 code bloat —— 程式碼變得比解決相同問題耗時更長,且更加複雜難以理解。

31. Minimize compilation dependencies between files

如果檔案之間的依賴過多,光是修改 class 就會導致大量重複編譯,降低開發效率。以下方法可用來減少依賴:

  • 前置宣告:只宣告 class 的存在而不暴露詳細結構,精簡 include 的 header 數量避免多餘的定義。
    • 有效的場景:當使用類型的指標引用,編譯器不需要知道具體類型大小即可編譯程式。 e.g. Person* Yoyo;Person& Yoyo;
    • 無效的場景:當直接建立類型物件,編譯器必須知道完整類型定義以計算物件的大小,而前置宣告無法提供此信息。 e.g. Person Yoyo;
  • Pointer to Implementation (pimpl):將 class 的具體實現隱藏於別的檔案,僅提供 interface,減少對定義檔的依賴。儘可能以引用指標操作類型,避免直接操作類型本身。
class Widget {
public:
    Widget();
    ~Widget();
    void doSomething();

private:
    class Impl;
    Impl* pImpl;
};
  • 拆分檔案:將 interface 與 implementation 撰寫成不同檔案,客戶端只需包含必要的檔案。
  • 使用抽象基類:抽象基類是一種不包含具體字段的類型,其主要目的是提供一組純虛擬函式,作為其他類型的公共接口。僅聲明功能接口,而不實現任何具體細節。

上一篇
[Day 10] Implementations I
下一篇
[Day 12] 中場休息 Q&A小測驗!!
系列文
30 天 Effective C++ 大挑戰!!30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言