概念
const就是為了直接表達“不變化的值”這一概念。也就是說該值只可讀,不可直接寫。
由於不可以修改,所以const常量在聲明的時候必須初始化
const int a; //error extern const int ext_a;
作用
- 可以定義const常量,具有不可變性
const int a = 4 ; //a = 5; //error
- 便於類型檢查,使編譯器對處理內容有更多了解
如 a = 5 在編譯的時候可以將對const常量進行修改的部分檢查出來 - 保護被修飾的內容
我們一直在建議不要修改函數的參數值,便於調試、維護,以及減少不必要的錯誤,但函數的調用者與函數的編寫者,所以無法人為的保證傳入的參數無法被修改,此時就運用const,運用編譯器對代碼進行檢查。主要還是和引用參數聯合使用,如果只是修改參數對調用者沒影響還好說,否則...(后續詳談)
void fun1(const int a) { //a=5; } void fun2(const ClassA& a) { //a.xx=xx }
- 提高代碼的可讀性、維護性
運用const常量替換代碼中出現的比較多的各種文字量,個人認為代碼中出現數字0、1,閱讀時還能理解,但是要是突然出來一個2、5、8,這是什么意思呢?可能自己寫的代碼過一陣子自己也不清楚了,此時我們就可以運用const常量或enum起一個合適的名字替代這些不易理解的數字。如果是字符串的話就只能運用const了,呃,或者是宏#define(個人不太喜歡宏,原因1.宏易出錯,不易理解;2.編譯器無法檢查類型;3.C++不建議使用宏) - 函數重載
兩個重載函數必須在下列一個或兩個方面有所區別:1、參數數量不同2、參數類型不同
如下 funA以上兩個方面都不滿足,但是卻可以重載,const函數對應const對象使用(后續詳談) -
class ClassA { public: void funA() {
//... } void funA() const { //... } };
const與指針
The C++ Programming Language里面給出過一個助記的方法: 把一個聲明從右向左讀。
char * const p1; //p1 is a const pointer to char const char *p2; //p2 is a pointer to const char const char * const p3; //p3 is a const pointer to const char
Effective C++同樣給出了一種方式,如果關鍵字const在星號的左邊,表示被指物是常量;如果出現在星號右邊,表示指針自身是常量;如果出現在兩邊,表示被指物和指針二者都是常量。其實指針只要分清指針本身與所指向的內容即可。個人覺得不用糾結與有些書上的叫法什么“指針常量”,“常量指針”,這都是在翻譯的過程中帶來的歧義。
如p1是一個const pointer,所以在聲明的時候必須初始化,否則編譯不通過,而*p1則是char類型。
p2是一個pointer但不是const,*P2才是const類型,所以p2可以不用初始化。
那么p1聲明指向的為char 是否可以指向const char 呢?p2聲明指向的為const char 是否可以指向char類型呢?
const char ca = 'a' ; char b= 'b'; char *p; //p=&ca; //error p=&b; char * const p1=&b; //char * const p1=&ca; //error const char *p2; p2=&ca; p2=&b; //ok const char * const p3=&b; //ok
因為對承諾不修改的內容要被修改,肯定是有問題的;但對可修改的內容不做修改沒有任何問題。如上面ca不可修改,*p可以修改,所以p=&ca錯誤;而b可以修改,*p1不可以修改,p=&b對b不會造成不良影響,所以可以通過。
那么是不是非const類型可以直接賦值與const類型呢?而const類型不能直接賦值與非const類型呢?
ClassA ca; const ClassA cst_b; ClassA cc=cst_b; //copy構造函數可以理解 ClassA c2; c2=cst_b; //?? const ClassA cst_d=ca; int a=1; const int b=a; int c=b; //??
以上代碼完全可以編譯通過,那該怎么解釋呢?個人理解為如果賦值后的對象不對原對象代碼不良影響,都是可以通過的。如int c=b;c的修改不會修改b;c2=cst_b,調用ClassA的賦值操作ClassA& operator=(const ClassA&),也同樣不會對原cst_b修改。
const與引用
引用與指針有所不同,因為引用本身可以看作是一個常量指針,引用在聲明是必須初始化,且不可以在引用值其他對象。所以引用只存在一種形式。const type& 而type& 與type& const 等價。const 引用主要運用在函數的參數傳遞方面。Effective C++ 第20條 Perfer pass-by-reference-to-const to pass-by-value.
int a; int & ref1=a; const int & ref2 =a; int & const ref3=a; //沒必要
const與函數
函數與const 的關系可以從三部分來看,返回值、參數、函數自身
- const修飾返回值
class ClassA { public: int a; ClassA():a(1) { } const int funA() //沒意義,因為返回值本身就無法修改 與int funA()一樣,而且也不存在funA()=4這種寫法 { return a; } int * const funB() //與funA一樣,const的限定的是返回值的指針,也無法修改 。與int * funB() 一樣 { return &a; } const int * funC() //const修飾的是返回指針所指內容,所以該函數的接收這也必須為const int*類型 { return &a; } const int &funD() //返回引用,該引用不可以修改 { return a; } void test() { int re=funA(); int *p= funB(); //int *p1=funC(); //error const int *p2=funC(); //int& c=funD(); //error const int& a2=funD(); } };
funA、funB其實實際意義都不大,因為不論返回值是不是const類型,函數調用者都不可能修改返回值的實際值。而函數調用者該不該用const與函數本身沒關系。
cosnt自定義類型返回值,往往可以降低因函數調用者錯誤而造成的以外,而又不至於放棄安全性和高效性ClassA funCA() { ClassA a; return a; } int _tmain(int argc, _TCHAR* argv[]) { ClassA ca; funCA()=ca; }
funCA()=ca;這句話具體有什么實際意義呢?沒意義,但是就是可以編譯通過,此時就可以將funCA返回值限定為const ClassA,這樣的寫法首先編譯器都不通過。
- const修飾函數參數
Effective C++ 至於const參數,沒什么特別新穎的觀念,它們不過就像local const對象一樣,你應該在必要使用它們的時候使用它們。除非你有需要修改參數或者loacl對象,否則請將他們聲明為const。int funA(const int a); int funB(int * const p); int funC(const int *p); int funD(const int& ra);
- const修飾成員函數自身
將const實施於成員函數的目的,是為了確認該成員函數可以作用與const對象身上。這類函數有兩個理由顯得重要
1.是class接口比較容易被理解。2.const對象使用class ClassA { public: int a; ClassA():a(1) { } void fun() { a=5; cout<<"this is not a const function"<<endl; } void fun() const { // a=5; //error cout<<"this is a const function"<<endl; } }; int _tmain(int argc, _TCHAR* argv[]) { ClassA ca; ca.fun(); //this is not a const function const ClassA cb; cb.fun(); //this is a const function }
const對象只能調用const函數,且const函數不能對成員函數做修改。若將void fun()函數刪掉,則編譯不通過錯誤提示:ClassA::fun”: 不能將“this”指針從“const ClassA”轉換為“ClassA &;若將void fun() const 函數刪掉,則ca.fun()調用const fun。
今天快下班了,先寫這些吧,明天補充總結!