您的位置:首页 > 编程语言 > C语言/C++

【基础知识】C++与Java的参数传递

2010-06-29 01:01 253 查看
今天我试着思考一下【参数传递】。

[b]理论基础[/b]

严格上讲,参数传递只有一种方式——值传递(by value)。那么广为流传的参数的两种传递方式:值传递(by value)和地址传递(by address)是怎么一回事呢?

这个问题或许连笨蛋都可以回答,因为仅仅从文字的语义上就可以找到答案,那就是:地址也是一种值。

回答如此简单,简单到足以让人们误以为理解其含义,止步于更深度的探求。懂得思考的人没有放弃继续追根到底的机会:什么是值?什么是地址?既然地址就是值,那么为什么存在值传递与地址传递的对立?

(未完待续,吃饭)

(吃饭回来后,又过了两天...)

什么是值?这个问题或许可以从盘古开天辟地说起,值就是数据,这个空间到处都存在数据。你问什么是数据?额,数据就是数据。但并不是所有的数据都是值。数据就其本身而言不能表示任何事物,是没有任何实际意义的。把‘计算机能够理解’定义为‘计算机可读’,那么值是有‘实际意义’的数据,是计算机可读的,而数据本身是计算机不可读的。因为值是编程语言能够表示的数据,是和类型相关的。比如int类型,struct类型,enum类型等,因此值是类型的值。类型有简单与复杂之分,值也有简单与复杂之别。(经过这番思考,我终于理解了一个概念。以前老是搞不清楚什么叫做‘计算机可读’,原来如果数据有了类型之后就是‘计算机可读’,数据有了类型之后就变成了‘值’,‘值’是计算机可读的。)

什么是地址?地址是相对于计算机内存而言的。计算机内存是一个存储‘数据’的地方。所以,内存和数据具有等价的概念。由于内存存储的是二进制的数据,因此内存最小的组成单位是bit,8个bit为一个最小单元,称为一个字节(byte)。为了便于组织数据,给每个字节赋予一个唯一的地址值,第一个字节为0,第二个字节为1...第n个字节为n。但是即使给每个字节都编了地址,数据依然是杂乱无章,没有任何实际意义。‘数据’通过‘类型’转化为有意义的‘值’。‘类型’好比‘模具’,凡是被框住的‘数据’就加工成了计算机可读的‘值’。值有大小之分,小到一个字节,大到有多个字节。对于有多个字节的值,存在多个地址值,我们规定首字节的地址为‘值’的地址值。

既然地址就是值,那么为什么存在值传递与地址传递的对立? 根本原因在于‘传递’二字,因为值是有大小的,有简单和复杂之分。复杂值的传递肯定比简单值的传递花费更多的代价。地址值顶多是个unsinged int类型的简单值。因此对于复杂类型而言,答案不言而喻。

[b]语言实现[/b]

类型有简单和复杂之分,类型的值也有简单和复杂之别。但是由于两种‘传递’方式的对立,最终衍生出三种类型。

简单类型(build-in): 值传递。

复杂类型(user-defined)有两种: 值类型和引用类型。值类型为值传递,引用类型为地址传递。

对于简单类型,各大主流编程语言处理上是一致的。对于复杂类型(class or struct),C++默认情况下是值类型,Java默认情况下是引用类型,C#两者兼具,struct为值类型,class为引用类型。

C++参数传递

在C++中,默认情况下是以by value方式传递对象到函数,同样也是以by value的方式从函数返回对象(这种方式是从C语言继承来的)。因此,无论是传入或是传出,都要复制并生成原对象(实参)的副本(形参),对副本(形参)的任何更改不会改变原对象(实参)的状态。

对于build-in类型,by value方式仅仅是复制变量的值,由于内置类型存储的数据比较简单,因此复制操作也不会带来大的性能问题。

对于user-defined类型,by value方式会调用对象的copy constructor。当copy constructor比较复杂时,在参数传递过程中就会引起一系列的构造与析构行为,造成性能的下降。为了提高性能,必须避免在参数传递时copy构造函数的调用。这时,按地址传递是比较好的选择。上文提到by value方式操作的仅仅是对象的副本,对副本的任何更改不会改变原有对象的状态。因此,从原对象(实参等)的角度来看,by value方式像一种只读行为,因为by value方式不会改变原对象的状态。因此为了更好的模拟by value的方式,在用地址传递代替值传递时,最好加上const关键字。

C++中有两种按地址传递的方式:pass-by-reference-to-const和pass-by-pointer-to-const。

pass-by-reference-to-const:

pass-by-pointer-to-const:[/b]

void getAnimalName(const Animal* ani){
cout << ani->getName() << endl;
}


上述两种传递方式在汇编级别和二进制级别的代码上是相同的。

待续。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: