const 引用 引用的本质 右值引用与指针

引用 const 指针

(1) 定义引用时 必须初始化

(2) 没有所谓的空引用

(3) 没有引用的引用

int main() 
{
	int a = 10;
	int& b = a;
    
	b += 10;
	a += 20;
    
	int* p = &a;
	int* s = &b;   
    
    cout<<p<<endl;
    cout<<s<<endl;
	//可以得到 p==s
	//证明 a和b指的是同一个实体
}
const与指针
int a=10,b=20;
int *p1=&a;
*p1=100;
 p1=b;

//const 修饰指针所指之物 限制不能修改指针所指之物的值
const int *p2=&a;    int x=*p2; //可读
int const *p3=&a;   *p2=100;//error  不可以修改指针指向的值
                     p2=&b;//可以修改指针自身的值

//const 修饰指针自身 限制不能修改指针自身的值    
int *const p4=&a;    int x=*p4;//可读
                     *p4=100; //允许修改指向的值
                      p4=&b;//error 不允许修改自身的值

//const 既修饰指针指向也修饰指针自身  限制既不能修改指针自身的值也不能修改指针所指之物的值
const int*const p5=&a;   int x=*p5//可读、
                         *p5=100;//不允许修改指向的值
                          p5=&b;//不允许修改指值自身的值

int a=10,b=20;
const int *p=&a;   //不允许修改所指之物的值

int *s0=p; //error 能力扩展了 此处依然可以通过*s0改变a的值从而改变*p的值 矛盾

const int *s1=p;

int *const s2=p;//error 能力扩展了 const 只能限制 s2自身的值   此处依然可以通过*s2改变a的值从而改变*p的值 矛盾

const int * const s3=p;
int a=10,b=20;

//不允许修改指针自身的值
int * const p=&a;    int x=*p;
                    *p=100;
                     p=&b;//error

int *s0=p;   int x=*s0
             *s0=100;
              s0=&b;//true  s0指向的修改 并不会影响 p的指向

const int *s1=p;      s1=&b//true s1的修改 并不会影响 p的值

int *const s2=p;

const int * const s3=p;


指针的引用
---------------------------------------------
int a=10,b=20;
int *s=&a;

int *&p1=s;//p1是s的别名
              *p1=100; a的值改变  => *s=100
               p1=&b  <==> s=&b;

const int *&p2=s;       *s+=100;  a的值也会改变 
                         *p2+=100;//error
                  s=&b; 导致 p2=&b <==> *p2=b//ok 
                      
int *const &p3=s;        p3=&b;//error
                         s=&b; 导致 p3=&b;
                         *p3=100;//ok

const int *const &p4=s;     p4=&b;//error
                            *p4=100;//error
                             s=&b; 导致 p4=&b 
                    *s=100 a的值也会改变 *p4也会改变
---------------------------------------------
int a=10,b=20;
const int *s=&a; //*s=100;  error

int *&p1=s; //error *p1的改变  会影响 *s的值

const int *&p2=s; 

int *const &p3=s; //error *p1的改变  会影响 *s的值

const int *const &p4=s;  
----------------------------------------------
int a=10,b=20;
 int *const s=&a; //*s=100;  error

int *&p1=s; 
//error p1是s的别名 p1自身的改变会影响s的改变 const 对s自身改变进行了封锁

const int *&p2=s; 
//error p3是s的别名  p3自身改变会影响s的改变 因为const 对s自身改变进行了封锁

int *const &p3=s;

const int *const &p4=s; 
const 易错点
class Array
{
	int* ptr;
public:
	Array(int x)
	{
		ptr = new int[10];
		for (int i = 0; i < 10; ++i)
		{
			ptr[i] = x;
		}
	}
	//void fun(const Array * const this)
	//此处第一个const 限制this指针所指之物不能改变
	//this所指对象中 ptr的值不能改 this->ptr 不能改 也就是 ptr不能指向其他地址
	//但是不能对ptr所指之物进行限制  此const没有连级性约束
	//this->ptr[i]+=100; //true
    
    //只有在设计Array时 设计为 const int *ptr;才可以约束 ptr的指向
	void fun()const
	{
		for (int i = 0; i < 10; ++i)
		{
			this->ptr[i] += 100;//true
		}
	}
};
引用的本质

引用的本质是被限制的指针(限制指针自身的值不能改变)

int a=10;
int &b=a;  ==>   int * const b = &a;

请添加图片描述

引用必须初始化的原因
int a=10;

int &b;  ==> int *const b; //error b的值是随机值 野指针
             b=&a;//error const 限制了 b的值 不能被修改 

所以: 指针自身是常性时必须初始化
int *const b=&a;//指针自身是常性必须初始化

所以: 引用必须初始化
int &b=a;

得出:引用必须初始化
常引用

常引用可以引用以下几种类型的数据:

1.普通变量

2.常变量

3.字面常量

	int a = 10;
	const int a1 = 100; 

	const int& b1 = a1; //常引用引用常变量  


	const int& b = a;   //常引用引用普通变量
	//const int * const b = &a;



	const int& c = 10; //常引用可以引用字面常量
	//等价下面三步
	//int tmp = 10;        //先定义一个临时量
	//const int& c = tmp;  //引用临时量
	//const int* const c = &tmp;

