引用(reference)

作者:jicanmeng

时间:2014年11月26日


  1. 引用的定义
  2. 引用的应用场景
    1. 函数的参数类型为引用类型
    2. 函数的返回值类型为引用类型
  3. 一个例子

1. 引用的定义:

引用(reference)是C++引入的一种新的数据类型,在C语言中是没有的。learncpp.com上面是这样描述的:References are a type of C++ variable that act as an alias to another variable.

声明一个引用类型的变量,这个变量会和一个已经分配的内存空间绑定在一起。假设内存空间使用变量var1来表示,那么s声明一个引用变量var2就相当于为var1起了一个"绰号",var1和var2表示是相同的一块内存空间。由于内存空间已经分配,所以编译时不会为引用本身分配内存空间。

在声明一个引用类型的变量时,必须为其指定绑定的内存空间,即声明引用的时候必须进行初始化。声明引用的时候使用符号"&"。看一个例子:

程序如下:

#include <iostream>
using namespace std;

int main(){
    int a = 3;
    int &b = a;
    int &c(a);

    b = 4;
    cout << "a is " << a << endl;
    c = 5;
    cout << "a is " << a << endl;

    return 0;
}

程序运行结果如下:

[jicanmeng@andy tmp]$ g++ reference1.cpp
				[jicanmeng@andy tmp]$ ./a.out
				a is 4
				a is 5
				[jicanmeng@andy tmp]$

声明一个引用类型的变量有两种形式,分别如第6行和第7行所示。声明了引用变量b和c后,a变量,b变量,c变量表示相同的内存空间。修改b就相当于修改a和c,修改c就相当于修改a和b。

在c语言中学习指针的时候,定义一个指针变量时:int a = 3; int *pointer = &a;,*表示定义的变量是指针类型的。而在正常使用时:*pointer = 4;, *是指针运算符,*pointer表示取指针变量pointer指向的内存的数据。

类似的,定义一个引用变量时:int a = 3; int &b = a;,&表示定义的变量是引用类型的。而在正常使用时:int *pointer = &b,&则表示取地址运算符,&b表示得到b表示的内存的地址。

指针和引用的区别如下:1. 指针本身要占用内存空间,而引用不需要;2. 引用是一个已分配内存空间的另一个标识,一旦初始化,他们的关系就确定下来,不可再修改。而指针指向另一个内存空间,指向可以更改。

c++中已经有了指针,为什么还要在添加一个引用类型呢?learncpp.com上给的解释是:Because references always “point” to valid objects, and can never be pointed to deallocated memory, references are safer to use than pointers.

2. 引用的应用场景:

reference有两个典型的应用场景:1. 函数的参数为引用类型;2. 函数的返回值类型为引用类型。

2.1 函数的参数类型为引用类型

第一种场景:函数的参数类型为引用类型:

#include <iostream>
using namespace std;

void foo(int &y) // y is now a reference
{
    cout << "y = " << y << endl;
    y = 6;
    cout << "y = " << y << endl;
} // y is destroyed here

int main()
{
    int x = 5;
    cout << "x = " << x << endl;
    foo(x);
    cout << "x = " << x << endl;
    return 0;
}

2.2 函数的返回值类型为引用类型

第二种场景:函数的返回值类型为引用类型。这里需要详细的说明一下如果返回的是一个局部变量的情况。函数的返回值可以通过传值返回,也可以通过传递引用返回,也可以通过传递指针返回。

1. 函数的返回值通过传值返回:

#include <iostream>
using namespace std;

int DoubleValue(int nX)
{
    int nValue = nX * 2;
    return nValue; // A copy of nValue will be returned here
} // nValue goes out of scope here

int main()
{
    int a = 3;
    cout << "return value is " << DoubleValue(a) << endl;
    return 0;
}

在这种方式下,当返回局部变量时,其实程序会将nValue复制一份返回给调用者。

2. 函数的返回值通过传递引用或传递指针返回:

#include <iostream>
using namespace std;

int & DoubleValue(int nX)
{
    int nValue = nX * 2;
    return nValue; // return a reference to nValue here
} // nValue goes out of scope here

int main()
{
    int a = 3;
    cout << "return value is " << DoubleValue(a) << endl;
    return 0;
}
#include <iostream>
using namespace std;

int * DoubleValue(int nX)
{
    int nValue = nX * 2;
    return &nValue; // return nValue by address here
} // nValue goes out of scope here

int main()
{
    int a = 3;
    int *pa = DoubleValue(a);
    cout << "return value is " << *pa << endl;
    return 0;
}

上面的两个程序都会提示警告:"warning: reference to local variable ‘nValue’ returned" 或者"warning: address of local variable ‘nValue’ returned"。因为函数调用结束后,局部变量会释放分配的内存空间,我们却返回了这块内存空间的引用和指针,所以会出现警告。其实,只要我们返回的是某块不释放的内存的引用或指针,都不会有警告的。

3. 一个例子:

上面提到,声明一个引用变量其实是对一块已经存在的内存空间进行绑定,这块内存空间常常使用变量名表示。来看一种特殊情况:一个指针变量表示的内存空间。我们看一个例子:

#include <iostream>
using namespace std;

int & multiply2(int *&nX)
{
    *nX = *nX * 2;
    return *nX;
}

int main()
{
    using namespace std;
    int a = 3;
    int *pa = &a;

    int b = multiply2(pa);
    cout << "a is " << a <<
        "b is " << b << endl;

    return 0;
}

这个例子中,注意两个地方:1. multiply2()函数的参数nX是指针类型的引用变量,表示nX绑定的内存空间用于存放int类型的变量的地址,nX和pa表示的是相同的内存空间。2. multiply2()函数的返回值类型是引用类型,不过因为这个函数返回的不是局部变量,所以没有警告,顺利编译通过。

参考资料

  1. <<C++实用教程>> 电子工业出版社 郑阿奇 主编 丁有和 编著
  2. The C++ Tutorial:
    http://www.learncpp.com/cpp-tutorial/611-references/
    http://www.learncpp.com/cpp-tutorial/612-references-vs-pointers-and-member-selection/
    http://www.learncpp.com/cpp-tutorial/73-passing-arguments-by-reference/
    http://www.learncpp.com/cpp-tutorial/74a-returning-values-by-value-reference-and-address/