再谈“引用”

  晚上又把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 a = 10
int &= a; //定义a的引用b

 

从函数传参可以更深刻的理解它,看: 

 

1void Swap(int a , int b);
2//
3int c = 10;
4int d = 12;
5Swap(c, d);

 

当调用Swap (c, d)时,有一个copy的过程,在Swap函数体中的a和b,仅仅只是变量c和d的副本,它们存储的内容相同,但所在的内存空间并不一样。

 

  再看:

1void Swap (int *a, int *b);
2//
3int c = 10;
4int d = 12;
5Swap(&c, &d);
6

 

  当调用Swap (&c, &d)时,一样,还是有一个copy的过程,但和上面的又不一样,上面copy的是变量,这里copy的是指针变量(存储内存地址),也就是说,在调用时,它会创建两个指针变量,然后把c和d的地址分别赋给它们,而在Swap函数体中执行时,就可以对这两个指针变量进行操作,也就是说,我们可以从这两个指针变量得到实参的地址,那么,改变实参的值当然没有问题。但是有一点是需要注意的,这里“创建了两个指针变量”。 

 

  再看:

1Swap (int &a, int &b);
2//
3int c = 10;
4int d = 12;
5Swap(c, d);

 

  这里用上了别名,Swap函数接受两个参数,它们是变量的引用(别名),当调用Swap (c, d)时,就不再有copy的过程了,在Swap函数体中,直接操纵着c和d,因为传递的是它们的别名,这就好比水言木是我的网名(别名),别人摔了水言木一巴掌,那就是实实在在地摔了我一巴掌。

 

  说到这里,就转过来看看C#中的ref关键字吧,看:

 

1int Swap(ref int a, ref int b) { … }
2//
3int c = 10;
4int d = 12;
5Swap(ref c, ref d);
6

 

  其实ref的作用和上面C++中的引用(别名)是一样的,当用上了ref时,就不再有传参时的参数拷贝过程了,而如果没用ref,那在Swap函数体中的a和b,其实就是c和d的副本。

 

  而C#中,是没有指针这东东的,那它有没有类似C++中传递指针的情况呢?有。看下面:

 

1Swap(Person p) { … }     //为了少打点字,这里就不考虑Swap函数的语义了
2//
3Person person = new Person();
4Swap(person);

 

  person是类的实例名(对象名),它所存储的,就是实例在托管堆中的地址,如上面所说,我们可以认为它是C++中的指针变量,那么,当调用Swap (person)时,就有一个copy的过程:在栈上开辟一块空间,存储person的值(值是Person实例的地址),然后在Swap函数体中操作的p,其实是栈上的另一块空间,因为它和实参person所存储的内容一样,都是实例的地址,所以,在函数中对p属性的赋值可以改变实参person相应的属性值,但是,如果在Swap中有一句p = new Person(),那么,之后的p,则是指向了另一块堆上的空间(新Person实例的地址)。

 

   而如果用的是ref:

 

1Swap(ref Person p) {…}
2//
3Person person = new Person();
4Swap(ref person);
5

 

那么,传参时就没有了参数copy的过程,于是,若在Swap中进行p = new Person(),那也是会使实参的值(地址)改变,那原先的Person实例,就会成为一个等待GC来收集的垃圾了。

 

洋洋洒洒写了不少字,到此结束。

 

相关文章:小记一次让我误会的引用传参

 

posted @ 2008-08-16 23:10  水言木  阅读(335)  评论(0编辑  收藏  举报