C++书上讲到容器的时候,说道引用不支持一般意义的赋值运算,如何理解?另外,为什么IO库类不支持赋值和复制?
解决方案:10分
所谓“引用不支持一般意义的赋值运算”应该是用指针的观点去理解引用的说法。
考虑如下代码:
int a, b; int &ri = a; //ri 引用a ri = b;//ri不会引用b,而是把b的值赋值给a int * pi = &a;//pi指向a * pi = b;//这句是给a赋值 pi = &b;//pi改为指向b
把引用看成特殊的指针,然后用指针的观点去待引用,才有这么一种说法,因为对引用的赋值与指针完全不同……
这样反而把问题弄复杂了。引用不但“不支持一般意义的赋值运算”,而且还不支持一般意义的取地址运算”呢……
事实上,如果不把引用与指针联系起来,那么引用的这些行为很正常。引用型变量就是给变量起一个别名,对它赋值当然就等同于对原变量赋值,对它取地址也理所应当地取到原变量的地址。引用型参数就是“变量参数”,让参数成为实参变量的别名,函数内部可以通过它来改变被引用的变量。
解决方案:10分
引用只有一次初始化的机会,其他时候,都是代表所引用的对象,对引用赋值就是对引用对象的赋值。
而不是对储存引用的这个变量的地址的内容赋值;
由于引用的别名性质,虽然实现上,引用也需要一个地址存放,但是概念上引用就是所引用的对象。
所以引用的地址就是所引用的对象的地址,引用的值就是引用的对象的值;
而储存引用的那个地址(代表引用这个变量)的内容和赋值一点关系也没有;
那里只是存储了引用对象的地址,是引用初始化以后就不会改变了的;
对程序员是透明的,在语法上,概念上,是不存在的,只是出于实现引用的需要,才会为引用分配内存的。
所以简单从语法上,是没有办法,得到引用这个变量的地址的。
而不是对储存引用的这个变量的地址的内容赋值;
由于引用的别名性质,虽然实现上,引用也需要一个地址存放,但是概念上引用就是所引用的对象。
所以引用的地址就是所引用的对象的地址,引用的值就是引用的对象的值;
而储存引用的那个地址(代表引用这个变量)的内容和赋值一点关系也没有;
那里只是存储了引用对象的地址,是引用初始化以后就不会改变了的;
对程序员是透明的,在语法上,概念上,是不存在的,只是出于实现引用的需要,才会为引用分配内存的。
所以简单从语法上,是没有办法,得到引用这个变量的地址的。
这个位置存储的数据,即所引用的对象地址,就是引用的地址,所以引用的值,就是所代表的变量的值。
所以存储引用这个变量的地址,就隐形了,所以引用赋值也就隐形了,被给所引用的对象赋值取代了。
解决方案:20分
int n = 10,m=11; //一般变量定义(非引用类型) int &ref = n; //C++引用定义,引用必须初始化,初始化后被引用的对象,就会永远保持不变。 //C++中,ref只能引用n,不会再改为引用别的变量,比如m; ref =m; //就是 n = m; 不是改为引用m; // 引用不支持一般意义的赋值运算,如何理解? //就是指的这种情况吧,把如果ref是指针,ref= &m;就会指向新对象了; //不过这种解释很晦涩,更不好理解了。 // //这里ref 没有更换引用的目标,还是引用的n,这个赋值操作和 n=m;效果一致 //其实就是 *(int *)&n =m;的意思;
引用的实现,就像下面的代码差不多;
//int n=10,m=11; //int &ref = n;<==> int *const pref = &n; //ref 是 n的别名,就是 n的另一个名字 //ref =m; <==> *pref =m; <==> n=m; //ref++; <==> (*pref)++; <==> n++; //m =ref; <==> m = *pref; <==> m = n; //ref +=m; <==> (*pref)+ m; //下面是 引用的实现和概念的区别。 // 实现上,就是个指针常量,指针的值是被引用变量的地址;概念上是别名,是另一个变量的代表。 //&ref<==> pref 实现上,保存了被引用变量的地址<==> &n; 概念上,因为是同一个对象,所以地址相同。 //ref<==> *pref 实现上,引用值就是地址中的值<==> n; 概念上,因为是同一个对象,所以值也相同。 //概念上,语法上,理解引用,以及引用初始化,赋值,取地址。 int n=10,m=11; int &ref =n; //ref:我就是n; n:我叫n,有时别人也叫我 ref,ref是我的外号(别名); ref = m;//ref:我就是n,给我赋值就是给n 赋值; n:ref 就是我呀,给ref赋值就是 给我赋值呀。 ref++;//ref:我就是n,我自增就是n自增; n:ref 就是我呀,ref自增就是我自增呀。 m =ref;//ref:我就是n,我的值赋值给m,就是n的值赋值给m; //n:ref 就是我呀,ref的值赋值给m,就是我的值赋值给m呀; int *p =&ref;<==> int *p=&n;//ref:我就是n,我的地址就是n的地址; //n:ref 就是我呀,ref的地址,就是我的地址;
PS:
这里出现了一个问题,引用 ref 这个变量的实现,本身也需要一个地址;
但是这个地址对于我们是透明的,不是可以用正常的语法手段获取的。
我们只能够获取,这个地址里面储存的数据,即被引用的对象的地址。
这也是C++刻意这样做的,同时也是必须这样做的。
对于C++的引用,用如下语句初始化后:
int &ref =n;
下面的恒等式,永远成立,直到其中一个结束生命周期为止:
&ref ==&n;
ref == n;
下面的例子,可以演示引用,取地址和取值,以及存储引用的实际地址之间的异同。
#include<iostream> using namespace std; struct ref_struct{ ref_struct(int &n):ref(n){};//有兴趣的话,也可以在这里输出. public: int &ref; } int main() { int n=10; ref_struct r(n); cout<<"存储引用的地址="<<&r<<",引用的地址=" << &r.ref<<",被引用对象的地址"&n<<endl; cout<<"引用的值="<<r.ref<<",被引用对象的值=" <<n<<endl; return 0; }