先放一个例子
swap函数对比(作用,交换两个数的数值)
void swap(int *a, int *b)//使用指针
{
int temp = *a;
*a = *b;
*b = temp;
return ;
}
void swap(int &a, int &b)//使用引用
{
int temp = a;
a = b;
b = temp;
return ;
}
什么是引用
引用,就是一个变量的别名
再来一个例子
int a = 2;
int & ra = a;
这里,我们定义ra,它是变量a的引用
下面会用上面定义的变量
放几个引用的用法
引用玩法1
int a = 2;
int & ra = a;
ra = 3 //!!
如果使用了上面那一条语句会怎样呢?
实际上
此时ra == 3 并且 a == 3
没有动a啊,a为什么会变啊
复习引用的定义
引用就是某一变量的别名
也就是说,变量ra 就是 变量a
我对ra变量执行的任何操作
等同于对a变量执行的任何操作
反之亦然
其实很好理解原理
变量a的地址和变量ra的地址是相同的
(可以自己在编译器上试一试)
引用玩法2
还是一个例子
void function(int & r)
{
r = 6;
}
int main()
{
int a = 2;
function (a);
cout << a;
}
将a作为形参传入函数
最后会输出多少呢?
????
实际上,会输出6
也就是说,a的值被修改为6
不是只有传入指针才会修改它的值的吗?
再来复习一下
引用也就是说是变量的别名
还有原理
引用变量和原变量地址是相同的
在函数声明中
void function(int & r)
定义了一个引用r
也就是说,r变量将会是传入实参的引用
因此
我们对r怎样
也就是对实际传入变量怎样
再来看回开头的例子
相信大家应该都懂了
关于概念
按值传递
(就是直接传数)
按址传递
(就是传入地址指针)
按引用传递
(就和前面讲到的一样)
为什么用引用
1. 好打,好看
自行对比
什么时候不小心打少了个*号的时候
什么时候*号和++优先级搞错的时候
(*a++ 的理解!!)
……
就会知道引用的方便了。
void swap(int *a, int *b)//使用指针
{
int temp = *a;
*a = *b;
*b = temp;
return ;
}
void swap(int &a, int &b)//使用引用
{
int temp = a;
a = b;
b = temp;
return ;
}
2. 效率高
这里会扯到一个是否生成临时变量的问题
- 按值传递
按值传递的最大优点就是在函数中无论怎么修改变量
都不会改变传入变量的值
原因便在于按值传递中函数内的是临时生成的变量
和传入的变量完全没有关系
- 生成临时变量如何影响效率
也许生成一个int变量效率差别不大
但是如果是传入一个有着好几个数组的结构体
或者是一个庞大的类对象
计算机声明这些的临时变量的时候
将会耗费不少计算资源与存储空间
这个时候,把形参定义为引用便显得尤为重要
引用可以同时具有引用优点和按值传递的优点
按值传递的最大优点就是不怕传入的变量被修改
引用可以吗??
我们可以这样子定义来避免变量被改动
void function(const int & a);
通过把a定义为常值引用(加一个const)
我们可以在兼顾引用效率高优点的同时
避免不小心被修改
若是a的值被修改编译器会马上报错方便修改
关于引用的几点注意事项
1. 函数形参有引用
以void function ( int & r)
为例
我们可以这样子调用吗?
int a = 2;
function(a+2);
function(4);
前面提到
引用的原理便是内存地址相同
可是,这里的(a+2)有地址吗?
可是,这里的4有地址吗?
我们可以这样用吗?
事实上,这里讲的引用的全名是左值引用
左值:能够放在赋值语句左边的东东
(也就是可被赋值,有自己的内存地址)
能够进行左值引用的一定前提是左值!!
a+2 ,4 明显不能够放在赋值语句左边
(a+2 = 5? 4 = 2?)
因此此时编译器会报错
这里有例外:const引用
如果是void function (const int & r)
那么这个时候我
function(a+2)不会出错
并且,会像按值传递那样生成临时变量
下面有一个总结
int x;
int f ( int & n);
f(x);
f(x+2);// error
f(10);//error , too.
int f2 (const int & n);
f2(x);
f2(x+2);// temporary variable
f2(10);// temporary variable
关于返回引用
// 读取一个引用,返回一个引用
int & function (int & a)
{
int b = 2;
a = a + b;
return a;
//返回一个引用
}
这里有几个比较容易出错的地方
1. 我可以返回b吗?
再复习一遍原理
引用变量和原变量内存地址一样!
如果这个程序改成
int & function (int & a)
{
int b = 2;
b = a + b;
return b;
//返回一个引用
}
将会报错!!
b 是一个局部变量
局部变量在代码块内可见
当退出代码块时自动销毁!
原内存地址的内容将会消失
假设我int一个变量c
int a = 2;
c = function(a)
变量c的地址将会与函数中临时创建的局部变量b相同
但是函数一旦结束
该地址所对应的内存块将会自动释放!!
此时变量c内的内容将不可访问(或者是垃圾数据)
返回引用(是个左值)
// 读取一个引用,返回一个引用
int & function (int & a)
{
int b = 2;
a = a + b;
return a;
//返回一个引用
}
对这个函数,我们有一个比较奇葩的用法
function (a) = 3;
原理其实很简单
因为函数返回值是个左值
所以可以放在赋值语句的左边
那这条语句到底做了什么呢?
1. 函数function接受变量a,
2. 函数体内,返回变量a的引用
3. 对变量a的引用,赋值给3;
所以最终,就是a = 3;
如果怕自己搞错
写出这样的代码
if ( (function(a) = 4 );
(是不会报错的!)
可以把函数声明改成这样
const int & function(int & a);
还有一种奇葩用法
int a = 2;
const int & r = a;
这里,a和r实际上是同一个变量
(引用的定义?变量别名)
可是,同一个变量,
我用a就可以赋值,就可以修改
我用r就不能够赋值和修改
(因为用了const)
关于指针,引用,传值的选择
- 如果向函数中传递数组,只能用指针
- 如果需要修改传入的变量,用引用和指针(引用更好)
- 如果传递比较大块的东东,推荐引用或指针(当然引用更好)
- 如果怕传入的变量被修改,请用const限制
- 没有如果了
总结
- 引用必须是左值
- 引用可以提高效率
- 不能反悔局部变量的引用
- 避免引用被修改可以用const来限制
- 数组不能引用!对于数组只能使用指针
- debug的时候你会有更多的总结
一些比较高级的东西
- 对于类的引用,既可以引用该类
也可以引用该基类的派生类
(这是实现多态的基础) - c++11中还有右值引用