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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章