C++的数组元素为什么不能是引用类型

来源:互联网 发布:无线传感器网络三要素 编辑:程序博客网 时间:2024/05/22 08:22

感谢原作者分享:http://blog.xinspace.space/2015/01/25/cpp-array-element-not-ref/


这几天在看c++基础内容,看到数组的时候,书里提到数组元素的约束条件:

1.元素类型支持赋值操作。

2.元素类型支持复制。

因此,除了引用类型对象和流对象外,其他的任意内置类型和满足上述约束的类类型均能成为数组的元素。

流对象不能成为数组元素是因为它不支持复制和赋值,不满足上述的两条约束。而引用却是可以赋值和复制呀,为什么数组元素不能是引用呢?

先讲一下引用的某些特点:

1.引用是对象的另一个名字(这里的对象指内存的区域,如int所占内存4个字节)。定义一个对象的引用,该引用并不占内存空间,它只是这个对象的别名。

举个例子:

int i = 10;该语句定义变量i为整型,占用4字节内存空间,初始值是10。粗略的说,这条语句在执行时操作系统从内存中分配4字节(假如地址是0x00000000-0x00000003),将这4个字节贴一个标签,叫i(若没有标签,想要读写这4字节就只能使用二进制或十六进制的地址了,比较麻烦)。之后,若程序读写i,就代表着读写这4个字节。所以i就是这4字节对象(这个对象指内存的特定区域,并不是面向对象中的对象)的一个名字或标签。对i赋值,就会把值写入到这4个字节的对象中。通过i可以方便的访问内存中的对象。i本身不占用内存空间,i代表的对象占用4字节空间。

int &ir = i;该语句定义引用ir,将其绑定到i所代表的对象。所以,ir就像i一样是一个名字、标号,ir代表的对象与i一样而已,就是0x00000000-0x00000003这4个字节。

那么定义一个变量和定义一个引用有什么区别吗?可以这么理解,定义变量时操作系统帮我们分配的内存空间(对象)、把变量名作为该对象的标签绑定到该对象上。而定义引用时操作系统只帮我们产生了一个标签,但是不会产生对象,当然也不会将这个标签绑定到某个对象上。因此,将引用这个标签绑定到对象上的工作需要程序员来做。

2.引用必须绑定到相应的对象。引用在定义时必须初始化,绑定到相应的对象,且之后不能更改。

从第1个特点来看,程序员必须手动将引用绑定到某个对象上。这就要求在定义引用时就必须给他提供初始化式,这个初始化式代表了某个对象(即内存中的某片区域)。如果程序员不这么做,那么这个标签的存在就没有意义,毕竟不能读写一个标签(即不能读写标签所代表的内存空间)在程序中没有任何意义,因此编译时便会报错。

而且引用在绑定到一个对象之后,不能够再改变。这很好理解:如果标签Q能够绑定到不同的对象的话,那么程序中不同位置出现的Q到底代表了哪个对象?是0x00000000-0x00000003(4字节,如int型)还是0x00000011-0x00000018(8字节,如double型)?因此,只要这个对象生命周期还未结束(在同一个作用域中),这个标签就一直贴在它身上,不能再用于其他对象。当然,不同的作用域可以出现重复的标签,这另当别论。

再说一下为什么数组的元素不能为引用:

1.大数组初始化麻烦。引用在定义时必须初始化。若数组元素是引用的话,则必须对每个元素进行初始化。比如int a[100000000]这样的大数组,一一赋值很麻烦。就算你可以一一初始化每个元素,那么往下看。

2.破坏数组元素的内存存放连续性。数组的一大优点就是可以随机快速访问某个元素,这是因为数组不仅在逻辑上连续,在物理上也连续。如对数组int a[10],给其第4个元素赋值可以写为:*(a+3) = 15;。能这么写是因为数组元素在内存中是连续存放的,如果数组a的首地址为0x00000000,那么a[0]的地址是0x00000000,a[1]的地址是0x00000004,a[2]的是0x00000008,以此类推。a中每个元素都占用4个字节空间,每个元素在逻辑上是连续的,即第2个元素紧接着第1个元素;同样,数组在物理内存中的存放也是连续的,即第2个元素的地址紧挨着第1个元素。

那么,数组元素为引用会是什么情况?上面引用第1个特点中提到,引用只是标签,需要手动绑定到某个对象(地址区域)才行。那么在给数组中的引用初始化时,不能保证逻辑上连续的引用元素所绑定的对象也正好物理上连续。

举个例子:

假如引用数组是合法的,编译可以通过,那么根据语法来说,定义语句应该是这样的:int &a[4],a是一个数组,有4个元素,每个元素都是引用。当然,我们还需要对每个元素进行初始化,所以写全的话是这样:

//首先定义4个对象,内存地址、标签和初值如下:

int i1 = 10; //内存地址是0x00000000-0x00000003,初值是10

int i2 = 11;//内存地址是0x00000014-0x00000017,初值是11

int i3 = 12;//内存地址是0x0000000c-0x0000000f,初值是12

int i4 = 13;//内存地址是0x00000020-0x00000023,初值是13

int &a[4] = {i1, i2, i3, i4};//a的4个元素分别绑定到4个对象。

这样看来,既然a是数组,那么逻辑上第1个元素紧挨着第2个元素,物理上也应该如此。但是这个例子告诉我们,a数组的元素在物理上并不连续,a[0]的地址是i1的地址,a[1]的地址是i2的地址,以此类推。甚至a[2]的地址比a[1]的地址还要靠前(本应该靠后才对)。

正是由于引用只是别名,不占用内存空间,才导致了上述的这个问题,使得数组在物理上不连续了。这样的话类似*(a+3)=15;的语句就行不通了。因为a+3是这样计算的:a的地址+3×元素所占字节数。这么计算的根基就是必须保证物理连续,否则无法通过数组首地址推出其余元素的地址。

3.操作系统无法为引用数组分配空间。对象是要占用内存空间的,如int a[10];这条定义语句定义了一个有10个整型元素的数组,因此操作系统在运行时从内存划出40字节留给数组a,然后访问a就会访问这40字节的地址。但是引用是不占用内存的,它只是名字,所以如果数组元素是引用的话,如int &ar[10]={….};,这个数组到底占用多少内存?不知道!因为引用不占用空间,操作系统无法给数组分配内存空间,也就无法读写这个数组。无法读写的数组还有意义吗?

综上所述,数组的元素不能是引用。


阅读全文
0 0
原创粉丝点击