C++引用
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间
类型& 引用变量名(对象名) = 引用实体;
void TestRef() { int a = 10; int& ra = a;//<====定义引用类型 printf("%p\n", &a); printf("%p\n", &ra); }
共用了同一块内存空间
注意:引用类型必须和引用实体是同种类型的
引用特性
1.引用必须在创建时进行初始化。
int number = 42; int& ref = number; // 引用初始化为变量 number
2.引用一旦引用一个实体,再不能引用其他实体
下面是一个示例说明引用在初始化后不能再引用其他实体:
int a = 42; int b = 99; int& ref = a; // 引用 ref 绑定到变量 a ref = b; // 错误!不能重新将 ref 绑定到变量 b
一旦引用初始化完成后,它将一直与该对象关联,不能再引用其他对象。
需要注意的是,引用可以通过指针的方式进行间接更改。通过指向引用的指针,可以间接修改引用所绑定的实体,但指针本身不能再引用其他实体。
下面是一个示例说明通过指针修改引用所绑定的实体:
int a = 42; int b = 99; int& ref = a; // 引用 ref 绑定到变量 a int* ptr = &ref; // 指针 ptr 指向引用 ref *ptr = b; // 通过指针间接修改引用所绑定的实体
3. 一个变量可以有多个引用
int number = 42; int& ref1 = number; // 第一个引用绑定到 number int& ref2 = number; // 第二个引用也绑定到 number
这意味着无论通过 ref1 还是 ref2 修改对象的值,实际上都是修改了同一个对象。
常引用
常引用(const reference)是指绑定到常量对象的引用。通过使用常引用,可以确保在引用的过程中,不会对被引用的对象做任何修改。
void TestConstRef() { const int a = 10; //int& ra = a; // 该语句编译时会出错,a为常量 const int& ra = a; // int& b = 10; // 该语句编译时会出错,b为常量 const int& b = 10; double d = 12.34; //int& rd = d; // 该语句编译时会出错,类型不同 const int& rd = d; }
有的同学会感到疑惑,为什么有的要加了const 才可以通过.这里要说一下权限的问题.
这里要先说一下
在引用的过程中
- 权限可以平移
- 权限可以缩小
- 权限不能放大
什么意思呢,下面我分别举例.
- 权限可以平移
const int a = 0; const int& c = a; // 权限的平移
- 权限可以缩小
int x = 0; const int& y = x; //权限可以缩小
- 权限不能放大
const int a = 0; int& b = a;// 权限的放大 error
那这样可以吗
const int a = 0; int b = a;
答案是可以的,因为这里是赋值拷贝
,b修改不影响a!
同学们在看看这个例子,这样引用正确吗
int i = 0; const double& d = i;
答案也是可以的:为什么i不是直接赋值给d的,他是通过一个临时变量来交换的,而这个临时变量拥有常属性,所有接收时候必须带上const来接收.
举一反3,同学在来看看这题
int func() { int a = 0; return a; } int main() { //int ret& = fuc();error const int& ret = func(); }
为什么不加const 编译不过?
答案: 其实跟上题差不多一样的道理,函数返回时候他是通过一个临时变量保存着a返回的,在这种情况下,常引用是需要的,因为临时变量的生命周期与表达式的生命周期相同,这个临时变量有着常属性,必须加上const 来接收,而const可以延长该临时变量的生命周期.通过使用常引用,将临时变量 a 的生命周期与常引用 ret 的生命周期进行了绑定.
当然,对于较小的数据类型(如整数或指针),编译器通常会选择将其存储在寄存器中进行返回。这是为了提高返回值的效率。这种寄存器返回值的优化是由编译器自动完成的。我们无需关心.
使用场景
1. 做参数
函数可以通过引用来传递参数,不仅可以避免复制大型对象的开销,还可以修改传入的参数。
void Swap(int& left, int& right) { int temp = left; left = right; right = temp; }
2. 做返回值
函数可以返回引用类型,使得可以直接对返回值进行修改,而无需拷贝。例如:
int& Count() { static int n = 0; n++; // ... return n; }
下面代码输出什么结果?为什么?
int& Add(int a, int b) { int c = a + b; return c; } int main() { int& ret = Add(1, 2); Add(3, 4); cout << "Add(1, 2) is :"<< ret <<endl; //输出7 return 0; }
1.函数运行时,系统需要给该函数开辟独立的栈空间,用来保存该函数的形参、局部变量以及一些寄存器信息等
2.函数运行结束后,该函数对应的栈空间就被系统回收
3.空间被回收指该块栈空间暂时不能使用,但是内存还在;比如:上课要申请教空,上完课之后教室归还给学校,但是教室本身还在,不能说归还了之后,教室就没有了
注意:如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用
引用返回,如果已经还给系统了,则必须使用传值返回。