2 變量和基本類型
2.3 複合類型
2.3.1 引用
- 引用必須初始化(引用是將它和初始值綁定在一起,而不是拷貝給引用)
- 引用不創建對象,而是起別名
- 引用只能綁定對象,而不能和某個表達式的計算結果綁在一起(無法右值)
- 綁定相同類型的對象(int & a = (int) b)
2.3.2 指針
- 指針是一個對象,允許對指針賦值和拷貝
- 可以指定多個對象
- 無需賦初值(不確定值是什麼)
- &取地址
- 指針類型要和對象嚴格匹配
- 為*p賦值是為它對象賦值
- 使用nullptr生成空指針,別用宏處理NULL,int變量直接複製給指針也是不行的
int *p = nullptr;
int *pi;
int zero = 0;
pi = zero; //fatal;
- 預處理變量不屬於std,它由預處理器管理
- 建議初始化指針
- 任何非0指針的條件值都是true
- 用非法指針會導致不可預估的後果
2.3.2.1 void* 指針
- void *是特殊的指針類型,可以存放任意對象的地址
- 不能對void*對象操作
2.3.3 理解複合類型的聲明
- 離變量名最近的符號(int *& r)對變量類型有最直接的影響, 所以r是一個引用
2.4 const限定符
2.4.1. const限定符的作用
const(constant,常量)用於聲明變量的值不可更改。例如:
C++
const int bufSize = 512;
- bufSize被聲明為常量,不能被賦新值。
- 一旦用const聲明變量,必須初始化。
- const變量可以參與大部分操作(如參與運算、賦值給其他變量),但不能被修改。
好處:防止無意間的修改,提升代碼安全性和可讀性。
2.4.2 const的作用域與鏈接性
- 默認情況下,
const變量只在定義它的文件內有效(內部鏈接)。 - 如果希望多個文件共享一個const變量,需用
extern修飾,並在一個地方初始化:
C++
// file_1.h
extern const int bufSize; // 聲明
// file_1.cc
const int bufSize = 512; // 定義並初始化
注意:extern const告訴編譯器這個變量會在別的地方定義,避免重複定義。
2.4.3. const引用
- 可以用
const修飾引用,表示不能通過該引用修改所引用的對象。
const int ci = 10;
const int &r1 = ci; // 正確
r1 = 20; // 錯誤,不能修改ci
- 常量引用可以綁定非常量對象、字面量、甚至表達式的結果(因為會生成臨時量)。
double dval = 3.14;
const int &ri = dval; // 合法,ri綁定到一個臨時int(int(dval))
2.4.4. const指針和指向const的指針
- 指向常量的指針(pointer to const):不能通過該指針修改所指對象,但可以改變指針本身的指向。
const int *p = &ci; // p指向一個const int
*p = 20; // 錯誤,不能通過p修改ci
p = &j; // 正確,可以指向別的int
- 常量指針(const pointer):指針本身不可變,但允許通過它修改所指內容。
int i = 0;
int *const p1 = &i; // p1是const指針
*p1 = 20; // 正確
p1 = &j; // 錯誤,不能改變p1的指向
- 指向常量的常量指針:
const int *const p2 = &ci; // p2本身和它所指的內容都不能變
2.4.5. 頂層const和底層const
- 頂層const(top-level const):修飾對象本身不可變(如指針本身)。
- 底層const(low-level const):修飾對象所指內容不可變(如指向const的指針、引用)。
例子:
C++
int i = 0;
const int ci = i, &cr = ci; // ci和cr都是底層const
int *const p = &i; // p是頂層const
const int *cp = &ci; // cp是底層const
const int *const p2 = cp; // p2是頂層+底層const
2.4.6. constexpr與常量表達式
- 常量表達式:值不可變且編譯時就能確定,如字面量、const修飾的常量等。
- constexpr變量:C++11引入,要求變量一定是常量表達式,且必須用常量表達式初始化。
constexpr int max_files = 20; // 正確,20是常量表達式
constexpr int limit = max_files + 1; // 正確
constexpr int sz = size(); // 錯誤,size()需是constexpr函數
- 指針和引用也可用constexpr,但其初始化值需為常量表達式或固定地址。
- constexpr指針,是指指針本身是常量(頂層const),與其所指對象無關。
2.5 處理類型
簡化拼寫,明確類型
2.5.1 類型別名
typedef double wages;
typedef wagees base, *p;
using SI = Sales_item;
wages hourly, weekly;
SI item;
typedef char *pstring;
const pstring cstr = 0; --> char* const cstr = 0;
const pstring *ps;
typedef的替換不是簡單的文本替換,const pstring中的const會修飾整個指針類型- 這與直接寫
const char *(指向常量char的指針)有本質區別 - 這種寫法在需要保證指針地址不變(但內容可變)的場景很有用
2.5.2 auto類型説明符
2.5.2.1 auto在一條語句中申明多個變量,變量的初始數據要相同
auto sz = 0, pi = 3.14; //錯誤
2.5.2.2 引用初始化時的auto推導
- 當使用引用初始化
auto變量時,編譯器會推導出被引用對象的實際類型(而非引用類型本身) - 示例:
int i = 0, &r = i;
auto a = r; // a的類型是int(不是int&),因為r是i的別名
2.5.2.3const限定符的處理規則
auto默認會忽略頂層const(即對象本身的const屬性)- 但會保留底層const(如指針指向的const對象)
- 示例:
const int ci = i, &cr = ci;
auto b = ci; // b是int(忽略ci的頂層const)
auto c = cr; // c是int(cr是ci的別名,同樣忽略頂層const)
auto d = &i; // d是int*(普通指針)
auto e = &ci; // e是const int*(保留指向const的底層const)
- 需要顯式指定頂層const:
const auto f = ci; // f是const int
- 將引用類型設為auto則無視頂層缺陷
const int ci = 42;
auto &g = ci; // ✅ g的類型是const int&(保留ci的const屬性)
- 非常量引用不能綁定字面值
- 常量引用能綁定字面值
2.6decltype
2.6.1 基本行為
- 對變量名的處理
- 直接使用變量名時,返回該變量的聲明類型(含
const/引用等限定符)
int i = 42, &r = i;
decltype(i) a; // ✅ int
decltype(r) b; // ❌ int& 必須初始化
- 對錶達式的處理
- 若表達式不是單純變量名,則返回表達式結果的值類別類型:
- 左值表達式 → 引用類型(如
int&) - 右值表達式 → 值類型(如
int)
2.6.2 關鍵場景分析
|
表達式形式 |
|
示例與説明
|
|
變量名(無括號)
|
變量聲明類型
|
|
|
變量名(帶括號)
|
引用類型
|
|
|
解引用操作( |
引用類型
|
|
|
算術表達式( |
值類型
|
|
2.6.3 與auto的核心區別
- 保留性差異
decltype嚴格保留所有類型信息(包括頂層const和引用)auto默認忽略頂層const和引用(除非顯式指定)
- 推導邏輯差異
const int ci = 10;
auto x = ci; // x 是 int(忽略頂層const)
decltype(ci) y = ci; // y 是 const int
2.6.4 典型應用場景
- 函數返回類型後置
template<typename T, typename U>
auto add(T x, U y) -> decltype(x + y) { ... }
- 元編程類型萃取
template<typename T>
void func(T obj) {
decltype(obj.value) member; // 精確提取成員類型
}
- 完美轉發輔助
template<typename T>
void wrapper(T&& arg) {
decltype(auto) val = std::forward<T>(arg);
}
2.6.5 注意事項
- 引用必須初始化
decltype(*p) c; // ❌ 錯誤:c是int&,必須初始化
- 避免誤用括號
decltype((i)) d = i; // d是引用,操作d會影響i
- 右值引用處理
decltype(std::move(i)) e; // e是int&&(右值引用)