編譯單位和連結

在 C++ 程式中, 符號 ,例如變數或函式名稱,可以在其範圍內宣告任何次數。 不過,它只能定義一次。 此規則是「一個定義規則」(ODR)。 宣告 會將名稱引入程式,以及足夠的資訊,以便稍後將名稱與定義產生關聯。 定義 引進名稱,並提供建立它所需的所有資訊。 如果名稱代表變數,定義會明確建立儲存體並初始化它。 函 式定義 是由簽章加上函式主體所組成。 類別定義是由類別名稱所組成,後面接著列出所有類別成員的區塊。 (成員函式的主體可選擇性地在另一個檔案中個別定義。

下列範例顯示一些宣告:

int i;
int f(int x);
class C;

下列範例顯示一些定義:

int i{42};
int f(int x){ return x * i; }
class C {
public:
   void DoSomething();
};

程式是由一或多個 翻譯單位 所組成。 翻譯單元包含實作檔案及其直接或間接包含的所有標頭。 實作檔案通常副檔名為 .cpp.cxx 。 標頭檔通常具有 或 .hpp.h 副檔名。 編譯器會獨立編譯每個轉譯單位。 編譯完成之後,連結器會將編譯的翻譯單位合併成單 一程式 。 ODR 規則的違規通常會顯示為連結器錯誤。 在多個翻譯單位中定義相同名稱時,就會發生連結器錯誤。

一般而言,讓多個檔案可見變數的最佳方式是在標頭檔中宣告變數。 然後在每個 .cpp 需要宣告的檔案中新增 #include 指示詞。 藉由在標頭內容周圍新增 include guard ,您可確保標頭宣告的名稱只會針對每個轉譯單位宣告一次。 在一個實作檔案中定義名稱。

在 C++20 中, 模組 會引進為標頭檔改善的替代方案。

在某些情況下,您可能需要在檔案中 .cpp 宣告全域變數或類別。 在這些情況下,您需要一種方式來告訴編譯器和連結器名稱具有何種 連結 。 連結的類型會指定物件的名稱只顯示在一個檔案中或所有檔案中。 連結的概念僅適用于全域名稱。 連結的概念不適用於範圍內宣告的名稱。 範圍是由一組括住大括弧所指定,例如在函式或類別定義中。

外部與內部連結

免費函式 是定義于全域或命名空間範圍的函式。 根據預設,非常數全域變數和免費函式具有 外部連結 ;它們可從程式中的任何轉譯單位看到。 沒有其他全域物件可以有該名稱。 具有 內部連結或 沒有連結 的符號只能在宣告它的轉譯單位內顯示。 當名稱具有內部連結時,同名可能會存在於另一個轉譯單位中。 類別定義或函式主體內宣告的變數沒有連結。

您可以藉由明確將全域名稱宣告為 static ,強制具有內部連結。 此關鍵字會將其可見度限制為宣告所在的相同翻譯單位。 在此內容中, static 表示與套用至區域變數時不同的專案。

根據預設,下列物件具有內部連結:

  • const 物件
  • constexpr 物件
  • typedef 物件
  • static 命名空間範圍中的物件

若要提供 const 物件外部連結,請將它宣告為 extern ,並為其指派值:

extern const int value = 42;

如需詳細資訊,請參閱extern

另請參閱

基本概念