const 以及 引用& 的用法

const T

	{   //【const T】

		int i = 5;
		const int constInt = 10;        //正确,声明常量必须初始化,
		const int constInt2 = i;        //正确
		//const int constInt3;          //错误,未被初始化
		//constInt = 20;                //错误,常量值不可被改变		
	}

在《C编程专家》一书中,介绍const 关键字不是常量;【2.2多做之过 章节中】,但是现在实际编译器是能够通过的

const T*

	{	//【const T*】

		int t = 3;
		const int i = t;
		const int i2 = 10;	
		const int* pInt = &i;           //正确,指向一个const int对象,即i的地址
		//*pInt = 10;                   //错误,不能改变其所指向的对象值(存储数据)
		pInt = &i2;                     //正确,可以改变pInt指针本身的值,此时pInt指向的是i2的地址
		const int* p2 = new int(8);     //正确,指向一个new出来的对象的地址
		delete p2;                      //正确
		//int* pInt = &i;               //错误,i是const int类型,类型不匹配,不能将const int * 初始化为int *
		int nValue = 15;
		const int * pConstInt = &nValue;    //正确,可以把int 赋给const int *,但是pConstInt不能改变其所指向对象的值,即nValue
	   //*pConstInt = 40;                   //错误,不能改变其所指向对象的值
	}

 

T *const

{   

		int nValue = 10;
		int* const p = &nValue;
		int* const p2 = &nValue;

		int nValue1 = 10;
		int nValue2 = 20;
		int* const constPoint = &nValue1;
		//constPoint = & nValue2;           //错误,不能改变constPoint本身的值(指向地址)
		*constPoint = 40;                   //正确,可以改变constPoint所指向的对象值,此时nValue1 = 40

		const int nConstValue1 = 5;
		const int nConstValue2 = 15;
		const int* pPoint = &nConstValue1;
		//*pPoint  = 55;                    //错误,不能改变pPoint所指向对象的值
		pPoint = &nConstValue2;             //正确,可以改变pPoint指针本身的值,此时pPoint邦定的是nConstValue2对象,即pPoint为nConstValue2的地址

		const int* const pPoint2 = &nConstValue1;
		//*pPoint2  = 55;                    //错误,不能改变pPoint所指向对象的值
		//pPoint2 = &nConstValue2;           //错误,不能改变pPoint本身的值	
	}

小结: 

  const int* 指针指向的对象值(存储的数据)不可以改变,但指针本身的值(指向的地址)可以改变;
  int* const 指针本身的值(指向的地址)不可改变,但其指向的对象值(存储的数据)可以改变。
  const int* const 是一个指向常量对象的常量指针,即不可以改变指针本身的值,也不可以改变指针指向的对象。

【左值、右值】:
在C++11中所有的值必属于左值、右值两者之一,右值又可以细分为纯右值、将亡值。
在C++11中可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值(将亡值或纯右值)。
举个例子,int a = b + c, a 就是左值,其有变量名为a,通过&a可以获取该变量的地址;
表达式b + c、函数int func()的返回值是右值,在其被赋值给某一变量前,我们不能通过变量名找到它,&(b + c)这样的操作则不会通过编译。

【右值、将亡值】:
在理解C++11的右值前,先看看C++98中右值的概念:C++98中右值是纯右值,纯右值指的是临时变量值、不跟对象关联的字面量值。
临时变量指的是非引用返回的函数返回值、表达式等,例如函数int func()的返回值,表达式a + b;不跟对象关联的字面量值,例如true,2,”C”等。

C++11对C++98中的右值进行了扩充。在C++11中右值又分为纯右值(prvalue,Pure Rvalue)和将亡值(xvalue,eXpiring Value)。
其中纯右值的概念等同于我们在C++98标准中右值的概念,指的是【临时变量和不跟对象关联的字面量值】;
将亡值则是C++11新增的跟右值引用相关的表达式,这样表达式通常是将要被移动的对象(移为他用),
比如返回右值引用T&&的函数返回值、std::move的返回值,或者转换为T&&的类型转换函数的返回值。

将亡值可以理解为通过“盗取”其他变量内存空间的方式获取到的值。
在确保其他变量不再被使用、或即将被销毁时,通过“盗取”的方式可以避免内存空间的释放和分配,能够延长变量值的生命期。

总结!:左值表示存储数据的地方,右值表示存储的数据。
左值可以在等式的左边,也可以是右边,但是右值只能是放在等式的右边;
可以理解为,左值是具有存储地址 + 存储数据的特性,右值只是具有存储数据的特性;*/

 //下面的例子为了好注释,将左值用 nonConst 右值用Const 代替说明

{
		//为了更好的理解,常量和非常量的初始化,用右值、左值带入说明。
		int c = 2;		 //正确,左值用右值初始化(变量初始化)
		const int d = c; //正确,左值用左值初始化(常量初始化)
		int e = d;		 //正确,左值用左值初始化(变量初始化)

		/*结论:初始化的是左值,const定义的是常量而不是右值,常量不代表右值,左值也不代表非常量。
				可以看出左值除  const定义的常量是左值,左值是非常量。
				为好理解我们称:常量左值为Const左值,非常量左值为nonConst左值*/
	}

	{  //左值好写,右值除一切常数、字符和字符串都是右值外几乎写不出来的,关键是右值不具有名字;
		int aIn;				//
		//int &d;				//错误,Const左值引用不初始化,编译失败
		int &bIn = aIn;			//正确,
		//const int &acn;		//错误,nonConst左值引用不初始化,编译失败
		const int &acn = aIn;	//正确,

		/*结论:无论是声明一个左值引用还是右值引用,都必须立即进行初始化。
				可以理解为是引用类型本身自己并不拥有所绑定对象的内存,只是该对象的一个别名,
				【引用(bIn)是对象(aIn)的另一个名字,注意!!aIn也不是真名,但可以这样理解。】
				而其原因是,左值引用是具名变量值的别名,而右值引用则是不具名(匿名)变量的别名。*/
	}

	{	///左值引用就是对一个左值进行引用的类型。右值引用就是对一个右值进行引用的类型,
		///事实上,由于右值通常不具有名字,我们也只能通过引用的方式找到它的存在。
		int aIn = 1;			// nomConst左值
		const int bCn = 2;		// Const左值
		const int &cCn = aIn;	// Const左值引用绑定到nonConst左值,编译通过
		const int &dCn = bCn;	// Const左值引用绑定到Const左值,编译通过
		const int &eCn = 3;		// Const左值引用绑定到右值,编程通过 #为甚麽可以???#
		
	    /*结论:常量左值引用是个“万能”的引用类型,它可以接受nonConst左值、Const左值、右值对其进行初始化。
				不过常量左值所引用的右值在它的“余生”中只能是只读的。*/

		//int &f = 2;			 //错误,nonConst左值引用绑定到右值,编译失败
		int &gIn = aIn;			 //正确,nonConst左值引用绑定到nonConst左值,编译通过
		//int &fIn = bCn;		 //错误nonConst左值引用绑定到Const左值,编译失败

		/*结论:nonConst左值只能接受nonConst左值对其进行初始化,左值引用通常也不能绑定到右值。*/
	}

	{	/* cosnt T& 深入了解:
		(1)首先,如果需要的话,将应用到类型T的隐式类型转换。
		(2)而后,将结果存入一个类型T的临时变量。
		(3)最后,将此临时变量用作初始化的值。*/

		//例如:
		const double& cd = 1; //ok

		//初始化的解释是:
		double temp = double(1); //首先建立一个具有正确数据类型的临时变量
		const double& cd = temp; //而后用这个临时变量作为cd的初始式,temp是个右值

		/*结论:由于左值的一大特点是可以对其赋值,而const正好将这个特点给阉割了,使该表达式成为了一个右值,所以可以用右值来初始化。*/
	}

 

知道了引用的概念,就可以更进一步的学习,const T*& 、T const* &

const int& 


		int i = 5;
		int& rInt = i;                      //正确,int的引用
		const int constInt = 10;
		const int& rConstInt = constInt;    //正确,引用及邦定的值都是常量
		const int& rConstInt2 = rInt;       //正确,用rInt邦定的对象(非常量)进行赋值
		const int& rConstInt2_1 = i;        //与上,作用相同
		rInt = 30;                          //这时,rConstInt2、rInt、i的值都为30
		// rConstInt2 = 30;                 //错误,rConstInt2是常量引用,rConstInt2本身不能改变所指向的对象

		/*调试查看内存如下:
		+&i	         0x006ffe74 {30}    int *
		+&rConstInt2	 0x006ffe74 {30}  	const int *
		+&rConstInt2_1 0x006ffe74 {30}	const int *
		+&rInt	     0x006ffe74 {30}    int *
		结论:引用绑定的是常量,且绑定无法改变。*/

/**/
		int i2 = 15;
		const int& rConstInt3 = i2;         //正确,用非常量的对象为其赋值
		const int& rConstInt4 = 50;         //正解,用一个常量值为其赋值
		///如果用表达式赋值后,它的值是表达式的“临时变量”,其值不会再变,它是纯右值【在左值、右值中说明】。
		const int& rConstInt5 = i + i2;     //正确,用表达式为其赋值,值为45

		/*调试查看内存如下:
		+&rConstInt5	0x004ff6f8 {45}	 const int *
		+&rConstInt3	0x004ff740 {15}	 const int *
		+&i2	0x004ff740 {15}	         int *
		+&i	0x004ff77c {30}	             int *
		结论:可以看出表达式赋值时,它产生了一个不同于,i和i2 的新地址。*/

		i = 20;								//此时i=20, rInt = 20, rConstInt4 = 45,说明rConstInt4邦定的是i + i2的临时变量		

 

【1.const T*】是指向常量的指针;【2.const T*&】指向常量的指针的引用。

		const int nConstValue = 1;                      //常量对象
		const int nConstValue2 = 2;                     //常量对象
		const int* pConstValue = &nConstValue;          //指向常量(对象)的常量指针
		const int* pConstValue2 = &nConstValue2;        //指向常量(对象)的常量指针
		const int*& rpConstValue = pConstValue;         //指向常量(对象)的常量指针的引用
		//*rpConstValue = 10;                           //错误,pConstValue指向的是常量(对象),常量(对象)的值不可改变
		rpConstValue = pConstValue2;                    //正确,此时rpConstValue是引用指向常量(nConstValue2)的常量指针(pConstValue2)
		pConstValue  = pConstValue2;                    //与上,作用相同 

		/*调试查看内存如下:
		+&nConstValue	    0x012ffa8c {1}	            const int *
		+&nConstValue2	0x012ffa80 {2}	            const int *
		+&rpConstValue	0x012ffa74 {0x012ffa80 {2}}	const int * *
		+&pConstValue  	0x012ffa74 {0x012ffa80 {2}}	const int * *
		结论:引用,等同于一个常量指针同时拥有两个名字,它的本质没变。
			  const T* 是指向常量指针,这意味着不能通过指针操作改变对象的值,因为对象是常量,
			  但是可以改变指针指向的常量对象*/

 

 【1.T *const】是指向非常量的指针;【2.T *const &】指向非常量的指针的引用。

	        int nValue = 5;
		int nValue2 = 10;
		int *const constPoint = &nValue;                //指向非常量(对象)的常量指针
		int *const constPoint2 = &nValue2;              //指向非常量(对象)的常量指针
		int *const &rpConstPoint = constPoint;          //指向非常量(对象)的常量指针引用,
		//rpConstPoint = constPoint2;                   //错误,constPoint是常量指针,指针本身的值(指针地址)不可改变
		*rpConstPoint = 20;                             //正确,指针指向的非常量(对象)值(存储数据)可以改变,

	    /*结论:T *const 是指向非常量指针,这意味着通过指针操作改变对象的值,因为对象是非常量*/
		int nValue = 5;                                 //nonConst左值
		const int  nConstValue = 1;						//也可以为:int const nValue = 5; 一般不要这样写。
		const int  nConstValue2 = nValue;				//正确,
		const int  nConstValue2_ = nConstValue;			//正确,
		const int* pConstValue3 = &nValue;			    //正确,
		const int* pConstValue3_ = &nConstValue;		//正确,
		const int*& rpConstValue = pConstValue3;		//正确,

		int *const rpConstPoint = &nValue;				//正确,
		int *const rpConstPoint2 = rpConstPoint;		//正确
		//int *const rpConstPointt_ = &nConstValue;		//错误,"const int *" 类型的值不能用于初始化 "int *const" 类型的实体。
		//int *const &rpConstPoint2 =  &pConstValue3;	//错误,"const int **" 类型的值不能用于初始化 "int *const&" 类型的实体。
		int *const &rpConstPoint2_ = rpConstPoint;		//正确,
	   /*总结!:【cosnt T*】 指向常量的指针,非常量和常量初始化。
				【cosnt T*&】 指向常量指针的引用,cosnt T* 初始化。
				【T *const】 指向非常量的指针用,非常量初始化。
				【T *const&】指向非常量的指针引用,T *const 初始化。*/
	

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值