1. 程式人生 > >2018-12-18 C++ 學習筆記

2018-12-18 C++ 學習筆記

文章目錄

一、const

(一) 指向常量的指標或引用

這一類指標或引用既可以指向常量也可以指向非常量,只不過這類指標或引用自以為自己指向了常量,所以自覺不去改變所指物件的值。

(二) 頂層 const 和底層 const

頂層 const 表示指標本身是個常量,底層 const 表示指標所指向的物件是一個常量。

int i = 0;
int *const p1 = &i; // 不能改變 p1 的值,這是一個頂層 const

const int ci =
42; // 不能改變 ci 的值,這是一個頂層 const const int *p2 = &ci; // 允許改變 p2 的值,這是一個底層 const const int *const p3 = p2; // 靠右的 const 是頂層 const,靠左的是底層 const const int &r = ci; // 用於宣告引用的 const 都是底層 const

執行拷貝操作時,因為被拷貝物件的值不會被改變,所以拷貝者與被拷貝者是否為常量都沒什麼關係。

故頂層 const 在執行拷貝操作時不受什麼影響,而底層 const 卻有所限制。當執行物件的拷貝操作時,拷貝者和被拷貝者必須具有相同的底層 const

資格,或者兩個物件的資料型別必須能夠轉換。

int *p = p3; // 錯誤,p 沒有跟 p3 一樣的底層 const
p2 = p3; // 正確,都是底層const
p2 = &i; // 正確,int* 可以轉換成 const int*
const int &r2 = i; // 正確,const int& 可以繫結到普通 int 上

二、constexpr 和常量表達式

常量表達式是指值不會改變並且在編譯過程就能得到計算結果的表示式。

(一) constexpr 變數

將變數宣告為 constexpr 可以讓編譯器來驗證變數的值是否為一個常量表達式。

constexpr 只能修飾字面值型別(算術型別,指標,引用)變數。

(二) 指標與 constexpr

const int *p = nullptr; // p 是一個指向整形常量的指標
constexpr int *q = nullptr; // q 是一個指向普通整形變數的常量指標

constexpr int i = 42; // i 必須定義在函式體之外
constexpr const int *ccp = &i; // ccp是常量指標,指向整形常量 i

(三) constexpr 函式

遵循兩項約定:

  • 函式的返回型別和所有形參的型別都得是字面值型別。
  • 函式體中有且只有一條 return 語句。

通常定義在標頭檔案。


三、auto

auto 推斷出來的型別一般會忽略頂層 const 而保留底層 const,如需要,需手動新增 const

const int ci = 30, &cr = ci;

auto b = ci; // b 是 int,不是 const int
auto c = cr; // c 是 int,因為 cr 是 ci 的別名,ci 是 const int
auto d = &ci; // d 是 const int*,因為底層 const 被保留

// 以下在 auto 前面新增的 const 為頂層 const
const auto e = ci; // e 是 const int
const auto f = &ci; // f 是 const int* const
auto &g = ci; // g 為 const int&
auto &h = 42; // auto 推斷結果為 int,h 為 int&,不能繫結字面值,需要在 auto 前面新增 const
auto *p = &ci; // p 為 const int*,同 d

總結:

  • auto 不會讓其修飾的變數成為一個常量,但可以成為一個指向常量的指標。
  • auto 在推斷時會保留右值的底層 const 屬性。
  • 若右值是引用型別,則 auto 會把引用單純看成別名,追溯該引用所繫結的變數的型別,並以該型別為推導依據。
  • auto 後面緊跟著符號 & 或 *,則 auto 會結合它們進行推斷。

四、decltype 型別指示符

有時會想要從表示式的型別推斷出要定義的變數的型別,但是不想用該表示式的值初始化變數。decltype 可選擇並返回運算元的資料型別。

表示式為變數時:

const int ci = 0, &cj = ci;
decltype(ci) x = 0; // 頂層 const 被保留,x 是 const int
decltype(cj) y = x; // 引用被保留,cj 是 const int&,故 y 也一樣
decltype(cj) z; // 錯誤,z 是一個引用型別,必須初始化

int j = 30;
decltype((j)) m; // 錯誤,m 是 int&,必須初始化
decltype(j) n; // n 是 int

表示式不是變數時:

int i = 42;
int *p = &i;
int &r = i;

decltype(r + 10) b; // 加法的結果為 int,故 b 是 int,只不過未初始化
decltype(*p) c; // 錯誤,c 是 int&,必須初始化 

總結:

  • 若表示式為變數且沒有加括號,則 decltype 將原封不動地返回該變數的原本型別,既不忽略頂層 const ,也不會把引用單純看成別名。
  • 若表示式為加了括號的變數,則返回的型別為該變數型別的引用。
  • 若表示式不是變數且不是解引用操作,則返回的型別為表示式運算結果對應的型別。
  • 若表示式為變數且為解引用操作,則返回型別為引用。

五、string 類新知

(一) 使用範圍 for 語句改變字串中的字元

string s("HUANGHAOBO");
for(auto &c : s){
    c = toupper(c); // c 是一個引用,該行語句會改變 s 中字元的值
}

(二) 下標型別 decltype(s.size()) 和 string::size_type

for(decltype(s.size()) i = 0; i < s.size(); i++){ /*...*/ }

string::size_type n;
while(cin >> n){
    if(n < s.size()){ /*...*/ }
}

六、vector 模板類新知

(一) 列表初始化

vector<string> v1{"aa", "bb", "cc"}; // 列表初始化
vector<string> v2("aa", "bb", "cc"); // 錯誤

vector<int> v3(10, 1); // 有 10 個元素,元素值皆為 1
vector<int> v3{10, 1}; // 列表初始化,有 2 個元素,值分別是 10 和 1

七、參考資料

《C++ Primer(第五版)》