前向宣告 (Forward Declarations)

盡可能地避免使用前向宣告。 只要 #include 你需要的標頭檔就好

定義

前向宣告是指在沒有提供完整定義的情況下,預先宣告某個實體的存在:

// 在 C++ 程式碼中:
class B;
void FuncInB();
extern int variable_in_b;
ABSL_DECLARE_FLAG(flag_in_b);  // 譯註:這是 Abseil 函式庫中一個用於宣告旗標(flag)的巨集。

優點

  • 前向宣告可以節省編譯時間。 #include 會迫使編譯器開啟更多檔案與處理更多輸入。
  • 前向宣告可以避免不必要的重編譯。 #include 在標頭檔做了一些無關的改動時,也會迫使編譯器重新編譯你的程式碼。

缺點

  • 前向宣告會隱藏依賴,這會讓使用者的程式碼在標頭檔被修改後略過了可能必要的重新編譯過程。

  • 前向宣告相較於 #include 會讓自動化工具更難找出定義一個符號的模組為何。

  • 一個前向宣告可能會被函式庫後續的修改搞壞。 函式與模板的前向宣告可能會限制標頭檔維護者對 API 進行變更,例如擴大參數型別、為模板新增預設參數,或遷移至新的命名空間。

  • 前向宣告 std:: 名稱空間內的符號可能會導致未定義行為。

  • 有時候難以判斷應該使用前向宣告還是完整的 #include。 甚至有時替換掉 #include 可能會無聲無息地改變程式碼的意義:

    // b.h:
    struct B {};
    struct D : B {};
    
    // good_user.cc:
    #include "b.h"
    void f(B*);
    void f(void*);
    void test(D* x) { f(x); } // 這裡會呼叫 f(B*)
    

    如果上面的程式碼中,將 #include 替換成 BD 的前向宣告的話,test() 就會變成呼叫 f(void*)

  • 從標頭檔前向宣告多個符號,通常比直接 #include 更繁瑣且難以維護。

  • 為了使用前向宣告而調整程式架構(例如將物件成員改為指標成員),可能會使程式變慢或增加複雜度。

決定

盡量避免前向宣告其他專案中的實體。