聲明或定義的格式以下:數組
const <類型說明符> <變量名> = <常量或常量表達式>; [1] <類型說明符> const <變量名> = <常量或常量表達式>; [2] [1]和[2]的定義是徹底等價的。
例如:函數
整形int(或其餘內置類型:float,double,char)this
const int bufSize = 512;
或者編碼
int const bufSize = 512;
由於常量在定義後就不能被修改,因此定義時必須初始化。spa
bufSize = 128; // error:attempt to write to const object const string cntStr = "hello!"; // ok:initialized const i, j = 0; // error: i is uninitialized const
非const變量默認爲extern。指針
const 對象默認爲文件的局部變量。要使const變量可以在其餘的文件中訪問,必須顯式地指定它爲extern。code
例如:對象
const int bufSize = 512; // 做用域只限於定義此變量的文件 extern const int bufSize = 512; // extern用於擴大做用域,做用域爲整個源程序(只有extern 位於函數外部時,才能夠含有初始化式)
聲明或定義的格式以下:內存
const <類型說明符> <數組名>[<大小>]…… [1] <類型說明符> const <數組名>[<大小>]…… [2] [1]和[2]的定義是徹底等價的。
例如:作用域
整形int(或其餘內置類型:float,double,char)
const int cntIntArr[] = {1,2,3,4,5};
或者
int const cntIntArr[] = {1,2,3,4,5}; struct SI { int i1; int i2; }; const SI s[] = {{1,2},{3,4}}; // 上面的兩個const都是變量集合,編譯器會爲其分配內存,因此不能在編譯期間使用其中的值(例如:int temp[cntIntArr[2]],這樣的話編譯器會報告不能找到常量表達式)
聲明或定義的格式以下:
const <類型說明符> &<變量名> = …… [1] <類型說明符> const &<變量名> = …… [2] [1]和[2]的定義是徹底等價的。
例如:
const int i = 128; const int &r = i;(或者 int const &r = i;)
const 引用就是指向const 對象的引用。
普通引用不能綁定到const 對象,但const 引用能夠綁定到非const 對象。
const int ii = 456; int &rii = ii; // error int jj = 123; const int &rjj = jj; // ok
非const 引用只能綁定到與該引用同類型的對象。
const 引用則能夠綁定到不一樣但相關的類型的對象或綁定到右值。
例如:
1.const int &r = 100; // 綁定到字面值常量 2.int i = 50; const int &r2 = r + i; // 引用r綁定到右值 3.double dVal = 3.1415; const int &ri = dVal; // 整型引用綁定到double 類型
編譯器會把以上代碼轉換成以下形式的編碼:
int temp = dVal; // create temporary int from double const int &ri = temp; // bind ri to that temporary
1.指向const 對象的指針(指針所指向的內容爲常量)
聲明或定義的格式以下(定義時能夠不初始化):
const <類型說明符> *<變量名> …… [1] <類型說明符> const *<變量名> …… [2] [1]和[2]的定義是徹底等價的。
例如:
const int i = 100; const int *cptr = &i;
或者
int const *cptr = &i; [cptr 是指向int 類型的const 對象的指針] 容許把非const 對象的地址賦給指向const 對象的指針,例如: double dVal = 3.14; // dVal is a double; its value can be change const double *cdptr = &dVal; // ok;but can't change dVal through cdptr
不能使用指向const 對象的指針修改基礎對象。然而若是該指針指向的是一個沒const 對象(如cdptr),可用其餘方法修改其所指向的對象。
如何將一個const 對象合法地賦給一個普通指針???
例如:
const double dVal = 3.14; double *ptr = &dVal; // error double *ptr = const_cast<double*>(&dVal); // ok: const_cast是C++中標準的強制轉換,C語言使用:double *ptr = (double*)&dVal;
2.const 指針(指針自己爲常量)
聲明或定義的格式以下(定義時必須初始化):
<類型說明符> *const <變量名> = ……
例如:
int errNumb = 0; int iVal = 10; int *const curErr = &errNumb; [curErr 是指向int 型對象的const 指針] 指針的指向不能被修改。 curErr = &iVal; // error: curErr is const 指針所指向的基礎對象能夠修改。 *curErr = 1; // ok:reset value of the object(errNumb) which curErr is bind
3.指向const 對象的const 指針(指針自己和指向的內容均爲常量)
聲明或定義的格式以下(定義時必須初始化):
const <類型說明符> *const <變量名> = ……
例如:
const double pi = 3.14159; const double dVal = 3.14; const double *const pi_ptr = π [pi_ptr 是指向double 類型的const 對象的const 指針] 指針的指向不能被修改。 pi_ptr = &dVal; // error: pi_ptr is const 指針所指向的基礎對象也不能被修改。 *pi_ptr = dVal; // error: pi is const
1.修飾函數的參數
class A; void func1(const int i); // i不能被修改 void func3 (const A &rA); // rA所引用的對象不能被修改 void func2 (const char *pstr); // pstr所指向的內容不能被修改
2.修飾函數的返回值
返回值:const int func1(); // 此處返回int 類型的const值,意思指返回的原函數裏的變量的初值不能被修改,可是函數按值返回的這個變量被製成副本,能不能被修改就沒有了意義,它能夠被賦給任何的const或非const類型變量,徹底不須要加上這個const關鍵字。 [*注意*]但這隻對於內部類型而言(由於內部類型返回的確定是一個值,而不會返回一個變量,不會做爲左值使用,不然編譯器會報錯),對於用戶自定義類型,返回值是常量是很是重要的(後面在類裏面會談到)。 返回引用:const int &func2(); // 注意千萬不要返回局部對象的引用,不然會報運行時錯誤:由於一旦函數結束,局部對象被釋放,函數返回值指向了一個對程序來講再也不有效的內存空間。 返回指針:const int *func3(); // 注意千萬不要返回指向局部對象的指針,由於一旦函數結束,局部對象被釋放,返回的指針變成了指向一個再也不存在的對象的懸垂指針。
class A { public: void func(); void func() const; const A operator+(const A &) const; private: int num1; mutable int num2; const size_t size; };
1.修飾成員變量
const size_t size; // 對於const的成員變量, [1]必須在構造函數裏面進行初始化; [2]只能經過初始化成員列表來初始化; [3]試圖在構造函數體內對const成員變量進行初始化會引發編譯錯誤。
例如:
A::A(size_t sz):size(sz) // ok:使用初始化成員列表來初始化 { } A::A(size_t sz)
2.修飾類成員函數
void func() const; // const成員函數中不容許對數據成員進行修改,若是修改,編譯器將報錯。若是某成員函數不須要對數據成員進行修改,最好將其聲明爲const 成員函數,這將大大提升程序的健壯性。
const 爲函數重載提供了一個參考
class A { public: void func(); // [1]:一個函數 void func() const; // [2]:上一個函數[1]的重載 …… }; A a(10); a.func(); // 調用函數[1] const A b(100); b.func(); // 調用函數[2]
如何在const成員函數中對成員變量進行修改???
下面提供幾種方式(只提倡使用第一種,其餘方式不建議使用)
(1)標準方式:mutable
class A { public: A::A(int i):m_data(i){} void SetValue(int i){ m_data = i; } private: mutable int m_data; // 這裏處理 };
(2)強制轉換:static_cast
class A { public: A::A(int i):m_data(i){} void SetValue(int i) { static_cast<int>(m_data) = i; } // 這裏處理 private: int m_data; };
(3)強制轉換:const_cast
class A { public: A::A(int i):m_data(i){} void SetValue(int i) { const_cast<A*>(this)->m_data = i; } // 這裏處理 private: int m_data; };
(4)使用指針:int *
class A { public: A::A(int i):m_data(i){} void SetValue(int i) { *m_data = i; } // 這裏處理 private: int *m_data; };
(5)未定義的處理方式
class A { public: A::A(int i):m_data(i){} void SetValue(int i) { int *p = (int*)&m_data; *p = i } // 這裏處理 private: int m_data; }; 注意:這裏雖說能夠修改,但結果是未定義的,避免使用!
3.修飾類對象
const A a; // 類對象a 只能調用const 成員函數,不然編譯器報錯。
4.修飾類成員函數的返回值
const A operator+(const A &) const; // 前一個const 用來修飾重載函數operator+的返回值,可防止返回值做爲左值進行賦值操做。 例如: A a; A b; A c; a + b = c; // errro: 若是在沒有const 修飾返回值的狀況下,編譯器不會報錯。
1.要大膽的使用const,這將給你帶來無盡的益處,但前提是你必須搞清楚原委;
2.要避免最通常的賦值操做錯誤,如將const變量賦值,具體可見思考題;
3.在參數中使用const應該使用引用或指針,而不是通常的對象實例,緣由同上;
4.const在成員函數中的三種用法(參數、返回值、函數)要很好的使用;
5.不要輕易的將函數的返回值類型定爲const;
6.除了重載操做符外通常不要將返回值類型定爲對某個對象的const引用;
1.能夠定義const常量,具備不可變性。
例如:
const int Max=100; int Array[Max];
2.便於進行類型檢查,使編譯器對處理內容有更多瞭解,消除了一些隱患。
例如:
void f(const int i) { .........} 編譯器就會知道i是一個常量,不容許修改;
3.能夠避免意義模糊的數字出現,一樣能夠很方便地進行參數的調整和修改。
同宏定義同樣,能夠作到不變則已,一變都變!如(1)中,若是想修改Max的內容,只須要:
const int Max=you want;
便可!
4.能夠保護被修飾的東西,防止意外的修改,加強程序的健壯性。
仍是上面的例子,若是在函數體內修改了i,編譯器就會報錯;
例如:
void f(const int i) { i=10;//error! }
5.爲函數重載提供了一個參考。
class A { ...... void f(int i) {......} file://一個函數 void f(int i) const {......} file://上一個函數的重載 ...... };
6.能夠節省空間,避免沒必要要的內存分配。
例如: #define PI 3.14159 file://常量宏 const doulbe Pi=3.14159; file://此時並未將Pi放入ROM中 ...... double i=Pi; file://此時爲Pi分配內存,之後再也不分配! double I=PI; file://編譯期間進行宏替換,分配內存 double j=Pi; file://沒有內存分配 double J=PI; file://再進行宏替換,又一次分配內存!
const定義常量從彙編的角度來看,只是給出了對應的內存地址,而不是象#define同樣給出的是當即數,因此,const定義的常量在程序運行過程當中只有一份拷貝,而#define定義的常量在內存中有若干個拷貝。
7.提升了效率。
編譯器一般不爲普通const常量分配存儲空間,而是將它們保存在符號表中,這使得它成爲一個編譯期間的常量,沒有了存儲與讀內存的操做,使得它的效率也很高。
{ size = sz; // error:試圖在構造函數體內對const成員變量進行初始化 }