?const限定符
- const對象一旦創建后其數值就不會被再次改變,因此const對象必須初始化。
- const對象只在文件中有效
- 在不同的文件中使用不同的const來定義不同的常量,那么每個文件定義的變量只會在自己所屬的文件中有效。如果想讓多個文件共享同一個const變量,那么使用關鍵字extern即可
const的引用
把引用綁定到const對象上,就像綁定到其他對象上一樣,稱之為對于常量的引用。和普通信用不同,對于常量的引用不能被用于修改它所綁定的對象。
const int ci = 1024;
const int &r1 = ci;//正確,引用及其對應的對象都是常量
r1 = 42; //錯誤,r1是對于常量的引用
int &r2 = ci; //錯誤,試圖讓一個非常量去引用一個常量對象
- 因為不允許直接為ci賦值,當然也不可以通過引用去改變ci,因此,對于r2的初始化是錯誤的,假設初始化合法,就可以通過r2來改變他引用的對象的數值,這顯然是不正確的。
初始化和對const的引用
-
引用的類型必須和其所引用對象的類型是一致的,但是有兩個例外。1,初始化常量引用時候允許用任意表達式來作為初始化的數值,只要該表達式結果可以轉化為引用的類型即可。尤其,允許一個常量引用綁定非常量的對象、字面值甚至是一個一般表達式。
int i = 42;const int &r1 = i; //允許將const int& 綁定到一個普通int對象上const int &r2 = 42;//r2是一個常量的引用const int &r3 = r1 * 2;//r3是一個常量的引用int &r4 = r1 * 2; //錯誤,r4是一個普通的非常量的引用
對const引用可能引用一個并非const的對象
-
常量的引用僅僅對于可以引用可以參與的操作進行了限定,對于引用的對象的本身是不是一個常量未做限定。因為對象也可能是一個非常量,所以可以通過其他途徑來改變它的值。
int i = 42;int &r1 = i; //引用r1綁定對象iconst int &r2 = i;//r2也綁定對象i,但是不允許通過r2來修改i的數值r1 = 0; //r1并非常量,i的數值修改為0r2 = 0; //錯誤,r2是一個常量的引用,因此不可以修改引用的元素的數值
指針和const
- 與引用一樣,可以另指針指向常量或者非常量。類似于常量的引用,指向常量的指針不能用于改變其所指對象的數值
- 要想存放常量對象的地址,只能指向常量的指針。
const double pi = 3.141592653; //pi是個常量,它的值不能改變double *ptr = π //錯誤,ptr是一個普通的指針const double *cptr = π //正確,cptr可以指向一個雙精度的常量*cptr = 43;//錯誤,不能給*cptr賦值
- 指針的類型必須和所指對象類型一致,但是有兩個例外:1,允許令一個指向常量的指針指向一個非常量的對象。
double dval = 3.14;//dval是一個雙精度的浮點數,它的數值可以改變
cptr = &dval; //正確,但是不能通過cptr來改變dval的數值
const指針
-
指針是對象而引用不是,因此就像其他對象類型一樣,允許把指針本身定位常量。常量指針必須初始化,而且一旦初始化,它的值(存放在指針中的那個地址)就不可以再改變了。
-
把*放在const關鍵字之前用來說明指針是一個常量,即不變的是指指針本身的數值而不是指向的那個值
int errNum = 0;int *const curErr = &errNum; //curErr一直指向errNumconst double pi = 3.14158;const double *const pip = π //pip是一個指向常量對象的常量指針
- 遵循從右往左讀的思想
- 離curErr最近的符號是const,意味著curErr本身是一個常量對象,對象的類型由聲明符的其余部分決定。聲明符的下一個符號是*,意思是curErr是一個常量指針。同理,pip是一個常量指針,指向的對象是一個雙精度浮點類型的常量。
- 指針本身是一個常量并不意味著不能通過指針修改其所指向的數值,能否這樣做完全依賴于所指向的對象的類型。例如,如果pip是一個指向常量的常量指針,不論是pip所指的對象值還是pip自己存儲的那個地址都不能改變。如果,curErr指向的是一個一般的非常量整數,那么完全可以用curErr來修改errNum的數值。
頂層const
- 指針本身是一個對象,它又可以指向另外一個對象,因此,指針本身是不是常量以及指針所指的是不是一個常量,是兩個相互獨立的問題。用名詞頂層const表示指針本身是一個常量;而使用名詞底層const來表示指針所指的對象是一個常量。
- 頂層的const可以表示任意的對象是常量,這一點適用于任何數據類型,如算數類型、類、指針等。底層const則與指針和引用等符合類型的基本類型部分相關。
- 比較特殊的是,指針類型既可以是頂層const也可以是底層const,這一點和其他類型相比區別比較明顯。
int i = 0;int *const p1 = &i; //不可以改變p1的數值,這是一個頂層的constconst int ci = 42; //不可以改變ci的數值,這是一個頂層的constconst int *p2 = &ci; //可以改變p2的數值,這是一個底層的constconst int *const p3 = p2; //靠右邊的是頂層const,靠左邊的是底層的constconst int &r = ci; //用于聲明的const都是底層const
- 底層const限制不可以忽視。執行對象的拷貝操作的時候,拷入和烤出的對象具有相同的底層const資格,或者兩個對象的數據類型之間能夠相互轉化,一般來說非常量可以轉化為常量,反之不可以。
constexper和常量表達式
- 常量表達式是指不會改變并且在編譯的過程中能得到編譯結果的表達式。顯然,字面值屬于常量表達式,用常量表達式初始化的const對象也是常量表達式。
- 一個對象或者表達式是不是常量表達式是由它的數據類型和初始值共同決定的。
const int max_files = 20; //max_files是常量表達式const int limit = max_files + 1; //limit是常量表達式int staff_size = 27;//staff_size不是常量表達式const int sz = get_size();//sz不是常量表達式
- staff_size的初始值是一個字面值常量,但是由于他的數據類型只是一個普通的int而不是const int,所以他不屬于常量表達式。
- sz本身是一個常量,但是他的具體值直到運行的時候才可以獲取到,因此也不是常量表達式。
constexpr變量
- C++11允許將變量聲明為constexpr類型,從而使得編譯器來驗證變量的數值是否是一個常量的表達式。
- 聲明為constexpr的變量一定是一個常量,而且需要用常量表達式來初始化。
constexpr int mf = 20; //20是常量表達式constexpr int limit = mf + 1 ;// limit是常量表達式constexpr int sz = size();// 只有當size是一個constexpr函數的時候,才是一條正確的聲明語句
字面值類型
- 常量表達式的值需要在編譯的時候就得到計算,因此對聲明constexpr時用到的類型必須有所限制。因為這些類型一般比較簡單,值也比較明顯,將其稱之為字面值類型。
- 算數類型、引用和指針都是屬于字面值類型。自定義的類型、IO庫、string則不屬于字面值類型,即不可以定義為constexpr。
- 指針和引用可以被定義為constexpr類型,但是他們的初始值會受到嚴格的限制,constexpr指針的初始值必須是nullptr或者是0,或者是存儲于某個固定地址中的對象。
- 先前指出函數體內部定義的變量一般來說不會存在到固定的地址中,因此constexpr指針不可以指向這種變量。相反,因為存儲于函數體外的對象其固定的地址不變,因此可以用于初始化constexpr指針。
- 其中,允許函數定義一類有效范圍超出函數本身的變量,這類變量和定義在函數體之外的變量一樣也有固定的地址,因此也可以用于對于constexpr指針的初始化。
指針和constexpr
- 在constexpr聲明中定義了一個指針,限定符號constexpr僅僅對于指針有效,而對于指針所指向的對象本身無效。
const int *p = nullptr;//p是一個指向整型常量的指針constexpr int *q = nullptr;//q是一個指向整數的常量指針
- 與其他常量指針相類似,constexpr指針既可以指向一個常量指針,也可以指向一個非常量
const int *np = nullptr;//np是一個指向整數的常量指針,其值為空
int j = 0;
constexpr int i = 42;//i的類型是整型常量
//i和j必須定義在函數體之外constexpr const int *p = &i;//p是常量指針,指向整型常量iconstexpr int *p1 = &j;
?