惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
CERT Recently Published Vulnerability Notes
U
Unit 42
T
The Blog of Author Tim Ferriss
H
Hackread – Cybersecurity News, Data Breaches, AI and More
B
Blog RSS Feed
Microsoft Azure Blog
Microsoft Azure Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Securelist
L
Lohrmann on Cybersecurity
Blog — PlanetScale
Blog — PlanetScale
Recorded Future
Recorded Future
D
DataBreaches.Net
Spread Privacy
Spread Privacy
T
Threat Research - Cisco Blogs
I
Intezer
P
Palo Alto Networks Blog
Simon Willison's Weblog
Simon Willison's Weblog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
I
InfoQ
宝玉的分享
宝玉的分享
Security Latest
Security Latest
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
Threatpost
Cisco Talos Blog
Cisco Talos Blog
P
Proofpoint News Feed
博客园 - 司徒正美
H
Hacker News: Front Page
Y
Y Combinator Blog
爱范儿
爱范儿
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
NISL@THU
NISL@THU
月光博客
月光博客
有赞技术团队
有赞技术团队
Cloudbric
Cloudbric
酷 壳 – CoolShell
酷 壳 – CoolShell
G
Google Developers Blog
A
Arctic Wolf
博客园 - 【当耐特】
W
WeLiveSecurity
V
Visual Studio Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
V
V2EX
C
Cyber Attacks, Cyber Crime and Cyber Security
S
SegmentFault 最新的问题
The GitHub Blog
The GitHub Blog
The Cloudflare Blog
Stack Overflow Blog
Stack Overflow Blog

博客园 - 水言木

好久没来了,发个招聘贴 RavenDB Tips: 巧妙设计文档的Id格式以提升查询性能 CQRS实践(4): 领域事件 CQRS实践(3): Command执行结果的返回 CQRS实践(2): Command的实现 CQRS实践(1): 什么是CQRS 准备重新开始写点东西 不要让编写set访问器成为习惯 [PoEAA] Learning Notes of Data Transfer Object 使用UrlRewriter进行Url重写的完整解决方案 迅速搭建SVN环境 小议GridView中的删除按钮 工厂的那些事儿 关于Ajax的只言片语[2008.7.31最后更新] 重温:从aspx到dll 小记一次让我误会的引用传参 关于ASP.NET的只言片语[2008.8.12最后更新] .ctor,.cctor 以及 对象的构造过程 汶川地震小感
再谈“引用”
水言木 · 2008-08-16 · via 博客园 - 水言木

  晚上又把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来收集的垃圾了。

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

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