引用(C++ 推荐 使用引用技术,因为语法方便。引用的本质是指针常量,但是所有的指针操作编译器都帮我们做了)
引用的基本使用
作用:给变量起别名(两个变量除了名字外是完全相同的)
语法:数据类型 &别名 = 原名;
#include <iostream>
#include <string>
using namespace std;
int main()
{
int a = 10; // 原名
int &b = a; // 引用(起别名)
cout << "a = :" << a << endl;
cout << "b = :" << b << endl;
b = 1001; // 修改别名的内容
cout << "a = :" << a << endl;
cout << "b = :" << b << endl;
system("pause");
return 0;
}
运行结果:
引用的注意事项
1、引用必须初始化
解释:int &b; // 这就是错误的,因为没有经过初始化
2、引用在初始化后,不可以改变(只可以当一个变量的别名,不可以再改成其他变量的别名)
int &b = a;
int &b = c; // 错误,因为b已经是a的别名了,不可以被改成其他变量的别名
引用做函数参数
作用:函数传参时,可以利用引用的技术让形参修饰实参(与形参是指针的作用相同)
优点:可以不使用指针来修改实参
#include <iostream>
#include <string>
using namespace std;
// 1、值传递
void SWAP01(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
// 2、地址传递
void SWAP02(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
// 3、引用传递(作用和2、地址传递作用相同)
void SWAP03(int &a, int &b) // 引用传递时,传进来的就是实参a和b“本身”(别名),而不是a、b的副本
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int a = 10;
int b = 20;
SWAP01(a, b); // 1、值传递(形参不修饰实参)
cout << "SWAP01 a = :" << a << endl;
cout << "SWAP01 b = :" << b << endl;
SWAP02(&a, &b); // 2、地址传递(形参会修饰实参)
cout << "SWAP02 a = :" << a << endl;
cout << "SWAP02 b = :" << b << endl;
SWAP03(a, b); // 3、引用传递(形参会修饰实参,作用和2、地址传递一样)
cout << "SWAP03 a = :" << a << endl;
cout << "SWAP03 b = :" << b << endl;
system("pause");
return 0;
}
运行结果:
引用做函数的返回值
作用:引用可以作为函数的返回值存在
【注意】不要返回局部变量的引用(同“不要返回局部变量的地址”类似)
函数调用还可以作为左值
#include <iostream>
#include <string>
using namespace std;
// 引用做函数的返回值
// 1、不要返回局部变量的引用
int& test01() // 引用的方式作为函数的返回
{
int a = 10; //存放在栈区(函数执行完a就被释放)
return a; // 返回局部变量(由于返回值类型为引用,所以这是在返回局部变量a的引用,也就是返回局部变量a的别名)
// 注意这种操作是非法的,因为它返回的是局部变量a的引用(别名),也就是说它返回的其实就是a本身(而不是a的值)
}
// 2、函数的调用可以作为左值
int& test02()
{
static int a = 10; // 存放在全局区,在全部的程序结束后由系统释放
return a; //这种情况就没有问题,因为执行完test02()后,a不会被释放
}
int main()
{
int &ref = test01(); // 由于返回值类型为引用,所以将引用ref来作为局部变量a的别名
// 等效为 int &ref = a; 但a所在的地址空间已经被释放了,里面的值已经不是10了
// 所以这一步可以看出,int &ref = a;其实是让 ref 与 a 指向同一个地址
cout << "ref = " << ref << endl;
int &ref2 = test02(); // ref2和静态局部变量a指向同一个地址,执行完test02()后a并没有被释放,因此下面的输出是正确的
cout << "ref2 = " << ref2 << endl;
cout << "ref2 = " << ref2 << endl;
test02() = 1000; // 返回值为引用类型时,函数调用可以作为左值
// 这句等效为a = 1000;因为test02()返回的就是a,注意不是a的值
cout << "ref2 = " << ref2 << endl;
cout << "ref2 = " << ref2 << endl;
system("pause");
return 0;
}
运行结果:
引用的本质
本质:引用的本质在C++内部实现是一个指针常量(即常量类型的指针,也即该指针本身就是一个常量)
int &ref = a; // 会自动转换为 int * const ref = &a; 所以ref不能是其他变量的别名(引用一旦初始化后,就不可以发生改变)
#include <iostream>
#include <string>
using namespace std;
void func(int &ref)
{
ref = 100; // 发现ref是引用,自动转成 *ref = 100;
}
int main()
{
int a = 10;
int &ref = a; // 发现是引用,转换为int * const ref = &a
ref = 20; // 发现ref是引用,自动转成 *ref = 20;
cout << "a = " << a << endl;
cout << "ref = " << ref << endl;
system("pause");
return 0;
}
运行结果:
结论:C++ 推荐 使用引用技术,因为语法方便。引用的本质是指针常量,但是所有的指针操作编译器都帮我们做了。
常量引用
作用:常量引用主要用来修饰形参,防止误操作
在函数形参列表中,可以加const修饰形参,防止形参改变实参。
#include <iostream>
#include <string>
using namespace std;
void showValue(int &a) // 引用的方式进行接收,即传入的是实参本身
{
a = 1000; // 实参会被修改
cout << "showValue val = " << a << endl;
}
void showValue_2(const int &a) // 引用的方式进行接收,即传入的是实参本身。但由于加了const修饰,就把引用变成了一个常量。
{ // 因此形参引用的方式加const是为了防止误操作(常见的应用场景)
//a = 1000; // 此操作非法,实参不会被修改
cout << "showValue val = " << a << endl;
}
int main()
{
// 常量引用
// 使用场景:用来修饰形参,防止误操作
int a = 10;
// int &ref = 10;// 此操作是非法的,因为右边是常量。被引用的对象必须是在栈区或堆区的数据,而常量位于常量区,即全局区。
const int &ref = 10; // 此操作是合法的。加上const后,编译器将代码修改为 int temp = 10; const int &ref = temp;
// 也就是说现在的引用是引用一块临时的空间比如temp,但其实我们找不到它的原名(编译器起好了),我们只能通过别名ref去操控它
// int &ref是一个指针常量,再加一个const,就既是指针常量,又是常量指针
// ref = 20; // 此操作非法,因为被ref指向的对象已经是一个常量了
int val = 100;
showValue(val); // 由于showValue()函数是以引用的方式传递参数的,因此传入的就是实参val本身
cout << "main() val = " << val << endl; // 验证实参是否被修改
system("pause");
return 0;
}
运行结果: