核心定義
| 概念 | 說明 |
| 聲明 | 向編譯器介紹名稱、型別,不分配記憶體。讓編譯器知道「這個符號存在」。 |
| 定義 | 在聲明基礎上分配記憶體、可選初始化。一個程式中同一實體有且僅有一個定義(ODR 原則)。 |
聲明 vs 定義 對比表
| 屬性 | 聲明 | 定義 | 僅聲明非定義 |
| 可出現多次? | 是 | 否 | 是 |
| 分配記憶體? | 否 | 是 | 是 |
| 可直接使用? | 否 | 是 | 是 |
| 知道完整構成? | 不一定 | 是 | 是 |
三種「聲明非定義」情況
- extern 變數(無初始化):
extern int i; // 僅聲明,定義在別處
extern int i = 10; // 有初始化 → 也是定義
- 類型前向聲明:
struct User; // 僅聲明,未定義成員
- 函數聲明(無函數體):
double sqrt(double); // 僅聲明
「聲明即定義」常見範例
int i; // 定義(預設 0)
char ch; // 定義
string s; // 定義
int count = 1; // 定義+初始化
const double pi = 3.14; // 定義(const 預設內部連結)
const char *name = "Njal";
const char *season[] = {"spring","summer","fall","winter"};
struct Date { int d,m,y; }; // 類型定義
int day(Date *p) { return p->d; } // 函數定義
template<typename T> T abs(T a) { return a<0?-a:a; } // 模板定義
enum Beer { Carlsberg, Tuborg, Thor };
namespace NS { int a; } // 命名空間變數定義
特殊情況深度解析
1. extern + 初始化 = 定義
extern int i = 10; // 既是聲明也是定義(編譯器會分配記憶體)
2. typedef 的糾結
typedef complex<short> Point; // C++ 標準視為「定義」(別名定義)
struct User; // 前向聲明(非定義)
typedef User USER; // 這算定義嗎?標準未明確,視編譯器實現而定
3. 類別成員變數
struct Date {
int y; // 這是「成員聲明」,非定義(不分配記憶體)
// 定義發生在建立 Date 物件時
};
4. 靜態成員的聲明與定義分離
class C {
static int x; // 類內:聲明
static const int a; // 類內:聲明(C++17 前需類外定義)
C(); // 構造函數聲明
};
int C::x; // 類外:定義
const int C::a = 11; // 類外:定義+初始化
常見誤區釐清
| 誤區 | 正確理解 |
| 「不加 extern 就是定義」 | 類成員變數不加 extern 但不分配記憶體,屬聲明 |
| 「類只是聲明」 | 類定義(class C { ... };)完整定義了型別佈局,是定義 |
int C::x; 是初始化 | 這是定義(分配記憶體),預設零初始化;=11 才是初始化 |
一行總結
定義必是聲明,聲明不一定是定義。定義分配記憶體,聲明只告知編譯器符號存在。