頭文件 (.h) = 接口契約,僅包含聲明與必須可見的定義。
實現文件 (.cpp) = 實現細節,包含非內聯函數定義、全域變量定義、靜態成員初始化。
| 實體類別 | 頭文件 (.h) | 實現文件 (.cpp) | 備註 |
|---|---|---|---|
| 全域實體 | 變量聲明 extern int g_val; |
變量定義/初始化 int g_val = 0; |
頭文件禁用定義,防多重定義錯誤 |
函數聲明 void foo(); |
函數定義 void foo() {...} |
||
內聯函數定義 inline void bar() {...} |
— | inline 暗示定義可見,通常放頭文件 |
|
| 類 (非模板) | 類定義 (含成員聲明) | — | |
| 類內定義函數 (隱式 inline) | — | 編譯器自動內聯請求 | |
類外 inline 成員定義 |
— | 需 inline 關鍵字 |
|
| 普通成員函數定義 | void Class::func() {...} |
||
static 數據成員初始化 |
int Class::s_val = 0; |
必須在單一 .cpp 定義 | |
| 類模板 | 模板類定義、所有成員聲明與定義 | — | 模板實例化需見完整定義,全部放 .h (或 .inl 後 include) |
| 模板成員函數定義 | — | 類外定義無需 inline |
|
| 函數模板 | 聲明與定義 | — | 同類模板 |
#ifndef MY_PROJECT_MODULE_H #define MY_PROJECT_MODULE_H // 內容... #endif // MY_PROJECT_MODULE_H
現代編譯器亦支持 #pragma once (非標準但普遍支持)。
inline 關鍵字深度解析inline,等同於在類外加 inline。模板本身不生成代碼,只有實例化 (如 vector<int>) 時編譯器才根據具體類型生成代碼。實例化發生在使用點的編譯單元,因此編譯器必須在該編譯單元可見模板的完整定義。若定義在 .cpp,其他 .cpp 包含 .h 時看不到定義,導致鏈接錯誤 undefined reference to `Vector<int>::push_back()'。
Vector.h,複雜成員函數實現放 Vector.inl,在 Vector.h 底部 #include "Vector.inl",保持 .h 乾淨。int, double, string),可在 .cpp 顯式實例化 template class Vector<int>;,將定義隱藏在 .cpp,提高編譯速度、減小目標文件。class Foo;) 替代 #include "Foo.h",降低編譯依賴。/***** 錯誤 1:頭文件定義非 inline 函數 *****/
// A.h
void helper() { ... } // 多文件 include 導致多重定義
/***** 錯誤 2:模板定義寫在 .cpp *****/
// Vector.h
template<typename T> class Vector { void push_back(const T&); };
// Vector.cpp
template<typename T> void Vector<T>::push_back(const T& v) { ... } // 鏈接失敗
/***** 錯誤 3:static 成員僅聲明未定義 *****/
// Foo.h
class Foo { static int count; };
// 無 Foo.cpp 定義 int Foo::count = 0; → 鏈接錯誤
/***** 正確範例 *****/
// Config.h
namespace Config {
extern const int MAX_CONN; // 聲明
inline int getTimeout() { return 30; } // 內聯定義
}
// Config.cpp
const int Config::MAX_CONN = 100; // 定義