正所謂酒足思淫慾,當衣食無憂的時候自然會產生很多的歪想法,就像當官一樣,權力越大,越容易腐敗。
《effective c++》中第三條:盡量使用const。這就說明使用const是有很大的好處的,const就是把權力盡可能控制一下,這樣就會減免很多出bug的機會。因為乙個專案不可能只有乙個人開發,即使是乙個人開發,當涉及到檔案多的時候,就會容易出現差錯,如果在另外乙個檔案中修改了乙個本不應該修改的變數,就會出現很大的錯誤。
const就是將修飾的部分確定為常,所謂常就是不讓修改,比如常量,常物件,常指標等等。當我們試圖修改這些常量的時候,編譯器就會報錯,就會阻止錯誤的發生。下面來看看有哪些const用法。
一、常量
宣告常量的形式:const 資料型別 資料名;
常量就是不允許修改其值,定義的時候必須賦初始值,不允許再次賦值,這個很好理解。那乙個類中也可能會有常資料成員,而我們在類體中往往宣告為const,初始化則是在建構函式中,這個時候建構函式的寫法就有規定了,看下面的程式:
class test
// 如果是test(int x, int y),編譯就會出錯
private:
const int a; // 宣告常資料成員
float b;
};int main()
看著這個建構函式有點彆扭,這才是初始化,而後面那種錯誤的寫法就是我們經常寫的建構函式,這個只是賦值,但是我們的常資料成員是不能給賦值操作的。所以只能寫成第一種寫法,而且強烈推薦將建構函式寫成這種形式。在main函式裡面定義了乙個常變數t,定義的時候必須賦初始值,以後都不能再對t進行賦值操作了,否則編譯會出錯。這裡還有個宣告和定義的區別,這也是乙個比較難理解的概念,類體裡面是宣告了乙個常資料成員,而main函式裡則是定義了乙個常變數,查查資料好好體會一下。
二、常物件
宣告常物件的兩種形式:const 類名 物件名(引數列表);
類名 const 物件名(引數列表);
什麼是常物件呢,就是物件不允許改變,也就是說,不能修改常物件的任何成員變數,當乙個物件定義為常物件的時候,任何修改其成員變數的操作都會報編譯錯誤的。看看下面這個程式:
#includeusing namespace std;
class test // 推薦這種寫法
void print()
private:
int a;
float b;
};int main()
有人說,我自己寫的程式幹嘛沒事在print()函式裡面寫個修改x值的**啊,吃飽了撐著。說的對,正常情況下不會這樣,但這也只是你一廂情願罷了,你的程式就允許這樣寫,而且這樣寫這一行是完全沒問題的,這就為出錯埋下了伏筆。但有時候確實需要這樣寫,比如我就要每次輸出x都為10,這就要看自己程式的必要性了。所以我們將這一行刪掉,不改變常物件的成員變數就ok了,所以我們定義物件的時候,如果不需要修改它的成員變數的時候就定義為常物件。這個問題是解決了,但是我們改了再編譯一次,還是會報錯,那就是常物件只能呼叫常成員函式。
三、常成員函式
宣告常成員函式的形式:返回值型別 成員函式名(形參列表) const;
上面提到的,當test2呼叫print()函式的時候就會報錯,這是因為test2是常物件,而print()函式是非常成員函式,所以不能呼叫。為啥嘞,定義成常物件,當然不想物件的成員變數被修改,非常成員函式是允許修改該類的成員變數的,所以二者衝突了。看下面這個程式:
#includeusing namespace std;
class test
void print() const
void seta(int x)
private:
int a;
float b;
};int main()
上面程式中,x=10這行編譯就會出錯,因為函式為const型,所以是不允許修改類的成員變數的,所以當我們的函式沒有涉及到成員變數的賦值操作,將其宣告為const型是不是更安全呢。test2是非常物件,即可以呼叫非常成員函式,也可以呼叫常成員函式,這個很好理解的。
四、指向物件的常指標
宣告常指標的形式:類名 * const 指標變數名;
指向物件的常指標,指標是常,就說明指標變數是不能改變的,在定義乙個常指標的時候就必須要賦初始值,以後再不能對其修改,也就是說這個指標再不能指向其他的物件了,但是指向的這個物件是可以改變屬性值的。看下面這個程式:
#includeusing namespace std;
class test
void print()
void seta(int x)
private:
int a;
float b;
};int main()
呼叫seta()方法對物件test1的屬性值進行修改是可以的,因為test1不是常物件,它的屬性值是可變的,除非該屬性宣告為const。但是在p = &test2的時候,就會報編譯錯誤,因為p已經定義是常,所以它的值是不能修改的,也就是說不能再指向其他的物件。
五、指向常物件的指標
宣告指向常物件的指標的形式:const 類名 * 指標名;
指向常物件的指標,這裡相當於在上層將接受點縮小,就是說不允許通過指標來修改指向物件的屬性值,這樣可能有點難以理解,結合下面的例子再來說吧:
#includeusing namespace std;
class test
void print()
void seta(int x)
private:
int a;
float b;
};int main()
看了程式裡面的注釋是不是感覺更加鬱悶了,又是物件又是常物件又是指標又是指向常物件的指標又是指向常物件的常指標,阿彌陀佛。這樣來理解吧,其實我們定義乙個指標的目的,當然是通過指標來操縱指向物件,那麼如果我們的物件沒做什麼特殊的限制,那麼我們想要保護該物件就只能從操縱者指標這裡來設定一些限制。就相當於乙個瓶子,我們不想讓瓶子裡面放大的東西,但是我們又想讓瓶子大一點好容納更多的東西,這樣的話我們就把瓶口做小一點,不讓大的東西進來。這裡也是一樣,如果我們定義乙個一般的物件test2,那麼我們選擇性的給他設定瓶頸,也就是指標,既可以用一般指標指向它,也可以用指向常物件的指標p1指向它,如果是p1的話,那麼我們就不能通過p1來修改它的屬性值,這樣是不是就達到了瓶頸的目的呢。但是對於乙個常物件,本來瓶子就小,當然不能有更大的瓶頸,所以只能用指向常物件的指標來指向它。最後又有個指向常物件的常指標,這是乙個最特別的指標,我們既不能修改指標的值又不能通過指標來修改物件的屬性值,這個就是最小的瓶頸,這些東西呢,我們都要根據實際情況來用,根據實際情況來設定我們的瓶頸,讓哪些大小的東西進來,不讓哪些大小的東西進來。
六、常形參
在定義函式的時候,常形參有兩種形式,一種是指向常變數的指標,一種是常引用,其實二者的作用都是一樣的,都是防止在函式體中改變實參變數的值。看下面這個程式吧:
void swap1(int a, int b)
void swap2(int *a, int *b)
void swap3(int &a, int &b)
void print(int a, int b)
int main()
這三個方法的目的都是用來交換a和b的位置,但是具體能不能成功就要看程式執行的結果了,執行完程式後,發現x2和y2,x3和y3的位置是交換了,但是x1和y1卻沒有,這個我們都了解過引數的值傳遞和引用傳遞吧,這個不用多說,在swap1方法中只是傳遞了乙個複製的值,實參當然不會交換。swap2形參是指標,這也是值傳遞,只不過傳遞的是實參的記憶體位址,交換的還是實際引數。swap3傳遞的是引用,也就是實際引數,所以也會交換成功。我們看出,在swap2和swap3方法裡面都對實際引數進行了修改,如果我們不想修改傳遞的實際引數怎麼辦呢,只要在形參前面加乙個const來修飾,這樣的話在函式體裡面是不能修改引數的值的,這樣就起了乙個保護的作用,具體哪些地方需要保護就根據自己的程式決定,當前這個當然不用保護,不然我們怎麼達到交換位置的目的呢。
大概我們經常接觸到的const用法就這些,盡量多用const可以讓我們的程式更加的安全,盡量的限制了bug的產生,細節決定成敗。
C const關鍵字分析詳解
目錄 在c語言中,被const修飾的是乙個不能被修改的變數。include 1 void test1 void test2 void test3 int main 結論 1.const如果放在 的左邊,修飾的是指標指向的內容,保證指標指向的內容不能通過指標來改 變。但是指標變數本身的內容可變。2.c...
c const關鍵字 總結
在全域性作用於里定義非const變數時,在整個程式中都可以訪問.除非特別說明,在全域性作用於的宣告的const變數時定義該物件的檔案的區域性變數.死變數只存在與那個檔案中,不能被其他檔案訪問.非const變數預設為extern.要使const變數能夠在其他地方的檔案中訪問,必須顯示指定為extern...
c const關鍵字總結
const int max size 1024 這是最常見的用法,大家都沒問題,const在定義的時候需要初始化。有兩點需要注意一下 a const變數預設是區域性變數,如果需要全域性訪問,需要顯示地extern b const int max size 1024 與 define max size...