void func(int i) {……..} void func(const int i /* 頂層const */) {……..} |
之所以能根據不同的實參來區分實際該調用哪個函數。(即函數重載)。最主要的根據在傳遞實參給函數形參的時候,能夠有不同的選擇,從而區分出不同的調用。
函數swap(int left, int right),當調用時用實參a、b調用時,swap(a, b)傳參相當於這樣left=a, right=b。
返回到我們的func函數,當用實參調用時func(a),對於第一個函數傳參形式爲int i=a,對於第二個函數數,形式傳遞形式爲const int i=a。那麼好了,畢竟我們實際只會調用其中的一個函數,是該到了選擇的時候了。
int i=a; const int i=a; |
我們可以發現不管實參a的定義,是int a還是const int a,上面兩個賦值(拷貝)操作合法的,而且並沒有什麼理由說明進行哪一種拷貝更合適。編譯器將不知道該怎麼進行選擇。將報錯,redefinition of ‘void func(int)’,所以在它看來這兩個函數是一個面孔。
void func(int &i) {……..} void func(const int &i /* 底層const */) {……..} |
我們將形參改成底層const。
int &i=a; const int &i=a; |
如果const int a,那麼int &i=a,是非法的。a是常量,如果int &i=a成功的話,那麼將有能力通過i改變常量a。所以只能選這調用第二個函數。
如果int a,上面兩種賦值都是可行的,但是int &i=a,token i與a訪問a變量內存權限相同,可讀可寫。而constint &i=a,token i對a變量的那塊內存只用於讀權限。所以可以認爲int &i=a,匹配更精準,所以調用第一個函數。
另外,類的成員函數,可以在函數末尾加上const,來修飾this指針,這是個底層const。
class which { void func() {……..} void func() const {……..} }; |
which A;
A.func();
將調用第一個成員函數。
cosnt which A;
A.func();
將調用第二個成員函數。
top-level const與low-level const
(頂層const與底層const)
指針本身是一個對象,它又可以指向另外一個對象。因此,指針本身是不是常量以及指針所指的是不是一個常量就是兩個相互獨立的問題。用名詞頂層const表示指針本身是一個常量,而用名詞底層const表示指針所指的對象是一個常量。
int i=0;
int *const p1=&i; //不能改變p1的值,這是一個頂層const
const int ci=42; //不能改變ci的值,這是一個頂層const
const int *p2=&ci; //不能改變p2的值,這是一個底層const
const int *const p3=p2;//靠右頂層const,靠左底層const
const int &r=ci; //用於聲明引用的const都是底層const
--à>>當執行拷貝操作時,兩者有明顯區別:
如賦值操作,它是先再申請一個對象的內存,然後將別的對象拷貝過來,即值拷貝。由於頂層const修飾的是對象本身,const對單純的值拷貝而言是沒有影響的。
int i=0;
const int ci=42;
i= ci; //正確,拷貝ci的值,ci是頂層const
int *p2;
int *const p3;
p2 = p3; //正確,指針p3指針本身是const,
//p2、p3指向的對象類型完全相同(底層const),而頂層const不起影響。
而底層const是起影響的,不能被忽視。當執行對象的拷貝操作時,必須具有相同的底層const或者能夠轉換,一般來說,非常量可以轉換成常量,反之則不行:
int *p2;
const int * p3=temp;//同時具有頂層與底層const
p2 = p3; //錯誤, p3指向只讀int,而p2指向可讀可寫int。此處如何成功只讀int將被p2修改。
p3=p2; //正確,int*能轉換成const int*
綜上,如果指針指向的對象具有相同的屬性,那麼指針本身是否是const,對於拷貝該指針不產生影響。如果指針指向的對象一個是const屬性的,另一個無const屬性,那麼只有單向拷貝該指針是合法的(由int*到const int*)。