常值
C++ 定義了一套完善的只讀量定義方法,被常量修飾符 const 修飾的對象或類型都是隻讀量,只讀量的內存存儲與一般變量沒有任何區別,但是編譯器會在編譯期進行衝突檢查,避免對只讀量的修改。因此合理使用 const 修飾符可以增加代碼健壯性。
常類型
在類型的名字前增加 const 修飾會將該類型的變量標記為不可變的。具體使用情況有常量和常引用(指針)兩種。
常量
這裏的常量即常變量,指的是 const 類型的變量(而不是標題裏泛指的只讀量)。常類型量在聲明之後便不可重新賦值,也不可訪問其可變成員,只能訪問其常成員。常成員的定義見後文。
類型限定符
C++ 中類型限定符一共有三種:常量(const)、可變(mutable)和易變(volatile),其中默認情況下是可變變量,聲明易變變量的情形是為了刻意避免編譯器優化。
| const int a = 0; // a 的類型為 const int
// a = 1; // 報錯,不能修改常量
|
常引用、常指針
常引用和常指針也與常量類似,但區別在於他們是限制了訪問,而沒有更改原變量的類型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | int a = 0;
const int b = 0;
int *p1 = &a;
*p1 = 1;
const int *p2 = &a;
// *p2 = 2; // 報錯,不能通過常指針修改變量
// int *p3 = &b; // 報錯,不能用普通指針指向 const 變量
const int *p4 = &b;
int &r1 = a;
r1 = 1;
const int &r2 = a;
// r2 = 2; // 報錯,不能通過常引用修改變量
// int &p3 = b; // 報錯,不能用普通引用指向 const 變量
const int &r4 = b;
|
另外需要區分開的是「常類型指針」和「常指針變量」(即常指針、指針常量),例如下列聲明
| int* const p1; // 類型為int的常指針,需要初始化
const int* p2; // 類型為const int的指針
const int* const p3; // 類型為const int的常指針
int (*f1)(int); // 普通的函數指針
// int (const *f2)(int); // 指向常函數的指針,不可行
int (*const f3)(int) = some_func; // 指向函數的常指針,需要初始化
int const* (*f4)(int); // 指向返回常指針的函數指針
int const* (*const f5)(int) = some_func; // 指向返回常指針的函數的常指針
|
我們把常類型指針又稱 底層指針、常指針變量又稱 頂層指針。
另外,C++ 中還提供了 const_cast 運算符來強行去掉或者增加引用或指針類型的 const 限定,不到萬不得已的時候請不要使用這個關鍵字。
常參數
在函數參數裏限定參數為常類型可以避免在函數里意外修改參數,該方法通常用於引用參數。此外,在類型參數中添加 const 修飾符還能增加代碼可讀性,能區分輸入和輸出參數。
| void sum(const std::vector<int> &data, int &total) {
for (auto iter = data.begin(); iter != data.end(); ++iter)
total += *iter; // iter 是 const 迭代器,解引用後的類型是 const int
}
|
常成員
常成員指的是類型中被 const 修飾的成員,常成員可以用來限制對常對象的修改。其中,常成員變量與常量聲明相同,而常成員函數聲明方法為在成員函數聲明的 末尾(參數列表的右括號的右邊)添加 const 修飾符。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 | #include <iostream>
struct ConstMember {
const int *p; // 常類型指針成員
int *const q; // 常指針變量成員
int s;
void func() { std::cout << "General Function" << std::endl; }
void constFunc1() const { std::cout << "Const Function 1" << std::endl; }
void constFunc2(int ss) const {
// func(); // 常成員函數不能調用普通成員函數
constFunc1(); // 常成員函數可以調用常成員函數
// s = ss; // 常成員函數不能改變普通成員變量
// p = &ss; // 常成員函數不能改變常成員變量
}
};
int main() {
const int a = 1;
int b = 1;
struct ConstMember c = {.p = &a, .q = &b}; // 指派初始化器是C++20中的一種語法
// *(c.p) = 2; // 常類型指針無法改變指向的值
c.p = &b; // 常類型指針可以改變指針指向
*(c.q) = 2; // 常指針變量可以改變指向的值
// c.q = &a; // 常指針變量無法改變指針指向
const struct ConstMember d = c;
// d.func(); // 常對象不能調用普通成員函數
d.constFunc2(b); // 常對象可以調用常成員函數
return 0;
}
|
常表達式 constexpr(C++11)
另請參閲 常值:常表達式 constexpr(C++11)
constexpr 説明符的作用是聲明可以在編譯時求得函數或變量的值,它的行為和 C 語言中的 const 關鍵字是一致的,會將變量結果直接編譯到棧空間中。constexpr 還可以用來替換宏定義的常量,規避 宏定義的風險。constexpr 修飾的是變量和函數,而 const 修飾的是類型。
Note
實際上把 const 理解成 "readonly",而把 constexpr 理解成 "const" 更加直觀。
| constexpr int a = 10; // 直接定義常量
constexpr int FivePlus(int x) { return 5 + x; }
void test(const int x) {
std::array<x> c1; // 錯誤,x在編譯期不可知
std::array<FivePlus(6)> c2; // 可行,FivePlus編譯期可以推斷
}
|
參考資料
本页面最近更新:,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:OI-wiki
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用