2 變量和基本類型

2.3 複合類型

2.3.1 引用

  1. 引用必須初始化(引用是將它和初始值綁定在一起,而不是拷貝給引用)
  2. 引用不創建對象,而是起別名
  3. 引用只能綁定對象,而不能和某個表達式的計算結果綁在一起(無法右值)
  4. 綁定相同類型的對象(int & a = (int) b)

2.3.2 指針

  1. 指針是一個對象,允許對指針賦值和拷貝
  2. 可以指定多個對象
  3. 無需賦初值(不確定值是什麼)
  4. &取地址
  5. 指針類型要和對象嚴格匹配
  6. 為*p賦值是為它對象賦值
  7. 使用nullptr生成空指針,別用宏處理NULL,int變量直接複製給指針也是不行的
int *p = nullptr;
int *pi;
int zero = 0;
pi = zero; //fatal;
  1. 預處理變量不屬於std,它由預處理器管理
  2. 建議初始化指針
  3. 任何非0指針的條件值都是true
  4. 用非法指針會導致不可預估的後果

2.3.2.1 void* 指針

  1. void *是特殊的指針類型,可以存放任意對象的地址
  2. 不能對void*對象操作

2.3.3 理解複合類型的聲明

  1. 離變量名最近的符號(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 基本行為

  1. 對變量名的處理
  • 直接使用變量名時,返回該變量的聲明類型(含const/引用等限定符)
int i = 42, &r = i;
decltype(i) a;  // ✅ int
decltype(r) b;  // ❌ int& 必須初始化
  1. 對錶達式的處理
  • 若表達式不是單純變量名,則返回表達式結果的值類別類型:
  • 左值表達式引用類型(如int&
  • 右值表達式值類型(如int

2.6.2 關鍵場景分析

表達式形式

decltype 推導結果

示例與説明

變量名(無括號)

變量聲明類型

decltype(i)int

變量名(帶括號)

引用類型

decltype((i))int&

解引用操作(*p

引用類型

decltype(*p)int&

算術表達式(r + 0

值類型

decltype(r + 0)int

2.6.3 與auto的核心區別

  1. 保留性差異
  • decltype 嚴格保留所有類型信息(包括頂層const和引用)
  • auto 默認忽略頂層const和引用(除非顯式指定)
  1. 推導邏輯差異
const int ci = 10;
auto x = ci;         // x 是 int(忽略頂層const)
decltype(ci) y = ci; // y 是 const int

2.6.4 典型應用場景

  1. 函數返回類型後置
template<typename T, typename U>
auto add(T x, U y) -> decltype(x + y) { ... }
  1. 元編程類型萃取
template<typename T>
void func(T obj) {
    decltype(obj.value) member;  // 精確提取成員類型
}
  1. 完美轉發輔助
template<typename T>
void wrapper(T&& arg) {
    decltype(auto) val = std::forward<T>(arg);
}

2.6.5 注意事項

  1. 引用必須初始化
decltype(*p) c;  // ❌ 錯誤:c是int&,必須初始化
  1. 避免誤用括號
decltype((i)) d = i;  // d是引用,操作d會影響i
  1. 右值引用處理
decltype(std::move(i)) e;  // e是int&&(右值引用)