對於學習Java或者C#的程序員來說,學習C++的時候,大概看到那些使用const int*& a之類的當參數時都會蒙了吧··
老實說,儘管當時我覺得自己C++書都看差不多了,學得也可以,但實際上,看到const,"*","&"的混合使用時,我也蒙了。
這篇文章就我自己的一些看法來談談不同的混合方式都有什麼作用。
- 沒有const的時候。
沒有const時,也就是*和&的混合使用。這裏又分&是當取地址運算符或者是引用運算符兩種情況,怎麼區分&是取地址還是引用呢?
很簡單,如果&之前沒有類型就是用作引用,否則就是當取地址符。例如:int& 這是引用,int* p = &a 這是取地址。
1.&是取地址時:
這種情況在譚浩強的C++程序設計裏面有說明 P168
如果有int* p = &a;那麼 &*p 是什麼含義呢?
由於"&"和"*"的優先級都爲3,但按自右至左方向結合。所有&*p = &(*p) = &a = p,也就是a的地址
下面程序的輸出結果是一樣的。
int a;
int* p = &a;
cout << "a的地址:" << &a << endl;
cout << "&*p的值爲:" << &*p << endl;
cout << "p的值爲:" << p << endl;
那麼 *&a 的含義呢?同樣可以分析,*&a = *(&a) = *p = a,也就是說,*&a與a等價。
同樣的,下面程序的輸出結果是一樣的。
int a = 1;
cout << "a的值:" << a << endl;
cout << "*&a的值爲:" << *&a << endl;
2.&是引用時:
這種情況大多出現在函數形參裏,當初也是這種情況把我搞混了。
例如如果一個函數的形參是 int*& p ,那麼它代表的是什麼意思,又有什麼作用呢?
可以這樣理解:int*& = (int*)&,也就是一個整型指針的引用。這又有怎麼作用呢?
這裏用一個例子來說明,引自譚浩強的C++程序設計裏的P173
#include <iostream>
using namespace std;
int main()
{
void swap(int* p1, int* p2);
int* pointer_1, *pointer_2, a, b;
cin >> a >> b;
pointer_1 = &a;
pointer_2 = &b;
if (a < b)
swap(pointer_1, pointer_2);
cout << "max=" << *pointer_1 << " min=" << *pointer_2 << endl;
return 0;
}
void swap(int* p1, int* p2)
{
int* temp;
temp = p1;
p1 = p2;
p2 = temp;
}
上面這段代碼試圖通過函數更改兩個指針的指向來達到交換的功能,即不改變a和b的值,但改變pointer_1和pointer_2的指向來達到使pointer_1指向較大值,而pointer_2指向較小值。但通過輸出的結果看,這並不成功。
爲什麼呢?
我們都知道,函數調用時會虛實結合,即把實參拷貝給形參(引用除外),這裏的形參是指針也是一樣的。
也就說調用時,系統在堆棧上創建了兩個整型指針變量(它們確實佔用了另一段內存),所以更改它們不會影響到實參。
具體過程如下圖所示:
那麼怎麼在函數內改變實參指針的指向呢?答案就是用*&,也就是指針的引用。首先要強調一點的是,有些初學C++的人會搞不清楚引用的實現機制,以爲引用跟指針用法一樣(我當初就這樣想的),但其實它們區別很大。下面說說我對引用的一些看法,或許對你有幫助。
書上一般會說:引用是爲一個變量取一個別名。
我覺得這樣反而不好理解,如果這樣說呢:引用是與變量使用同一塊內存。
如果將變量看成是一小塊內存,那麼使用不同內存的變量自然不能當成是同一個變量,儘管它們的值可以一樣,但更改一處並不會改變另一處。但引用就是使用相同內存的,所以引用可以看成是與變量是同一個變量。
看下面的例子:
#include <iostream>
using namespace std;
void f1(int& a)
{
cout << &a << endl;
}
void f2(int a)
{
cout << &a << endl;
}
int main()
{
int a;
int& b = a;
int* p = &b;
cout << &a << endl;
cout << &b << endl;
cout << p << endl;
f1(a);
f2(b);
return 0;
}
從結果中可以看到,a和b的地址是一樣的,而且在函數f1的形參與實參的地址也是一樣的,而不是用引用的f2的地址就不同。而我們也可以用指針指向引用變量,也可以用引用當作整型參數來傳遞。所以說引用跟原變量完全就是同一個變量。
可以用下面一段代碼來實現在函數內改變實參指針的指向:
#include <iostream>
using namespace std;
void Func1(int* p);
void Func2(int*& p);
int b = 2;
int main()
{
int a = 1;
int* p = &a;
cout << "a的地址:" << &a << endl;
cout << "b的地址:" << &b << endl;
Func1(p);
cout << p << endl;
Func2(p);
cout << p << endl;
return 0;
}
void Func1(int* p)
{
p = &b;
}
void Func2(int*& p)
{
p = &b;
}
運行上面的代碼,可以看到,調用Fun1後,p的指向依然是a,但調用Fun2後,p的指向就改到b了。也驗證了上面那個說法。
至於另一種情況,也就 int&* 的情況,這個情況大家上機一試就知道了,是不同通過編譯的。原因很簡單,沒有這樣的定義。
- 有const的情況
在搞懂了沒有const的情況後,有const的情況無非就是加了一層常量限定了。
1.const與*的結合
首先是3個常見的const和指針的結合:const int* p,int* const p,const int* const p。
這3種在大部分的C++書籍上都有介紹了。分別是:
const int* p | 指向常量的指針 |
int* const p | 常指針 |
const int* const p | 指向常量的常指針 |
這3種也是一般用在函數參數裏比較多。
這裏稍微介紹一下:
const int* p 不能改變指針指向的值,可以改變其指向。即*p = 1是非法的,而p = &b是合法的。
int* const p 能改變指針所指向的值,不可以改變其指向。即*p = 1是合法的,而p = &b是非法的。
const int* const p 不能改變指針所指向的值,也不可以改變其指向。即*p = 1和p = &b都是非法的。
怎麼記憶着三種奇怪的寫法呢?這裏是我的記憶方法:
使用左結合:
const int* p => (const int)* p 也就是一個指針所指向的是常量
int* const p => ((int*) const) p 也就是一個指針本身是常量
而const int* const 就是兩者的結合。
2.const與&的結合
下面看看const 和 & 的結合使用。
最常見的就要數: const int& a 了,這個最常使用在類的拷貝構造函數裏,這就是默認的寫法。
這個理解起來很簡單,就是使用引用,同時又是常量。跟const int a的區別就在於:
在函數虛實結合時,有&的使用引用方式,而沒有的就是值傳遞。
我剛開始以爲也會有 int& const a 的形式,後來試了一下,確實有,但是沒用。
看下面的代碼:
#include <iostream>
using namespace std;
const void Func3(int& const a)
{
a = 3;
}
int main()
{
int a = 1;
Func3(a);
cout << a << endl;
return 0;
}
這段代碼可以通過編譯,最後結果是實參a的值改爲3了,也就是說int& const a 是等價於int& a的。
本來我猜想, const int& const a 是等價於const int& a的。後來才發現這種寫法是編譯不過的。呵呵··
3.const、*、&的結合
最後就是3種形式的結合,const 和 * 和 &的結合。
1. const int*& p 的使用
這個應該算是比較常見到一種了吧,第一次看的時候相信很多人都會蒙了吧,呵呵
上面提到了int*&是指針的引用,不要以爲const int*&是(const) (int*&)是這兩組的組合意思,根據左結合,
const int*&是(const int*)&的意思,前面也提到const int*是指向常量的指針的意思,那麼
const int*&就是指向常量的指針的引用的意思。
看下面的代碼:
#include <iostream>
using namespace std;
int b = 2;
void Func3(const int*& p)
{
//*p = 4; // 去掉這個註釋的話會編譯錯誤
p = &b;
}
int main()
{
int a = 1;
const int* p = &a; // 不加const的話會編譯錯誤
Func3(p);
return 0;
}
綜合地說,const int*&的功能如下:
不能改變指針所指向的值,可以改變指針的指向,改變形參指針的指向會改變實參的指向。
2.int*& const p 的意思
經過測試,int*& const是等價於int*&的,也就說後面的const不起作用。
3.同理,const int*& const 是不能通過編譯的。
各位看客是不是以爲到此爲止呢,呵呵,還有一些可能你想都沒想到的用法呢!
4.int* const &p 的使用
通過前面那麼多的分析,大家也試試猜猜這個用法的意思。採用左結合
int* const &p => (int* const)& p 也就是一個常指針的引用。
同樣用一小段代碼來測試一下:
#include <iostream>
using namespace std;
int b = 2;
void Func3(int* const &p)
{
*p = 4;
//p = &b; // 去掉這個註釋的話會編譯錯誤
}
int main()
{
int a = 1;
int* p = &a; // 這裏沒有定義常指針!
Func3(p);
cout << a << endl;
return 0;
}
老實說,這是個只有無聊的人才會想到的用法(狂暈),我們分析一下:
我們在指針型的形參加引用是爲了可以改變實參的指向,但我們又定義了形參是一個不能改變指向的常指針。所以:
int* const& p 是等價於 int* const的。
- 總結:
可能會有人說這種用法int const *,這個···翻翻書就知道了,這個是等價於int* const的,也就是const 和 * 的位置可以換的。
上面說了很多組合,但實際有用的也就那麼幾種。這裏總結一下:
1.int*& p 這應該是最有用的一個,因爲可以用它當形參,在函數體內改變實參的指向。
2.const和*的3種結合都比較常用,特別是前2種。
3.const int& a 這也是很常用的一種用法,特別在面向對象裏面,引用是提高效率的鑰匙。
4.const int*&,這個雖不常用,但某些場合還是會看到的。
到此爲止,希望各位看客多多支持~~看過後沒那麼蒙的就頂頂吧。