汇编

请添加图片描述

函数返回值为引用的注意事项
首先看一个例子
int* fun() {
	int ar[10] = { 12,23,34,45,56,67,78,89,90,100 };
	return ar;//返回数组首地址 存入一个临时量之中
}
int main() {
    
	int* p = fun(); //返回数组 ar的首元素的地址
    //调用函数 分配栈帧
    //函数调用结束 分配的栈帧销毁
    //此时 p指向 ar首元素的地址 但是由于 函数分配的栈帧已被销毁 p此时是失效指针 
    //不能从被回收的空间中获取值(错误的 不安全的)
    
	for (int i = 0; i < 10; ++i) {
        
		printf("%p => %d\n", p, *p);
        //printf函数也会分配栈帧
        //会骚扰到之前 fun使用的且当前已经回收的栈帧 
        
		p += 1;
	}
	return 0;
}
/* 输出结果:
00AFF960 => 12
00AFF964 => -858993460
00AFF968 => -858993460
00AFF96C => -858993460
00AFF970 => 0
00AFF974 => -858993460
00AFF978 => -858993460
00AFF97C => 23
00AFF980 => 4659967
00AFF984 => 11532924
*/
//结论:不可以从失效空间中获取值 这是不安全的
以引用返回局部变量 (不允许 不安全)

请添加图片描述

函数能把什么样的数据以引用返回呢?

此变量的生存期不受函数生存期的影响(函数的生存期不会影响变量的生存期),那么此变量可以以引用返回.

函数结束,变量仍然存活就可以以引用方式返回.

以下几种类型的变量 函数可以以引用返回

1.加上静态关键字 static 的变量(静态变量)(静态变量在数据区,函数结束,静态变量依然存活).

2.全局变量

3.以引用进入的变量(形参为引用,形参可以以引用返回)

4.堆区

练习例子
class Object
{
private:
	int value;
public:
	Object(int x = 0) :value(x) {}
	~Object() {}
	void SetValue(int x) { value = x; }
	int GetValue() { return value; }
	//使用一个函数实现SetValue和GetValue()函数的功能
    //既能设置值也能获取值

	//int * const Value(Object * const this)
	int& Value() {  //普通对象可以调用
		return value;
	}

	//const int * const Value(const Object * const this)
    const int& Value() const //常对象可以调用,普通对象也可以调用
	{//返回值类型必须与函数类型相匹配 形参为const int * const 如果要返回形参的值 返回值类型也必须为 const int * const   
		return value;
	}
};
int main() {

	Object obj(10);//普通对象
	obj.Value(); ·
	//首先调用最为匹配的 int& Value();
	//如果没有最匹配的函数 可以退而求其次 调用重载函数const int& Value()const
	
	const Object objc(20);//常对象
	objc.Value();
	//只能调用 const int& Value()const
	
	int x = 0;
	x = obj.Value();  //x=obj.value;
	obj.Value() = 100;  //obj.value=100;
	cout << x << endl;
}
左值和右值 右值引用
左值右值定义
//lvalue 左值   xvalue   rvalue 右值   prvalue 重右值
//左值可以取地址&;  生存期和变量名一样 
//右值 不可以取地址 因为无名字
//字面常量 比如:10 叫做重右值
int main() {
	//可以取地址的值就是左值
	int a = 10;  //左值
	//左值可以取地址&a;  生存期和变量名一样 
    //右值 不可以取地址 无名字
    
	int& b = a;
    int&& c=10;
    //int&& d = c; error c虽然是10的右值引用 但是 此处c是个变量名已经具有名字  一旦具有名字就不是右值 就变成一个左值了  
	//具有名字 就是左值
    
    String s1;
    String& sx=s1; //正确
    //String& sx=String("hello");// error 调用构造函数构造的是一个无名对象 不具有名字;  左值引用必须引用一个具有名字的对象
    String&& sy=String("hello");// true 调用构造函数构造的是一个无名对象  可以使用右值引用 

}
右值引用
int main() {

	const int& a = 10;
	//相当于
	//int tmp=10;  1.定义临时空间,存放10
	//const int& a = tmp;
	//const int* const = &tmp; 2.用指针a指向临时空间

	int&& b = 10; //右值引用
	
	//b = 10;
	// 底层
	//int tmp = 10;    
	//int* const b = &tmp; 可以改变值

	return 0;


}
int fun() 
{
	int a = 10;
	return a;
}
int main() 
{
	int x = fun();
	//将亡值 在函数调用结束 将亡值消失

	//int& b = fun(); //error fun() 此时 是10 是一个右值

	const int& c = fun();
	//int tmp = 10;
	// const int * const c = &tmp;
	//直接把a的值放入主函数栈帧中
	//c引用此空间
	//值不可改变                 


	int&& d = fun();
	//int tmp = 10
	// int * const d = &tmp;
	//直接把a的值放入到主函数栈帧空间中
	// b引用 
	//值可以改变
	//相当于把 将亡值的生存期 扩大到 与 右值引用具有相同的生存期
	
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值