C++語言const 關鍵字使用方法圖文詳解
之前一直在學習C/C++,關於const的使用,這裡出現一點,那裡出現一點。知識用時方恨少,這一段時間正好各種筆試題,其中關於const的用法也是層出不窮,所以疲於在書本上各種翻,這裡彙總一下,加深自己的印象的同時,也方便以後查閱和學習。菜鳥一個,若有錯誤,望指正!
const關鍵字
常型別是指使用型別修飾符const說明的型別,常型別的變數或物件的值是不能被更新的。不管出現在任何上下文都是為這個目的而服務的。
const使用方法
定義const物件
const修飾符可以把物件轉變成常數物件,意思就是說利用const進行修飾的變數的值在程式的任意位置將不能再被修改,就如同常數一樣使用!任何修改該變數的嘗試都會導致編譯錯誤:
注意:因為常量在定以後就不能被修改,所以定義時必須初始化:
對於類中的const成員變數必須通過初始化列表進行初始化,如下所示:
const物件預設為檔案的區域性變數
在全域性作用域裡定義非const變數時,它在整個程式中都可以訪問,我們可以把一個非const變數定義在一個檔案中,假設已經做了合適的宣告,就可以在另外的檔案中使用這個變數:
與其他變數不同,除非特別說明,在全域性作用域宣告的const變數是定義該物件的檔案的區域性變數。此變數只存在於那個檔案中,不能被其他檔案訪問。通過指定const變數為extern,就可以在整個程式中訪問const物件。
注意:非const變數預設為extern。要使const變數能夠在其他檔案中訪問,必須在檔案中顯式地指定它為extern。
const 引用
const引用是指向const物件的引用:
可以讀取但不能修改refVal,因此,任何對refVal的賦值都是不合法的。這個限制有其意義:不能直接對ival賦值,因此不能通過使用refVal來修改ival。同理,用ival初始化ref2也是不合法的:ref2是普通的非const引用,因此可以用來修改ref2指向的物件的值。通過ref2對ival賦值會導致修改const物件的值,為防止這樣的修改,需要規定將普通的引用繫結到const物件是不合法的。 const 引用可以初始化為不同型別的物件或者初始化為右值。如字面值常量:
同樣的初始化對於非const引用卻是不合法的,而且會導致編譯時錯誤。其原因非常微妙,值得解釋一下。觀察將引用繫結到不同的型別時所發生的事情,最容易理解上述行為。對於下一段程式碼:
編譯器會將這些程式碼轉換為一下形式:
我們發現編譯器會建立一個int型的暫時變數儲存dval,然後將ri繫結到temp上。
注意:引用在內部存放的是一個物件的地址,它是該物件的別名。對於不可定址的值,如文字常量,以及不同型別的物件,編譯器為了實現引用,必須生成一個臨時物件,引用實際上指向該物件,但使用者不能訪問它。
如果ri不是const,那麼可以給ri賦一新值。這樣做不會修改dval的,而是修改了temp。期望對ri賦值會修改dval的程式設計師會發現dval沒有被修改。僅允許const引用繫結到需要臨時使用的值完全避免了這個問題,直接告訴程式設計師不能修改,因為const引用是隻讀的哈~(其實就是避免程式設計師心理預期產生反差。。。)
注意:非const引用只能繫結到與該引用相同型別的物件。 const引用則可以繫結到不同但相關的型別的物件或繫結到右值。
const物件的動態陣列
如果我們在自由儲存區中建立的陣列儲存了內建型別的const物件,則必須為這個陣列提供初始化: 因為陣列元素都是const物件,無法賦值。實現這個要求的唯一方法是對陣列做值初始化。
C++允許定義類型別的const陣列,但該類型別必須提供預設建構函式:
這裡便會呼叫string類的預設建構函式初始化陣列元素。
指標和const限定符的關係(重點!!!!!!!非常容易搞混)
const限定符和指標結合起來常見的情況有以下幾種。
指向常量的指標(指向const物件的指標)
C++為了保證不允許使用指標改變所指的const值這個特性,強制要求這個指標也必須具備const特性:
這裡cptr是一個指向double型別const物件的指標,const先頂了cptr指向的物件的型別,而並非cptr本身,所以cptr本身並不是const。所以定義的時候並不需要對它進行初始,如果需要的話,允許給cptr重新賦值,讓其指向另一個const物件。但不能通過cptr修改其所指物件的值。
而我們將一個const物件的地址賦給一個普通的非const指標也會導致編譯錯誤。
不能使用void*指標儲存const物件的地址,必須使用const void*型別的指標儲存const物件的地址。
下面令人頭暈的一個問題來了----à 允許把非const物件的地址賦給指向const物件的指標,例如:
但是我們不能通過cptr指標來修改dval的值!!!即使它指向的是非const物件。
然後,我們一定要知道,不能使用指向const物件的指標修改基礎物件,然而如果該指標指向了非const物件,可用其他方式修改其所指的物件,所以事實上,可以修改const指標所指向的值的,但是不能通過const物件指標來進行而已!如下所示:
通過以上,我們知道指向const物件的指標 確切的講: 自以為指向const的指標!
常指標(const指標)
C++中還提供了const指標——本身的值不能被修改。
我們可以從右往左把上述定義語句讀作"指向int型物件的const指標"。與其他const量一樣,const指標的值不能被修改,這意味著不能使curErr指向其他物件。Const指標也必須在定義的時候初始化。
指標本身是const的試試並沒有說明是否能用改真真修改其所指向的物件的值。指標物件的值能否修改完全取決於該物件的型別。
指向常量的常指標(指向const物件的const指標)
如下可以這樣定義:
這樣pi_ptr首先是一個const指標,然後其指向一個const物件~~~
函式和const限定符的關係(另一難點!!!理解)
類中的const成員函式(常量成員函式)
在一個類中,任何不會修改資料成員的函式都應該宣告為const型別。如果在編寫const成員函式時,不慎修改了資料成員,或者呼叫了其它非const成員函式,編譯器將指出錯誤,這無疑會提高程式的健壯性。使用const關鍵字進行說明的成員函式,稱為常成員函式。只有常成員函式才有資格操作常量或常物件,沒有使用const關鍵字說明的成員函式不能用來操作常物件。常成員函式說明格式如下:
<型別說明符> <函式名> (<引數表>) const;
其中,const是加在函式說明後面的型別修飾符,它是函式型別的一個組成部分,因此,在函式實現部分也要帶const關鍵字。下面舉一例子說明常成員函式的特徵。
函式過載
既然const是定義為const函式的組成部分,那麼就可以通過新增const實現函式過載咯。
其中print成員函式就實現了兩個版本~~~ 過載哦,輸出結果為 5,52。 const物件預設呼叫const成員函式。
const 修飾函式返回值
const修飾函式返回值其實用的並不是很多,它的含義和const修飾普通變數以及指標的含義基本相同。如下所示:
一般情況下,函式的返回值為某個物件時,如果將其宣告為const時,多用於操作符的過載。通常,不建議用const修飾函式的返回值型別為某個物件或對某個物件引用的情況。原因如下:如果返回值為某個物件為const(const A test = A 例項)或某個物件的引用為const(const A& test = A例項) ,則返回值具有const屬性,則返回例項只能訪問類A中的公有(保護)資料成員和const成員函式,並且不允許對其進行賦值操作,這在一般情況下很少用到。
const修飾函式引數
傳遞過來的引數在函式內不可以改變(無意義,因為Var本身就是形參)
引數指標所指內容為常量不可變
引數指標本身為常量不可變(也無意義,因為char* Var也是形參)
引數為引用,為了增加效率同時防止修改。修飾引用引數時:
這樣的一個const引用傳遞和最普通的函式按值傳遞的效果是一模一樣的,他禁止對引用的物件的一切修改,唯一不同的是按值傳遞會先建立一個類物件的副本,然後傳遞過去,而它直接傳遞地址,所以這種傳遞比按值傳遞更有效.另外只有引用的const傳遞可以傳遞一個臨時物件,因為臨時物件都是const屬性,且是不可見的,他短時間存在一個區域性域中,所以不能使用指標,只有引用的const傳遞能夠捕捉到這個傢伙.
const限定符和static的區別
const定義的常量在超出其作用域之後其空間會被釋放,而static定義的靜態常量在函式執行後不會釋放其儲存空間。
static表示的是靜態的。類的靜態成員函式、靜態成員變數是和類相關的,而不是和類的具體物件相關的。即使沒有具體物件,也能呼叫類的靜態成員函式和成員變數。一般類的靜態函式幾乎就是一個全域性函式,只不過它的作用域限於包含它的檔案中。
在C++中,static靜態成員變數不能在類的內部初始化。在類的內部只是宣告,定義必須在類定義體的外部,通常在類的實現檔案中初始化,如:double Account::Rate=2.25; static關鍵字只能用於類定義體內部的宣告中,定義時不能標示為static
在C++中,const成員變數也不能在類定義處初始化,只能通過建構函式初始化列表進行,並且必須有建構函式。
const資料成員,只在某個物件生存期內是常量,而對於整個類而言卻是可變的。因為類可以建立多個物件,不同的物件其const資料成員的值可以不同。所以不能在類的宣告中初始化const資料成員,因為類的物件沒被建立時,編譯器不知道const資料成員的值是什麼。
const資料成員的初始化只能在類的建構函式的初始化列表中進行。要想建立在整個類中都恆定的常量,應該用類中的列舉常量來實現,或者static const。
const成員函式主要目的是防止成員函式修改物件的內容。即const成員函式不能修改成員變數的值,但可以訪問成員變數。當方法成員函式時,該函式只能是const成員函式。
static成員函式主要目的是作為類作用域的全域性函式。不能訪問類的非靜態資料成員。類的靜態成員函式沒有this指標,這導致:
1、不能直接存取類的非靜態成員變數,呼叫非靜態成員函式
2、不能被宣告為virtual
其中關於static、const、static cosnt、const static成員的初始化問題:
類裡的const成員初始化
在一個類裡建立一個const時,不能給他初值
類裡的const成員初始化
類中的static變數是屬於類的,不屬於某個物件,它在整個程式的執行過程中只有一個副本,因此不能在定義物件時 對變數進行初始化,就是不能用建構函式進行初始化,其正確的初始化方法是:
資料型別 類名::靜態資料成員名=值
類裡的static cosnt 和 const static成員初始化(這兩種寫法是一致的!!)
最後通過一個完整的例子展示以上結果:
const 的難點
如果函式需要傳入一個指標,面試官可能會問是否需要為該指標加上const,把const加在指標不同的位置有什麼區別;如果寫的函式需要傳入的引數是一個複雜型別的例項,面試官可能會問傳入值引數或者引用引數有什麼區別,什麼時候需要為傳入的引用引數加上const。 const是用來宣告一個常量的,當你不想讓一個值被改變時就用const,const int max和int const max 是沒有區別的,都可以。不涉及到指標const很好理解。一旦涉及到指標,則比較容易出問題。
如果const位於星號的左側,則const就是用來修飾指標所指向的變數,即指標指向的物件為常量;如果const位於星號的右側,const就是修飾指標本身,即指標本身是常量。
因此,[1]和[2]的情況相同,都是指標所指向的內容為常量(const放在變數宣告符的位置無關),這種情況下不允許對內容進行更改操作,如不能*a = 3 ;[3]為指標本身是常量,而指標所指向的內容不是常量,這種情況下不能對指標本身進行更改操作,如a++是錯誤的;[4]為指標本身和指向的內容均為常量。
如果理解了上面的所有資訊,我相信應該問題不大。僅供參考~~~ 希望大家熱烈討論哈。 好了,先寫到這裡,在以後需要補充我再加東西吧。。。敲得手疼~~~ ~!!!
下面是其他網友的補充
在類中,如果你不希望某些資料被修改,可以使用const關鍵字加以限定。const 可以用來修飾成員變數、成員函式以及物件。
一、const 成員變數
const 成員變數的用法和普通 const 變數的用法相似,只需要在宣告時加上 const 關鍵字。初始化 const 成員變數只有一種方法,就是通過引數初始化表
二、const 成員函式
const 成員函式可以使用類中的所有成員變數,但是不能修改成員變數,這種措施主要還是為了保護資料而設定的。const 成員函式也稱為常成員函式。
常成員函式需要在宣告和定義的時候在函式頭部的結尾都加上 const 關鍵字,如:
class Student{ public: Student(char *name,int age,float score); void show(); //宣告常成員函式 char *getname() const; int getage() const; float getscore() const; private: char *m_name; int m_age; float m_score; }; Student::Student(char *name,float score): m_name(name),m_age(age),m_score(score){ } void Student::show(){ cout<<m_name<<"的年齡是"<<m_age<<",成績是"<<m_score<<endl; } //定義常成員函式 char * Student::getname() const{ return m_name; } int Student::getage() const{ return m_age; } float Student::getscore() const{ return m_score; }
三.const物件
一旦將物件定義為const 物件之後,不管是哪種形式,則該物件就只能訪問被 const 修飾的成員了(包括 const 成員變數和 const 成員函式),因為非 const 成員可能會修改物件的資料(編譯器也會這樣假設),C++禁止這樣做。
例:
#include <iostream> using namespace std; class Student{ public: Student(char *name,float score); public: void show(); char *getname() const; int getage() const; float getscore() const; private: char *m_name; int m_age; float m_score; }; Student::Student(char *name,m_score(score){ } void Student::show(){ cout<<m_name<<"的年齡是"<<m_age<<",成績是"<<m_score<<endl; } char * Student::getname() const{ return m_name; } int Student::getage() const{ return m_age; } float Student::getscore() const{ return m_score; } int main(){ const Student stu("小明",15,90.6); //stu.show(); //error cout<<stu.getname()<<"的年齡是"<<stu.getage()<<",成績是"<<stu.getscore()<<endl; const Student *pstu = new Student("李磊",16,80.5); //pstu -> show(); //error cout<<pstu->getname()<<"的年齡是"<<pstu->getage()<<",成績是"<<pstu->getscore()<<endl; return 0; }
好了這篇文章就介紹到這了,看完了這些基本上就可以理解了