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;//这里输出就不正常了,是一个未知的值
}