c++引用相关

c++引用相关

左值

具名内存 —>也就是能够取该值取对象地址

分类

非常左值

无 const 修饰

常左值

有 const 修饰

举例

int a = 10; // a 为非常左值
&a; //语法正确
a = 20;// 语法正确

const int b = 10; // b 为常左值
&b; // 语法正确
b = 20; // 语法错误,因为被 const 修饰,无法修改 b 的值

生命周期

当前作用域的生命周期

右值

匿名内存—>不能对该对象取地址

举例

# 常量
10;
&10;//err,编译器会报错:lvalue required as unary '&' operand(需要左值作为一元 '&' 的操作数)
# 函数返回值
int foo() {}
int main() {
		foo(); // 执行流程,1.会在 mian 的函数栈中分配一块匿名内存,用于接收该函数的返回值。2.生成跳转指令
  	&foo(); // 对函数调用取地址,仍然报错:err,编译器会报错:lvalue required as unary '&' operand(需要左值作为一元 '&' 的操作数)
  	10 = 20; // 编译器报错:lvalue required as left operand of assignment 10 = 20(赋值操作的左操作数必须是左值))
		// 类似的,函数调用的值也不能修改
    // foo() = 20; //error
}

生命周期

语句级别的生命周期。(直白点就是分号结束)

备注

原因是98/03标准标注更改右值毫无意义,所以编译器对于更改右值的操作会报错。

引用

  • 引用即内存的别名
# 示例
int a = 10;
int& b = a; // b 为 a 的引用
  • 引用本身不占用内存并非实体,对引用的所有操作都是对目标内存进行操作
#include <iostream>

using namespace std;

int main() {
  int a = 10;
  int& b = a; // 应该理解为 b是 a所代表的内存的别名
  b = 20; // 实际修改的是 a 的内存的值
  cout << "a = " << a << endl;
  cout << "b = " << b << endl; // 读取的是引用 b的目标内存的值,也就是 a 内存的值
  // 如果此时对 a,b 同时进行取地址,会发现是同一个地址,佐证上面的内容,此处省略。
}
# 输出结果
# a = 20
# b = 20
  • 引用必须初始化,且不能更换目标
int& c;// 未初始化引用,编译器会报错:'c' declared as reference but not initialized(c 作为引用但是没有被初始化)
  • 不存在引用的引用
  • 引用的常属性应该和目标的常属性保持“一致”
const int a = 10;
const int& b = a; // ok
int& c = a; // err
# 因为原值不能修改,所以也不能通过引用对原值进行修改
  • 引用的限定可以比原值更加严格
int a = 10;
const int& b = a; // ok
  • 引用可以延长右值的生命周期

​ 如果一个右值被声明了引用,则会将右值的生命周期提高到引用的作用域

int& a = 10; //err,编译器会报错,invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int' (无效的初始化:不能将 int 类型的右值绑定到 int& 类型的非 const 引用)
const int& b = 10; //ok,ps. 10不让改是因为标准规定修改右值无意义,引用 b 不让修改是因为加了常属性 const,最终达到引用 b 和 10 都不能修改
// 同理,函数返回值也可以做类似操作
int& c = foo();//err
const int& d = foo(); //ok
  • 常引用即万能引用

​ 可以引用常左值,非常左值,右值

​ ps:如果常引用引用的是非常左值,那么通过常引用将失去对目标内存的修改权限

  • 常指针即万能指针

​ 可以指向常左值,非常左值,右值

​ ps:如果常指针指向的是非常左值,那么通过常指针将失去对目标内存的修改权限

  • 引用的生命周期不能长于目标

应用

在 c 语言中的数据传递(初始化,赋值,传参,返回值。。。)都是值传递。(将数据复制一份)

example:

int a = 10;
a = 30;
foo(a);
int b = foo(a);

在 c++中可以进行引用传递

引用型参数

函数的形参是实参的别名,避免对象复制带来的开销。

非常引用型形参

函数内部可以修改实参

#include <iostream>
using namespace std;

// 如果调用此函数,则原数的值不会发生更改,因为是值传递
void swap(int x,y) {
  int z = y;
  y = x;
  x = z;
}
// 调用此函数,原数的值会发生更改,但是注意,此时还是进行值传递,只不过传递的值是参数的地址,通过修改地址指向的值进行数据修改
void swap(int* x,y) {
  int z = *y;
  *y = *x;
  *x = z;
}
// 调用此函数,原数的值会发生更改,此时是直接将引用传递到函数中进行数值修改
void swap(int& x,y) {
  int z = x;
  x = y;
  y = z;
}
int main() {
  int a = 10,b = 20;
  // 引用修改
  swap(a,b);
  // 指针修改
  swap(&a,&b);
}
常引用型实参

防止对实参的意外修改

void print1(int& x,int& y) {
  // 此时可能发生对实参进行修改
}
void print2(const int& x,const int& y) { // 常引用型形参
  x = 888;// 在函数形参前添加 const 修饰,可以避免函数内部对实参进行修改,如果意外发生修改操作,则编译器会报错:assignment of read-only referen 'x'(只读的引用赋值)
}

print1(444,555); // err
print2(444,555); // ok
引用型返回值

从函数中返回引用,一定要保证在函数返回以后,该引用的目标依然有效

返回全局,静态变量的引用
# include <iostream>
using namespace std;
int g_value = 0;
// 引用型返回值
int& foo() {
  return g_value;
}
int& bar(){
  static int s_value = 0;
  cout << s_value <<endl;
  return s_value;
}
int main() {
  foo() = 100; // 此处不会生成匿名内存来保存返回值,因为此处 foo()函数所代表的含义为 g_value 的引用,所以此处可以进行赋值操作。同时 g_value 的值也会被修改为 100.
  bar() = 200; //同上,s_value 的值会被修改为 200。
  bar(); // 因为 s_value 为静态局部变量,所以只会初始化一次,第二次调用可以通过输出语句查看 s_value 的值是否被修改(此处被修改)
  return 0;
}
返回在堆中动态创建的对象的引用
int& hum() {
  int* pn = new int; // 在堆中申请 4 个字节的堆内存
  return *pn;
}

int main() {
  hum(); // 此处返回的类型为*pn 的引用
}
返回引用型参数本身
int& fun(int& x) {
  return x; // 返回引用型参数本身
}

int main() {
  int a = 10;
  fun(a) = 200;// 返回值还是代表了 a 的引用,也就是说 a 的值还是被修改为了200
}
不能返回局部变量的引用
int& boo() {
  int m = 100;
  return m;
}

int main() {
  boo(); // 此处 boo 函数调用结束跳转回来后,m 对象已经被销毁,此时引用指向的目标无效,编译器会报警告(严重!)
}
非常引用型返回值

通过引用可以修改目标

常引用型返回值

不能通过引用修改目标

指针引用对比

在实现层面(汇编),引用就是指针,但是在 c++语言层面,引用不是实体类型

  • 指针可以不做初始化,但是引用必须初始化
  • 指针的目标可以在初始化后随意变更(除非是指针常量),但是引用一旦初始化后就无法变更
  • 存在空指针,但是不存在空引用
  • 存在二级指针(指针的指针),但是不存在二级引用(引用的引用)
  • 存在指针的引用,但是不存在引用的指针
  • 存在指针数组,但是不存在引用数组,存在数组引用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值