C Plus Plus Reference

  • 在引用的使用中,单纯给某个变量取个别名是毫无意义的,引用的目的主要用于在函数参数传递中,解决大块数据或对象的传递效率和空间不如意的问题。
  • 用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率,且通过const的使用,保证了引用传递的安全性。
  • 引用与指针的区别是,指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。
  • 使用引用的时机。流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。

引用作为参数

引用的一个重要作用就是作为函数的参数。以前的C语言中函数参数传递是值传递,如果有大块数据作为参数传递的时候,采用的方案往往是指针,因为这样可以避免将整块数据全部压栈,可以提高程序的效率。但是现在(C++中)又增加了一种同样有效率的选择(在某些特殊情况下又是必须的选择),就是引用。

传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。

使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。

使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用”*指针变量名”的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。

1
2
3
4
5
6
7
void swap(int &p1,  int &p2)  //此处函数的形参p1, p2都是引用 
{
int p;
p=p1; p1=p2; p2=p;
}

swap(a,b); //直接以变量a和b作为实参调用swap函数

常引用

常引用声明方式:

const 类型标识符 &引用名=目标变量名;

用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为const,达到了引用的安全性。

1
2
3
4
int a ;
const int &ra=a;
ra=1; //错误,不能通过引用对目标变量的值进行修改
a=1; //正确

引用作为返回值

  • 以引用返回函数值,定义函数时需要在函数名前加 &
  • 用引用返回一个函数值的最大好处是,在内存中不产生被返回值的副本
  • 将引用作为函数返回值时应该注意不能返回局部数据(例如局部变量、局部对象、局部数组等)的引用,因为当函数调用完成后局部数据就会被销毁,有可能在下次使用时数据就不存在了,C++ 编译器检测到该行为时也会给出警告;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <cstdio>

using namespace std;

int & func(int &n) {
n += 10;
return n;
}

int main() {
int a = 10;
int &b = func(a); // 传递a的引用给b
printf("a = %d, b = %d, &a = %p, &b = %p\n", a, b, &a, &b);
b++;
printf("a = %d, b = %d\n", a, b);

int x = 10;
int y = func(x); // 用y接收func()的返回值
printf("x = %d, y = %d, &x = %p, &y = %p\n", x, y, &x, &y);
y++;
printf("x = %d, y = %d\n", x, y);

return 0;
}

// a = 20, b = 20, &a = 0x7fff159e9214, &b = 0x7fff159e9214
// a = 21, b = 21
// x = 20, y = 20, &x = 0x7fff159e9218, &y = 0x7fff159e921c
// x = 20, y = 21

引用类型的类成员变量

  1. 不能有默认构造函数,必须提供构造函数
  2. 构造函数的形参必须为引用类型
  3. 初始化必须在成员初始化链表内完成

凡是有引用类型的成员变量的类,不能有缺省构造函数。默认构造函数没有对引用成员提供默认的初始化机制,也因此造成引用未初始化的编译错误。

不能直接在构造函数里初始化,必须用到初始化列表,且形参也必须是引用类型。构造函数分为初始化和计算两个阶段,前者对应成员初始化链表,后者对应构造函数函数体。引用必须在初始化阶段,也即在成员初始化链表中完成,否则编译时会报错(引用未初始化)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>

using namespace std;

class Ref
{
public:

Ref (int &target) : myref(target) {
cout << "Ref constructor" << endl;
}

void printRef() {
cout << "myref is: " << myref << endl;
}

virtual ~Ref () {}

private:
int &myref;

};

int main(int argc, char *argv[])
{
int a = 20;
Ref r(a);
r.printRef();

int &b = a;
Ref r1(b);
r1.printRef();

// error:引用定义时必须初始化
// int &c;

return 0;
}

Reference

Donate article here