C++的那點事,const,指針和引用的混合使用

對於學習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*&,這個雖不常用,但某些場合還是會看到的。

 

到此爲止,希望各位看客多多支持~~看過後沒那麼蒙的就頂頂吧。

發佈了58 篇原創文章 · 獲贊 59 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章