1.引用的使用
1.作用:给变量起别名,本质就是名字起一个别名,但是指向同一个地址。别名与原名的数据类型必须一样。
2.语法:数据类型 &别名 = 原名 (int & b =a)
下面就用一个实列来说明
#include <iostream>
using namespace std;
int main()
{
// 数据类型 &别名 = 原名
int a = 10;
int& b = a; // 创建引用给a的地址再起一个名字b,因此a与b都指向同一块内存
cout << "a = " << a <<endl;
cout << "b = " << b <<endl;
system("pause");
return 0;
}
我们会发现,打印出来的a与b都是10。
通过修改b也可以修改a的值,本质就是同一个地址,两个名字都可以访问
#include <iostream>
using namespace std;
int main()
{
// 数据类型 &别名 = 原名
int a = 10;
int& b = a; // 创建引用给a的地址再起一个名字b,因此a与b都指向同一块内存
b = 100;
cout << "a = " << a <<endl;
cout << "b = " << b <<endl;
system("pause");
return 0;
}
我们发现a与b的输出都改为了100。
3.引用的注意事项
第一点就是引用必须初始化,下面这样定义是错误的,必须初始化。
int& c; // 错误的
这样是正确的,吧b初始化,表示b是a的别名,a与b都指向同一段内存。
int a = 10;
int& b = a;
第二点是引用在初始化后,不可以改变。
如果改成b=c,这个就是赋值操作,吧c里面的100赋值给b,打印后发现a与b都是100了.
b=c// 是赋值操作不是修改引用。
2.引用做函数参数
对于做函数的参数,函数传参时,有三种,一种是值传递,一种是地址传递,还有一种是传递引用。
1.作用:函数传参时,可以利用引用的计数让形参修饰实参
2.优点:可以简化指针修改实参
下面就介绍一下三种函数参数的区别
#include <iostream>
using namespace std;
// 1. 值传递 形参不会修饰实参,即main里面的a与b值不会改变
void mtSwap0(int a,int b)
{
int temp = a; // 在这里交换的是形参里面的a与b而不是实参里面的a与b,因此main里面的a与b没
a = b; //有改变,本质其实是四个个变量的地址不同
b = temp;
}
// 2. 地址传递 形参会修饰实参,即main里面的a与b值会改变
void mySwap1(int* a,int* b) //这里的a与b就是实参里面的a与b只是起一个别名但是地址都一样
{
int temp = *a; //因为之间传递的是地址,因此在这里交换,main里面的也得到了交换
*a = *b; // *a是取a里面的地址*b是取b里面的地址,这句就是把b地址里面的值赋给a地址里面的值
*b = temp;
}
// 3. 引用传递 形参会修饰实参,即main里面的a与b值会改变
void mySwap2(int& a,int& b)
{
int temp = a; //引用是起别名,但本质是操作main函数里面的地址通过起别名来操作main里面的值
a = b;
b = temp;
}
int main()
{
int a = 10;
int b = 20;
// mtSwap0(a, b); //发现a与b值没有交换,因此值传递 形参不会修饰实参
// mySwap1(&a, &b); //发现a与b值交换,因此地址传递 形参会修饰实参
mySwap2(a, b); //引用即&c = a表示 a与c同一个地址 发现a与b值交换,因此引用传递 形参会修饰实参
cout << "a = " << a << endl;
cout << "b = " << b << endl;
return 0;
}
总结:通过引用参数产生的效果与按指针传递是一样的,引用的语法更加清晰。
3.引用做函数的返回值
1.不要返回局部变量的引用。
因为局部变量属于栈区,属于改函数执行完后,改内存被编译器直接释放,数据就消失了。
通过以下案例展示
#include <iostream>
using namespace std;
int& ceshi()
{
int a = 10;
return a;
}
int main()
{
int& reference = ceshi();
cout << "reference = " << reference << endl;
cout << "reference = " << reference << endl;
system("pause");
return 0;
}
结果显示第一次打印是正常的,那是因为编译器做了保留,还没来得及释放这个内存
第二次打印就是乱码,表示编译器已经把局部变量a的地址释放。
解决方法是可以在 int a前面加static把局部变量改为全局变量,这样在执行完函数后,a的内存就不会被释放了,因为他是静态变量放在全局区,这里的数据是在程序结束后系统自己释放。对于内存的那个区可以在我的其他文章详细看一下。
代码测试
#include <iostream>
using namespace std;
int& ceshi()
{
static int a = 10;
return a;
}
int main()
{
int& reference = ceshi();
cout << "reference = " << reference << endl;
cout << "reference = " << reference << endl;
cout << "reference = " << reference << endl;
cout << "reference = " << reference << endl;
system("pause");
return 0;
}
发现打印的结果都是10没有出现上面的第二次之后乱码。
总结:不能返回局部变量的引用,要想返回就在数据类型前面加static改为静态变量。
2.函数的调用可以作为左值。
#include <iostream>
using namespace std;
int& ceshi()
{
static int a = 10;
return a; //这里的a是原名
}
int main()
{
int& reference = ceshi(); // 通过这句给a起别名 别名叫reference
cout << "reference = " << reference << endl;
cout << "reference = " << reference << endl;
ceshi() = 1000; //这句相当于给a赋值1000,ceshi()这个函数返回的就是a,等价于a = 1000
cout << "reference = " << reference << endl;
cout << "reference = " << reference << endl;
system("pause");
return 0;
}
通过上面代码我们发现
前两次打印10,后面两次打印1000,这个原因是首先我们通过
int& reference = ceshi();
这句代码给变量a起了一个别名叫reference,因为变量a用static声明了因此是一个全局区的变量。在程序运行完后,系统自动释放改空间。
通过打印我们可以看出reference是等于10的。
ceshi() = 1000;
这句就是函数的调用可以作为左值,因为ceshi()返回的是变量a,那么就是a = 1000,
又因为reference 是a的别名,两个是一个内存,因此在后面两次打印reference的值的时候就是1000了。
总结:如果函数的返回值是引用,那么这个函数的调用可以作为左值。
左值就是在等号左边的东西(如 a=10,那么a就是左值)。
4.引用的本质
引用的本质在c++里面就是在内部实现一个指针常量
指针常量是什么如果不清楚可以在我前面的文章查看,就是指针的指向不可以修改,指向的值可以修改。
int& ref = a; 就等于 int* const ref = &a;就是指针常量,即指针的指向不可以修改
ref = 20; 内部ref就是解引用,即 *ref =20 ;只不过我们使用int& ref这样的话编译器就自动帮我们转换了。直接ref = 20就可以了,而不用*ref = 20;
下面就是实列
#include <iostream>
using namespace std;
// 引用本质就是 int* const ref = &a int& ref = a;
void func(int& ref)
{
ref = 100; // ref是引用 本质是 *ref = 100
}
int main()
{
int a = 10;
int& ref = a; // 本质就是 int* const ref = &a指针指向不可以修改,值可以修改
ref = 20; // 本质就是 *ref = 20 ;
cout << "a = " << a << endl;
cout << "ref = " << ref << endl;
func(a);
system("pause");
return 0;
}