一、引用是什么?
引用就是给已经存在的变量取个别名,也就是取外号。比如说,班级里有一个小孩叫赵铁柱,人高马大打篮球很好,人称“二班奥尼尔”。当这个外号被大家熟知后,那讨论到他时,不管是说“赵铁柱昨天和姑娘表白了”还是“二班那个奥尼尔被女生发好人卡了”,大家都知道指的是谁。同样,假设我们已经定义了一个叫做a的变量,但是在某些场合人们更习惯叫这个变量为b,那就可以通过引用的方式,给a取个外号叫b。但是就像不管取多少外号,我们都知道这说的是一个人一样,不论一个变量有多少个引用,它在内存中都只有一个存储位置,存储的数值都是一样的。所以,对引用的操作,其实就是对本体的操作。
二、引用咋写?
引用的写法用到了符号 & 。这个符号在C++里用处广泛,有时候表示与条件(&&),有时候表示取地址,但在这里,它有了船(全)森(新)的用法:给变量取外号(创建变量的引用)。
我们知道,英美两国的英语单词在某些时候不一样。比如在英国,老绅士们管秋天叫autumn;但到了美国一些地方,美国老铁会亲切地称秋天为fall。为了照顾两国人民,,我们要让秋天有两个名字,但是归根到底,秋天就是秋天嘛。那这时候 ,引用该怎么处理呢?
int autumn; //英国人:这是我说的秋天
int & fall = autumn; //美国人:俺也一样!
看到了吗?定义引用三部曲:
1、写出引用类型(int)
2、亮出引用身份(&)
3、给出引用名称(fall)
这里,& 这个符号和int一起,表示定义了一个指向int的引用。至此,我们就可以愉快地混用autumn和fall而不用顾忌两国人民因为秋天的叫法不同而吵架了。
我们做个试验:
#include <iostream>
using namespace std;
int main()
{
int autumn = 90;
int & fall = autumn;
cout << "British: autumn last " << autumn << " days." << endl;
cout << "American: fall last " << fall << " days." << endl;
autumn++;
cout << "British: In this year, autumn last " << autumn << " days." << endl;
cout << "American: yes, I agree with you, fall last " << fall << " days." << endl;
cout << "The address of autumn is " << &autumn << endl;
cout << "The address of fall is " << &fall << endl;
}
上述代码运行的结果是:
从上面的结果可以看到,autumn和fall这两个名称在内存中的地址都是一样的,表示他就是一个变量的两个名称。对其中一个进行操作,另外一个也受到同样的影响。
三、为啥要用引用?
1、引用让程序猿可以在调用函数里修改数据对象
当引用作为函数参数时,我们将这种传递方式称为按引用传递。用了按引用传递的方式后,被调用函数和调用的函数用的变量就是同一个,而不是一个副本了。比如:
void func1()
{
int value1, value2;
value1 = value2 = 0;
func2(a, b);
}
void func2(int& a, int& b)
{
//some other operation...
}
在func2函数中,a和b分别是func1的value1和value2的引用,对a和b的操作也会同样改变value1和value2的特性。
2、用引用传递结构比较大的结构或类,可以让程序运行速度-1s加快
当传入调用函数的结构体或者类很大,那么使用引用来传递可以加快调用函数的运行速度。这个也很好理解,函数就不用再复制数据了,多余的操作就关闭了,函数的执行效率又占领高地了。当然,如果配合const使用,效果会更安全。
四、引用有啥要注意的地方?
说到这里,那我们在取外号(划掉)创建引用的时候有什么要注意的吗?
1、引用声明的时候就要初始化
这条很好理解。套用成语,“皮之不存,毛将焉附”,引用说白了就是一个变量的“外号”,如果没有原始变量这个本体,外号又有什么存在的意义呢?所以,下面的情况(第二行)是绝对绝对不可以的!
这一条包含两点内涵:一是引用不能不进行初始化,二是引用不可以先声明再赋值。下面第三行是引用的正确打开方式。
int value = 1;
int & wrongUseOfReference;
int & rightUseOfReference = value;
2、引用可以通过初始化设置,但是不能通过赋值设置
这又怎么理解呢?我们简单做这样一个实验。
现在英国和美国人就秋天的看法达成一致了;现在中国也要加入,并准备用“qiutian”代指秋天。那在去用fall来代指秋天可以吗?看例子:
#include <iostream>
using namespace std;
int main()
{
int autumn = 90;
int & fall = autumn; // 初始化的时候设置了引用
cout << "British: autumn last " << autumn << " days." << endl;
cout << "American: fall last " << fall << " days." << endl;
cout << "autumn address is " << &autumn << endl;
cout << "fall address is " << &fall << endl;
int qiutian = 91;
fall = qiutian; // 通过赋值设置引用
cout << "British: autumn last " << autumn << " days." << endl;
cout << "American: fall last " << fall << " days." << endl;
cout << "Chinese: qiutian last " << qiutian << " days." << endl;
cout << "autumn address is " << &autumn << endl;
cout << "fall address is " << &fall << endl;
cout << "qiutian address is " << &qiutian << endl;
}
看运行完的结果:
乍一看,fall确实变成了秋天的别名:值从90变成91了嘛!但我们定眼一看,发现不对!fall还是和autumn有同样的地址,但是和qiutian的地址不一样。所以说,fall = qiutian 这句话其实就是一个朴实无华且枯燥的赋值,并没有实现引用的效果。一句话,引用一旦出生了,就只能跟随一个变量。
3、当原始数据想发生改变时,使用按引用传递
老规矩,上例子。现在有一个函数,想计算传入参数的立方,当我们用引用传参的时候:
#include <iostream>
using namespace std;
double cube(double & value);
int main()
{
double param = 4.0;
cout << cube(param);
cout << " is cube of " << param;
}
double cube(double & value)
{
value *= value * value;
return value;
}
得到的输出是:
在这里,cube函数中value是main函数中param的引用,cube的过程改变了value的值,同时也改变了param的值。
那怎样防止呢?我们可以使用值传递,这样,cube里折腾的value是main函数中param的复制品,不论value如何折腾,param自岿然不动:
#include <iostream>
using namespace std;
double cube(double value);
int main()
{
double param = 4.0;
cout << cube(param);
cout << " is cube of " << param;
}
double cube(double value)
{
value *= value * value;
return value;
}
这样的结果是:
或者可以用const对传入引用进行限制,以一种“霸道总裁”的方式告诉cube函数:你不能对我传给你的引用做修改:
#include <iostream>
using namespace std;
double cube(const double & value);
int main()
{
double param = 4.0;
cout << cube(param);
cout << " is cube of " << param;
}
double cube(const double & value)
{
double result = value * value * value;
return result;
}
和上面值传递也有同样的效果。这时,再写成 value *= value * value就会出编译错误,因为这个时候涉嫌非法修改const引用。由此看来,const是一种非常实用的保护措施。
4、如果传递的数据对象是数组,只能使用指针,不能用引用了
(未完待续)