《C++Primer》之——引用,及引用的应用场景

1. 引用不能拷贝,引用不可变

引用不能拷贝,也没得拷贝:因为“赋值运算符=”的拷贝操作,对于引用来说,是绑定操作。
引用不可变,也没得变:因为“赋值运算符=”的赋值功能,对于引用来说,是对被引对象的赋值

通过下面的代码来理解:与指针对比一下,能更好的理解上面的两句话
/*	指针部分	*/
		int i=1,j=2;
		int* ptri=&i;//ptri是一个指针
	/*对指针的赋值是真的赋值*/
		ptri=0;//将指针ptri修改为了空指针(这里只是测试直观性需要,平常最好用nullptr)
		ptri=&j;//将指针的值修改为了j的地址
	/*对指针的拷贝是真的拷贝*/
		int* ptrj=ptri;//将ptri中存放的j的地址拷贝给ptrj

/*	引用部分	*/
		int m=1;
		int& ptrm=m;//ptrm是一个引用
	/*对引用的赋值是 给被引对象赋值*/
		ptrm=0;//将m的值修改为1
	/*对指针的拷贝是 绑定*/
		int& ptrmm=ptrm;//按照一般类型,该运算应该是将ptrm中的值拷贝给ptrmm。但是对引用而言,该运算是将ptrmm绑定到m上(也可能是绑定到ptrm上,求大神指点)

2. 数组的引用

数组的引用怎么写?
数组的引用,其中的元素是引用类型吗?(对string、vector等也是一样)

  • 2.1 数组的引用怎么写?

    看下面的代码吧

    int array[]={1,2,3};//array是int数组,维度为3
    int (&array_ref)[3]=array;//array_ref的数据类型是“数组的引用”
    第一个问题:外面的括号能省么?————不能;省了就是“引用的数组”了
    第二个问题:数组大小3能省么?————不能;维度是“数组的引用”类型的一部分
    

    数组指针也与之类似P205,(不过平常用的都是指向数组首元素的指针P105)(数组与指针、引用的爱恨纠缠得再开一篇记录一下)

    /*数组指针*/
    	int array[] = { 1,2,3 };
    	int (*p)[3] = &array;//这里的指针指向的是一个长度为3的数组,对p解引用得到的是一个长度为3的数组
    	cout << (*p)[0] << (*p)[1] << (*p)[2]  << endl;//****注意数组指针p的使用方式****
    	//int (*p)[5] = &array;//error:"int (*)[3]"类型的值不能用于初始化"int (*)[5]"类型的实体  
    
    /*平常用的指向数组首元素的指针*/
    	int array[] = { 1,2,3 };
    	int* q = array;
    	cout << q[0] << q[1] << q[2] << endl;
    
  • 2.2 数组的引用中的元素是引用类型吗?

    这得看思考的角度:我的思考是这样的:

    • 若一对象为“数组的引用”类型,比如上面的array_ref,则可以通过该对象中的元素 访问修改原数组中的元素。从这个角度看,数组的引用类型中的元素 是 引用类型(是对原数组中对应元素的引用)。
    • 也只是这么理解吧,实际中咋样我也不懂。。可能数组的引用里面就没有元素,对数组的引用取元素,就是取原数组中的元素。两种思考方式都是一个效果(求大神讲解)
       

    如下代码:

    	string s("a value");
    	string& str = s;
    	cout << s[0] << "  "
    		 << str[0] <<
    		 << endl; //输出结果 a a
    	char& c = s[0];//c是对s[0]的引用
    	char& cc = str[0];//cc是对s[0]的引用
    	char ccc = str[0];
    	auto accc = srt[0];//注:这里是因为auto并不会将 引用类型的对象初始化的变量 识别为引用类型;P61
    	
    	str[0] = 'A'; cout << s << endl;//输出结果 A value
    	s[0] = 'B'; cout << s << endl;//输出结果 B value
    	c = 'C'; cout << s << endl;//输出结果 C value
    	cc = 'D'; cout << s << endl;//输出结果 D value
    	ccc = 'E'; cout << s << endl;//输出结果 D value
    	accc = 'F'; cout << s << endl;//输出结果 D value
    

3. 范围for与引用

范围for的独特性——范围for语句中,访问对象中元素时,是不可对元素进行修改的,想修改得加引用 P83

看下面两段代码

	char arr[] = "123";
	for (int i = 0; i < 3; i++)
	{
		arr[i] = '4';
	}
	cout << arr << endl; //输出结果 444

	for (auto i : arr) {
		i = 'G';//不报错,但对数组无效果;对string亦是如此
		cout << i << endl;//输出结果 G
	}
	cout << arr << endl; //输出结果 444
	int intarr[] = { 1,2,3 };
	for (int i = 0; i < 3; i++)
	{
		intarr[i] = 4;
	}
	cout << intarr[0] << intarr[1] << intarr[2] << endl; //输出结果 444

	for (auto i : intarr) {
		i = 5;//不报错,但对数组无效果;
		cout << i << endl;//输出结果 5
	}
	cout << intarr[0] << intarr[1] << intarr[2] << endl;//输出结果 444

可以看出来,范围for语句中,是无法对原序列中元素的值进行修改的,因为范围for语句中变量i只是对序列中元素的拷贝。
范围for语句中想要修改原序列中元素的值,则需要使用 引用。
看代码

	char arr[] = "123";
	for (auto& i : arr) {
		i = 'G';
		cout << i << endl;//输出结果 G
	}
	cout << arr << endl; //输出结果 GGG

4. 函数 引用传参 与 返回引用

记住两句话:
1. 形参的初始化机理 与 变量的初始化 完全相同 P187
2. 函数返回的临时量的初始化机理 与 变量的初始化 完全相同 P201
现在就很好理解函数中涉及的引用了——引用传参 与 返回引用

  • 4.1 函数引用传参

    看代码

    void func(int& k);
    int i=1;
    int& refi1=i;
    int& refi2=refi1;
    //下面三种都正确
    func(i);//这里的传参过程与refi1的初始化 异曲同工
    func(refi1);//这里的传参过程与refi2的初始化 异曲同工
    func(refi2);
    
    void func(int (&array_ref)[10]);
    int array[10]={0};
    int (&refarr1)[10]=array;
    int (&refarr2)[10]=refarr1;
    //下面三种都正确
    func(array);//这里的传参过程与refarr1的初始化 异曲同工
    func(refarr1);//这里的传参过程与refarr2的初始化 异曲同工
    func(refarr2);
    
  • 4.2 函数返回引用

    返回引用类型:则函数调用是左值 。 其余返回类型得到的是右值。

    看代码

    char& get_val(string& str, string::size_type ix) {//此函数的返回值的初始化,类似于1.中ptrmm的初始化
    	return str[ix];//不管这是啥类型,函数规定的返回类型是引用,故返回左值
    }
    char get_val2(string& str, string::size_type ix) {//此函数的返回值的初始化,类似于2.2中ccc的初始化
    	return str[ix];//不管这是啥类型,函数规定的返回类型是非引用,故返回右值
    }
    
    void main(){
    	string s("a value");
    	get_val(s, 0) = 'E'; cout << s << endl;//输出结果 E value
    	//get_val2(s, 0) = 'F'; cout << s << endl;//error: 表达式必须是可修改的左值
    }
    

    注意:不要返回局部对象的引用或指针 (虽然有些编译器可以返回局部对象的指针或引用,但仅仅是能返回而已,返回的指针并无法正常使用,这位大佬讲的很清晰。测试代码如下)

int* func() {
	int p[] = { 5,6 };
	return p;
}

void main() {
	int a[] = { 1,2,3 };
	int* q = func();
	cout << q[0]<<q[1] << endl;//输出正常:56
	int* gyh = a;
	cout << q[0] << q[1] << endl;//这里输出就不正常了,是一个未知的值
}

----------书山有路勤为径---------
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值