#include
using std::cout;
using std::endl;
//引用做参数
void Swap(int& x, int& y)
{
auto tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 1;
int b = 2;
cout << "a = " << a << ' '<< "b = " << b << endl;//输出a = 1 b = 2
Swap(a, b);
cout << "a = " << a << ' ' << "b = " << b << endl;//输出a = 2 b =1
return 0;
}
如果形参是引用类型,当形参改变时,实参也会被改变。
//引用做返回值
#include
using std::cout;
using std::endl;
int& Count()
{
int n = 0;
n++;
return n;
}
int main()
{
int& ret = Count();
cout << ret << endl;//输出结果为1或者随机值
cout << " " << endl;
cout << ret << endl;//输出结果为随机值
return 0;
}
解释一下为什么两次输出不同,这里就要引入对栈帧的理解,当函数调用结束时,函数栈帧就会被销毁,被调函数中的变量也被销毁了,空间被释放,归还给操作系统,那么这里第一次输出n时,其实n引用的变量的地址是不在的,但是编译器的不同,可能此时栈帧没有被及时的销毁,就侥幸得到了正确的结果,理论上讲应是一个随机值,但当我们执行下一个语句时,上一个函数的栈帧就一定被销毁了,所以n引用的对象此时是不确定的,自然输出的就是随机值。
看这里就是第一次输出的时候出现的警告。
#include
using std::cout;
using std::endl;
int& Count()
{
static int n = 0;
n++;
return n;
}
int main()
{
int& ret = Count();
cout << ret << endl;//输出结果为1
cout << " " << endl;
cout << ret << endl;//输出结果仍然为1
return 0;
}
返回引用对象n存在于静态区静态,此时当这个函数调用结束后栈帧虽然也被销毁了,但是存在于静态区的静态变量n并不会被销毁。
总结一下几种常见当函数调用结束后返回的引用对象仍然存在的情况:
其实要想让引用对象一直存在就是让它不会被销毁,例如
当引用对象是一个全局变量时
当引用对象存放于静态区时
当引用对象的空间是动态开辟时(例如malloc,calloc时)
只有在引用时才会有权限的放大,缩小和平移,在拷贝的过程中不会涉及。
这里z被const修饰不可被改变,且z是x的别名,此时就是属于权限的缩小,原对象可以被改变,但是不能通过它的别名z来进行改变了。
原对象被const修饰是常量,不可被改变但是你现在给他取了一个别名,让它是可以改变的了,这就是权限的放大,这里是不允许的,值允许权限的平移或者是缩小。
函数返回引用时可能会遇到的权限问题。
我们先引入一个例子来对这部分知识进行了解,当函数调用时返回值返回的是原值还是这个值得拷贝呢?(这里说的是返回值不涉及指针和引用)
注意我们函数调用返回值时,返回的是值的拷贝,即使返回值被静态变量static修饰。
返回的拷贝值具有常性,具有常性就是说这个值不可被修改,即常量
那么引用返回的是什么?
注意这里与上述的不同,这里就是返回的这个对象的别名,不是值的拷贝。所以引用在不被其它限定符修饰的情况下不具有常性。
类型转换的过程中会不会产生临时变量呢?
看下面例子:
这里肯定会有类型转换,那么是不是对原数据进行类型转换呢?显然这里不是的,因为打印的结果d和i仍是原数值,那么是怎么比较出来,又是谁进行的类型转换呢?答案是它的拷贝,这里又有一个重要的问题,当发生类型转换时,也会产生临时变量。
所以当函数返回引用时要注意对象是否发生权限的改变。
1.
这种情况没有用到引用,压根就不涉及权限的改变。
2.
3.
一定要注意临时变量具有常性
一定要注意临时变量具有常性
一定要注意临时变量具有常性
4.
这样就可以引用,因为被const修饰就也具有常性
就是我原对象如果不能修改,那么就允许引用来修改我,这属于权限的放大。放大是不被允许的。
但是可以允许我修改我自己,引用不可以修改我,这属于权限的缩小。
如果我是可以被修改的,也可以通过引用来修改,或者我不能被修改,也不可以通过引用来修改,那么这属于权限的平移。
记住,权限只允许缩小和平移,不允许放大。