Language agnostic 按引用传递还是按值传递?
学习一种新的编程语言时,可能遇到的一个障碍是,默认情况下,该语言是按值传递还是按引用传递 这是我用你们最喜欢的语言问你们的问题,到底是怎么做到的?可能的陷阱是什么Language agnostic 按引用传递还是按值传递?,language-agnostic,oop,parameters,pass-by-reference,pass-by-value,Language Agnostic,Oop,Parameters,Pass By Reference,Pass By Value,学习一种新的编程语言时,可能遇到的一个障碍是,默认情况下,该语言是按值传递还是按引用传递 这是我用你们最喜欢的语言问你们的问题,到底是怎么做到的?可能的陷阱是什么 当然,你最喜欢的语言可以是你玩过的任何东西: 首先是一些代码: public void swap(int x, int y) { int tmp = x; x = y; y = tmp; } 调用此方法将导致以下结果: int pi = 3; int everything = 42; swap(pi, everythi
当然,你最喜欢的语言可以是你玩过的任何东西: 首先是一些代码:
public void swap(int x, int y)
{
int tmp = x;
x = y;
y = tmp;
}
调用此方法将导致以下结果:
int pi = 3;
int everything = 42;
swap(pi, everything);
System.out.println("pi: " + pi);
System.out.println("everything: " + everything);
"Output:
pi: 3
everything: 42"
即使使用“真实”对象也会显示类似的结果:
public class MyObj {
private String msg;
private int number;
//getters and setters
public String getMsg() {
return this.msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getNumber() {
return this.number;
}
public void setNumber(int number) {
this.number = number;
}
//constructor
public MyObj(String msg, int number) {
setMsg(msg);
setNumber(number);
}
}
public static void swap(MyObj x, MyObj y)
{
MyObj tmp = x;
x = y;
y = tmp;
}
public static void main(String args[]) {
MyObj x = new MyObj("Hello world", 1);
MyObj y = new MyObj("Goodbye Cruel World", -1);
swap(x, y);
System.out.println(x.getMsg() + " -- "+ x.getNumber());
System.out.println(y.getMsg() + " -- "+ y.getNumber());
}
"Output:
Hello world -- 1
Goodbye Cruel World -- -1"
因此,很明显,Java通过值传递其参数,因为pi和所有对象以及MyObj对象的值不会交换。
请注意,“按值”是java中向方法传递参数的唯一方法。(例如,C++之类的语言允许开发人员在参数类型之后使用‘<强>和<强>引用来传递参数。
现在是棘手的部分,或者至少是会让大多数新java开发人员感到困惑的部分:(借用)原作者:托尼·辛茨
public void tricky(Point arg1, Point arg2)
{
arg1.x = 100;
arg1.y = 100;
Point temp = arg1;
arg1 = arg2;
arg2 = temp;
}
public static void main(String [] args)
{
Point pnt1 = new Point(0,0);
Point pnt2 = new Point(0,0);
System.out.println("X: " + pnt1.x + " Y: " +pnt1.y);
System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);
System.out.println(" ");
tricky(pnt1,pnt2);
System.out.println("X: " + pnt1.x + " Y:" + pnt1.y);
System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);
}
"Output
X: 0 Y: 0
X: 0 Y: 0
X: 100 Y: 100
X: 0 Y: 0"
成功更改pnt1的值!
这意味着对象是通过引用传递的,事实并非如此!
正确的语句应该是:对象引用按值传递。
更多来自Tony Sintes:
该方法成功地改变了
pnt1的值,即使是
按值传递;然而,交换
pnt1和pnt2失败!这是少校
混乱的根源。大体上
方法,pnt1和pnt2仅此而已
而不是对象引用。当你经过的时候
pnt1和pnt2到tricky()方法,
Java按值传递引用
就像任何其他参数一样。这
是指传递给
方法实际上是
原始参考资料。下图1
显示两个指向
Java通过
方法的对象
(来源:) 结论或长话短说:
- Java通过值传递它的参数
- 在java中,“按值”是将参数传递给方法的唯一方法
- 使用作为参数给定的对象中的方法将改变对象,因为引用指向原始对象。(如果该方法本身改变了某些值)
- 由于系统必须复制参数,因此速度比通过引用慢
- 仅用于输入
- 速度更快,因为只传递一个指针
- 用于输入和输出
- 如果与全局变量一起使用,可能会非常危险
- 别忘了还有按名称传递,以及按值传递结果
“按值传递”结果与“按值传递”结果类似,增加了一个方面,即在作为参数传递的原始变量中设置值。在某种程度上,它可以避免对全局变量的干扰。显然在分区内存中更好,在分区内存中,通过引用传递可能会导致页面错误()
“按名称传递”表示仅在实际使用时计算值,而不是在过程开始时计算值。Algol使用pass-by-name,但一个有趣的副作用是编写交换过程()非常困难。此外,通过名称传递的表达式在每次访问时都会重新计算,这也可能有副作用 c#通过值传递其参数(默认情况下) 因此,调用此版本的swap将不会产生任何结果:
string x = "foo";
string y = "bar";
swap(x, y);
"output:
x: foo
y: bar"
但是,c#确实给开发人员提供了通过引用传递参数的机会,这是通过在参数类型之前使用“ref”关键字来实现的:
private void swap(ref string a, ref string b) {
string tmp = a;
a = b;
b = tmp;
}
string x = "foo";
string y = "bar";
swap(x, y);
"output:
x: bar
y: foo"
此交换将更改引用参数的值:
private void swap(ref string a, ref string b) {
string tmp = a;
a = b;
b = tmp;
}
string x = "foo";
string y = "bar";
swap(x, y);
"output:
x: bar
y: foo"
c#还有一个out关键字,ref和out之间的区别很微妙。
采用
输出参数不需要
分配给作为
调用前输出参数;
但是,被调用方必须
在指定之前,先指定out参数
返回
及
相反,参考参数是
最初被认为是由
卡利。因此,被调用方不是
需要分配给ref
参数,然后再使用。参考参数
都被传递到一个
方法
一个小陷阱是,与java一样,通过值传递的对象仍然可以使用其内部方法进行更改
结论:
- 默认情况下,c通过值传递其参数
- 但在需要时,也可以使用ref关键字通过引用传递参数
- 值传递的参数的内部方法将改变对象(如果该方法本身改变某些值)
- 有一个针对.NET的
很多人对引用对象实际上是按值传递(在C#和Java中)感到惊讶。它是堆栈地址的副本。这可以防止方法更改对象实际指向的位置,但仍然允许方法更改对象的值。在C#中,可以通过引用传递引用,这意味着您可以更改实际对象指向的位置。PHP也是通过值传递的
<?php
class Holder {
private $value;
public function __construct($value) {
$this->value = $value;
}
public function getValue() {
return $this->value;
}
}
function swap($x, $y) {
$tmp = $x;
$x = $y;
$y = $tmp;
}
$a = new Holder('a');
$b = new Holder('b');
swap($a, $b);
echo $a->getValue() . ", " . $b->getValue() . "\n";
然而,在PHP4中,对象被视为。这意味着:
<?php
$myData = new Holder('this should be replaced');
function replaceWithGreeting($holder) {
$myData->setValue('hello');
}
replaceWithGreeting($myData);
echo $myData->getValue(); // Prints out "this should be replaced"
Python使用pass-by-value,但由于所有这些值都是对象引用,因此净效果类似于pass-by-r
>>> def do_something(a, b):
... a = "Red"
... b.append("Blue")
...
>>> a = "Yellow"
>>> b = ["Black", "Burgundy"]
>>> do_something(a, b)
>>> print a, b
Yellow ['Black', 'Burgundy', 'Blue']
>>> a = ["Purple", "Violet"]
>>> do_something(a, b)
>>> print a, b
['Purple', 'Violet'] ['Black', 'Burgundy', 'Blue', 'Blue']
>>> a = "Yellow"
>>> b = ("Black", "Burgundy")
>>> do_something(a, b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in do_something
AttributeError: 'tuple' object has no attribute 'append'
spaceused=: [: 7!:5 <
exectime =: 6!:2
big_chunk_of_data =. i. 1000 1000 100
passbyvalue =: 3 : 0
$ y
''
)
locale =. cocreate''
big_chunk_of_data__locale =. big_chunk_of_data
passbyreference =: 3 : 0
l =. y
$ big_chunk_of_data__l
''
)
exectime 'passbyvalue big_chunk_of_data'
0.00205586720663967
exectime 'passbyreference locale'
8.57957102144893e_6
void swap(int *x, int *y); //< Declared as pass-by-reference.
void swap(int x, int y); //< Declared as pass-by-value (and probably doesn't do anything useful.)
float *FtoC(float temp)
{
float c;
c = (temp-32)*9/5;
return &c;
}
float *FtoC(float *temp)
{
*temp = (*temp-32)*9/5;
return temp;
}
sub incr {
my ( $x ) = @_;
$x++;
}
my $value = 1;
incr($value);
say "Value is now $value";
sub incr {
$_[0]++;
}
my $value = 1;
incr($value);
say "Value is now $value";
sub incr_ref {
my ( $ref ) = @_;
$$ref++;
}
my $value = 1;
incr(\$value);
say "Value is now $value";