再谈“引用”
晚上又把Netbeans6.1给装上了,不过不是用来做java的,是做C++的,唉,无奈啊,谁叫上学期给挂了呢。想想自己其实也挺酷,一张C++的卷子,全用C#来解答,在答到中间程序题时,不知咋的,叛逆心突然膨胀,觉得在纸张上写程序很无聊,就写了几个“同上”,“… ...”,结果就……,不过老师其实还不错,后来问他时,他说最后一题给了我满分(只有这题有写完整),这说明他还是没有过分计较语言的。
话说回来,其实C++也不是那么可恶,虽然C#比它elegant了许多。复习起C++,还是比较容易接受的,不过有些地方不说还真是不注意,正好看到了C++中的引用,就随便拉出word扯两句。
在C#中,引用是指内存的地址,比如新建一个对象Person p = new Person();,那么p所保存的,就是Person实例(在托管堆上分配内存)的地址,而p本身,是栈上的一个变量。
那在C语言中呢,有指针的概念,在C++中又有引用的概念,感觉C++中的引用和C#中的引用还算是基本一样的意思,但在C++的书中往往解释很多,因为指针的存在。那指针变量和引用的区别在哪呢。
首先,指针不是指针变量,指针是内存地址,而指针变量,是一个储存着这个地址的变量,就像前面C#中的Person p = new Person(),这个p其实就可以称为一个指针变量,它首先是一个变量,然后它保存的值是一个内存地址(当然,C#中不会这么称呼它的)。
而C++中的引用,其实是变量的“别名”,比如:int a = 10; 那么,a是在栈上开辟的一块内存空间,存储着10,而a的引用(别名)所存储的内容,也就是a所在的内存空间的上整型值10,可以把引用用“别名”来代替,再用“别名”的字面意思来理解,其实就比较好理解了。a的别名,则可认为除了名字和a不一样外,其它完全一样,地址,存储内容,都一样。
a的别名(引用)是这样定义的:
int &b = a; //定义a的引用b
从函数传参可以更深刻的理解它,看:
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
当调用Swap (c, d)时,有一个copy的过程,在Swap函数体中的a和b,仅仅只是变量c和d的副本,它们存储的内容相同,但所在的内存空间并不一样。
再看:
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/dot.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
6
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
当调用Swap (&c, &d)时,一样,还是有一个copy的过程,但和上面的又不一样,上面copy的是变量,这里copy的是指针变量(存储内存地址),也就是说,在调用时,它会创建两个指针变量,然后把c和d的地址分别赋给它们,而在Swap函数体中执行时,就可以对这两个指针变量进行操作,也就是说,我们可以从这两个指针变量得到实参的地址,那么,改变实参的值当然没有问题。但是有一点是需要注意的,这里“创建了两个指针变量”。
再看:
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/dot.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
这里用上了别名,Swap函数接受两个参数,它们是变量的引用(别名),当调用Swap (c, d)时,就不再有copy的过程了,在Swap函数体中,直接操纵着c和d,因为传递的是它们的别名,这就好比水言木是我的网名(别名),别人摔了水言木一巴掌,那就是实实在在地摔了我一巴掌。
说到这里,就转过来看看C#中的ref关键字吧,看:
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/dot.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
6
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
其实ref的作用和上面C++中的引用(别名)是一样的,当用上了ref时,就不再有传参时的参数拷贝过程了,而如果没用ref,那在Swap函数体中的a和b,其实就是c和d的副本。
而C#中,是没有指针这东东的,那它有没有类似C++中传递指针的情况呢?有。看下面:
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/dot.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
person是类的实例名(对象名),它所存储的,就是实例在托管堆中的地址,如上面所说,我们可以认为它是C++中的指针变量,那么,当调用Swap (person)时,就有一个copy的过程:在栈上开辟一块空间,存储person的值(值是Person实例的地址),然后在Swap函数体中操作的p,其实是栈上的另一块空间,因为它和实参person所存储的内容一样,都是实例的地址,所以,在函数中对p属性的赋值可以改变实参person相应的属性值,但是,如果在Swap中有一句p = new Person(),那么,之后的p,则是指向了另一块堆上的空间(新Person实例的地址)。
而如果用的是ref:
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/dot.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
那么,传参时就没有了参数copy的过程,于是,若在Swap中进行p = new Person(),那也是会使实参的值(地址)改变,那原先的Person实例,就会成为一个等待GC来收集的垃圾了。
洋洋洒洒写了不少字,到此结束。
相关文章:小记一次让我误会的引用传参