C++中const用法小結(jié)
const在C++中使用十分廣泛,不同位置使用的意義也不盡相同,所以想寫篇文章對其做一個總結(jié)。
首先,明確const是“不變”這個基本意義,但是不變不意味著什么都不變,下面將會看到。
1. const與變量
基本原則:const變量(對象)不能被修改
const在變量中的引入和魔數(shù)有關,所謂“魔數(shù)”指的是突然出現(xiàn)的一個常量值(也叫字面值常量)。
for(int i = 0; i < 512; i++)
{
// todo
}
上例中,512即為魔數(shù),512突然出現(xiàn)在循環(huán)中,令人不能得知其意義,所以引入const。
const int length = 512;
for(int i = 0; i < length; i++)
{
// todo
}
這樣就知道循環(huán)是在長度范圍內(nèi)。
1.1 const修飾一個變量(或者說對象),使其變成一個常量,表示該變量的值無法再被修改,正因為如此,所以定義一個常量的時候,必須初始化。
1.2 const常量的作用域:
我們知道,在全局作用域內(nèi)聲明一個變量(此處特指非const修飾的變量),其作用于整個程序,在其他文件中也能被引用,原因是在全局作用域聲明一個變量,默認是extern修飾的。
在全局作用域內(nèi)聲明一個const變量,默認不是extern修飾,所以其只能作用于本文件內(nèi),若要在其他文件中訪問,需要顯式聲明為extern
2. const與引用
基本原則:const引用是指向const變量(對象)的引用
const int ival = 1024; const int &refVal = ival;
2.1 const引用可以指向一個相關類型(不是本類型)的const變量
double dval = 3.14; const int &refVal = dval;
編譯器將double轉(zhuǎn)換成一個臨時的int對象,然后讓const引用綁定到這個臨時對象,所以改變dval的值不會改變refVal,也就是說dval仍然是非const變量,refVal仍然是常量引用。
primer第四版是上面的說法,但我在VS2012中,const也可以指向一個本類型的非const變量,查找資料的原因大概是滿足reference-campatible條件。
理論上,我們應該嚴格遵守,常量引用指向常量對象,非常量引用指向非常量對象,避免出錯。
3. const與指針
const與指針的關系分為兩種:const修飾的指針和指向const對象的指針,二者const的位置不相同
3.1 指向const對象的指針(const位于指針符號*前面)
對于一個const對象,必須用一個指向const的指針來指向它。原因在于,const修飾使得對象無法被改變,而指針如果不是指向const的指針,則可以通過指針來修改對象,這是不被允許的。
const int ival = 1; const int *ptrVal = &ival;
反過來,對于一個指向const對象的指針,可以指向任意一個對象,這個該怎么理解呢?我們首先看看指針賦值的過程:
int *ptr = &val;
將val的地址賦值給ptr,因為賦值的只是地址,所以不知道ptr所指向的對象是否為const。
如果我們把一個地址賦值給一個指向const的指針,那么指針認為這是一個const的對象,也就是說,ptr指針指向了一個“自認為”是const的對象。
int ival = 1; const int *ptrVal = &ival;
上面的程序是正確的,我們需要明確,ival是非const變量,所以我們可以通過給ival賦值更改ival的值。ptrVal指向了一個自認為是const的對象,所以我們無法通過*ptrVal來更改ival的值。
3.2 const修飾的指針(const位于指針符號*后面)
int *const ptr;
上式聲明了一個const類型的指針,表示的意思是指針本身是一個常量,不能被修改。
如何理解?指針本身的值是一個地址,如果指針本身是一個常量,則這個地址值不能被修改,也就是說指針只能指向這個地址,不能指向其他地方。但指針所指向的地址的內(nèi)容不屬于指針本身的值,所以其所指向的內(nèi)容可以改變。
int ival = 1; int *const ptr = &ival; *ptr = 2; // ok int ivalTwo = 11; ptr = &ivalTwo // error
綜上,可以定義一個指向const對象的const指針
const int *const ptr = &ival;
3.3 typedef中易出錯的const指針
typedef string *ptr; const ptr s_ptr;
上式不能直接替換理解為const string *s_prt; 從而認為s_ptr是一個指向const string的指針。
首先,ptr是一個指針,const修飾的是一個指針,所以應該是string *const s_ptr; s_ptr是一個指向string的const指針。
4. const與數(shù)組
const與數(shù)組的點在于const在定義時必須初始化這個原則,所以使用動態(tài)分配數(shù)組時,如果數(shù)組存儲的是const類型的對象,必須進行初始化(使用初始化符號())。
5. const與函數(shù)返回值
修飾函數(shù)的返回值,用于返回一個常量。
const int foo();
5.1 返回通過值傳遞
如果函數(shù)返回時采用值傳遞,比如返回一個int類型,那么函數(shù)會把返回的值(比如47)復制到外部臨時存儲單元中(產(chǎn)生臨時副本),所以加const修飾毫無意義
int foo(); const int foo();
二者完全相同。需要注意的是,值傳遞產(chǎn)生臨時副本,效率低(下面const與函數(shù)參數(shù)有講),所以通常采用引用傳遞來返回。
5.2 返回通過引用傳遞(并不多見)
如果返回值不是內(nèi)部類型,通常使用引用傳遞來返回結(jié)果,因為引用傳遞的是本身,不需要產(chǎn)生臨時副本。但需要注意的是,此時僅僅返回一個別名。
ClassType &foo(); const ClassType &foo();
const修飾的返回引用值,表示函數(shù)調(diào)用的結(jié)果只能賦值給一個同類型的const引用。
5.3 返回通過指針傳遞
const ClassType *foo();
表示函數(shù)返回一個ClassType類型的指針,這個指針指向一個const對象,指針所指的內(nèi)容不能被修改,所以函數(shù)的返回值只能賦值給指向一個const的同類型的指針。
const ClassType *ptr = foo(); //ok ClassType *ptr = foo(); //error
6. const與函數(shù)參數(shù)
首先需要明確,const修飾的目的就在于保護所修飾的內(nèi)容不被改變。
在C++中,函數(shù)參數(shù)分為值傳遞,指針傳遞和引用傳遞。
6.1 值傳遞
值傳遞在函數(shù)調(diào)用時產(chǎn)生一個臨時副本,函數(shù)中對傳入?yún)?shù)的修改和操作是對副本的操作,不改變實參本身的值,所以無需const來保護。
值傳遞的保護很好,但值傳遞存在缺點,需要產(chǎn)生臨時副本,如果傳入的是對象,那么需要進行構(gòu)造、復制、和析構(gòu)等操作,效率不高。這時候可以考慮引用傳遞
void foo1(int x); void foo2(ClassType instance); //開銷較大
下面這種保護無意義:
void foo1(const int x); void foo2(const ClassType instance);
6.2 引用傳遞
通過傳入實參的引用,降低開銷。因為引用即本身,不需要去產(chǎn)生一個臨時副本。
void foo1(int &x); void foo2(ClassType &ref);
對于上述兩個函數(shù),函數(shù)調(diào)用和值傳遞的形式完全一樣,不同的是函數(shù)內(nèi)部得到的x和ref是調(diào)用傳入實參的引用。也正因為如此,引用可以通過函數(shù)改變傳入的參數(shù)來改變實參。這對與實參來說,比較危險,這時候需要通過const修飾來保護傳入的引用不被修改。
void foo1(const int &x); void foo2(const ClassType &ref);
通常來說,對于基本內(nèi)部類型,不存在對象的構(gòu)造等操作,所以下面兩種保護參數(shù)不被修改的方式效率基本一樣。
void foo1(int x); void foo1(const int &x);
6.3 指針傳遞
指針傳遞在保護參數(shù)不被修改上和引用傳遞是一樣的,指針傳遞還有一個功能是可以擴大接收參數(shù)的范圍:
void foo1(const ClassType *ptr);
結(jié)合上面const與指針,我們知道,ptr指向一個自認為是const類型的對象,所以!傳入的對象不一定是const修飾的對象,可以是const對象,也可以非const對象。反過來,如果沒有const修飾函數(shù)的形參,則只能傳入非const對象。
需要明確,無論傳入的是否是const對象,都無法通過指針來修改這個對象,這和上面指針與const的關系是一致的。
最后需要知道:const只能修飾一個輸入?yún)?shù),如果是輸出參數(shù),無論是引用傳遞還是指針傳遞,都不能使用const來修飾
7. const與類的數(shù)據(jù)成員
const修飾的數(shù)據(jù)成員不能在構(gòu)造函數(shù)中進行初始化,只能使用成員初始化列表進行初始化。
我的理解是,因為在構(gòu)造函數(shù)執(zhí)行之前,使用成員初始化列表對數(shù)據(jù)成員進行初始化,如果在構(gòu)造函數(shù)中對數(shù)據(jù)成員進行初始化,相當于對const進行二次賦值,這是不被允許的。
之所以這么理解,可以參照類中引用類型的初始化,也是必須在初始化列表中進行初始化,因為引用類型也是在定義的時候必須初始化,要求和const一樣,所以二者都只能使用成員初始化列表來進行初始化。
8. const與類的成員函數(shù)
const成員函數(shù)中,const位于函數(shù)的參數(shù)列表后面(函數(shù)聲明前面表示函數(shù)的返回值是一個常量)
const修飾的成員函數(shù)表示成員函數(shù)是一個只讀的作用,不改變成員變量。
const成員函數(shù)真正的含義在于,const其實修飾的成員函數(shù)的是隱含參數(shù)this指針,也就是說傳入的是const ClassType *this,因為this指向一個const對象,所以不能修改。
因為this是指向?qū)ο蟮闹羔?,所以我們需要再次結(jié)合const與指針的知識:
(1)const修飾了this,得到const ClassType *this,this指向一個“自認為”是const的對象(也就是本身),所以任何對象(const或者非const)都可以調(diào)用一個const成員函數(shù),因為傳入的指針都把自身這個對象看作是const對象,所以不能被修改。
(2)對于一個const對象,當其調(diào)用成員函數(shù)的時候,默認都傳入this指針參數(shù),因為this此時指向一個const對象(本身),所以相當于成員函數(shù)被const修飾,成員函數(shù)是一個const成員函數(shù),所以反過來說,const對象只能調(diào)用const成員函數(shù),因為非const修飾的成員函數(shù),this指針不是指向const對象。
(3)在8.2的基本上,進一步,每個成員函數(shù)都可以調(diào)用其他成員函數(shù),每個成員函數(shù)都傳入this指針,所以成員函數(shù)相互調(diào)用必須保持this指針的一致性,所以const成員函數(shù)只能調(diào)用const成員函數(shù),因為二者傳入的this指針都是const修飾的。對于非const成員函數(shù),其傳入非const修飾的this指針,所以不能被調(diào)用。
搞清楚const真正的含義就明白了,一定要保持const成員函數(shù)傳入的是const指針這個意識,對象調(diào)用就需要看對象(本身,指針,引用)是否是const。
相關文章
OpenCV實現(xiàn)簡單攝像頭視頻監(jiān)控程序
這篇文章主要為大家詳細介紹了OpenCV實現(xiàn)簡單攝像頭視頻監(jiān)控程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-08-08
C++報錯:Id?returned?1exit?status的解決辦法
最近剛學c語言,不止一次遇到了同一種報錯,經(jīng)過總結(jié)分享給大家,下面這篇文章主要給大家介紹了關于C++報錯:Id?returned?1exit?status的解決辦法,需要的朋友可以參考下2023-04-04
c語言中回調(diào)函數(shù)的使用以及實際作用詳析
回調(diào)函數(shù)就是一個通過函數(shù)指針調(diào)用的函數(shù),如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個函數(shù),當這個指針被用來調(diào)用其所指向的函數(shù)時,我們就說這是回調(diào)函數(shù),這篇文章主要給大家介紹了關于c語言中回調(diào)函數(shù)的使用以及實際作用的相關資料,需要的朋友可以參考下2021-07-07

