Java和C++中的参数传递
1概念定义
1.1按值传递:
将一个参数传递给一个函数时,函数接收的是原始值的一个副本。因此,如果函数修改了该参数,仅改变副本,而原始值保持不变。
1.2按引用传递:
将一个参数传递给一个函数时,函数接收的是原始值的内存地址,不是值的副本。因此,如果函数修改了该参数指向的内容,调用代码中的原始值也随之改变。
2 C++和Java应用程序中的参数传递
C++和Java中,当参数不是引用时,传递的都是该值的一个副本(按值传递)。
区别在于引用:
在C++中参数是引用时,您传递的就是这个引用,或者内存地址(按引用传递)。
在Java中参数是对象引用时,您传递的是该引用的一个副本(按值传递),而不是引用本身,但是调用方法的对象引用和副本都指向同一个对象。
总之,Java应用程序在传递不同类型的参数时,其作法与C++并无不同。Java应用程序按值传递所有参数,这样就制作所有参数的副本,而不管它们的类型。
3 实例解析
3.1代码一(C++版本)
#include<iostream></iostream>
using namespace std;
void modify(int a, int *p, int &r)
{
printf("in modify...\n");
a = 0;
*p = 7;
p = 0;
r = 0;
printf("a is %d\n", a);
printf("p is %d\n", p);
printf("r is %d\n", r);
}
int main ()
{
int val, ref;
int *pint;
val = 10;
ref = 50;
pint = (int*)malloc(sizeof(int));
*pint = 15;
printf("val is %d\n", val);
printf("pint is %d\n", pint);
printf("*pint is %d\n", *pint);
printf("ref is %d\n\n", ref);
printf("calling modify\n");
modify(val, pint, ref);
printf("returned from modify\n\n");
printf("val is %d\n", val);
printf("pint is %d\n", pint);
printf("*pint is %d\n", *pint);
printf("ref is %d\n", ref);
return 0;
}
程序运行结果:
val is 10
pint is 4262128
*pint is 15
ref is 50
calling modify
in modify...
a is 0
p is 0
r is 0
returned from modify
val is 10
pint is 4262128
*pint is 7
ref is 0
这段代码声明了三个变量:两个整型变量和一个指针变量。将所有三个变量作为参数传递给modify()。前两个参数是按值传递的,最后一个参数是按引用传递的。
Modify()的函数原型表明最后一个参数要作为引用传递。C++按值传递所有参数,引用除外;
Modify()更改了所有三个参数的值:
将第一个参数设置为0。
将第二个参数所指向的值设置为7,然后将第二个参数设置为0。
将第三个参数设置为0。
将第一个参数设置为0。
将第二个参数所指向的值设置为7,然后将第二个参数设置为0。
将第三个参数设置为0。
将新值打印出来,然后函数返回。
当执行返回到main时,再次打印出这三个参数的值以及指针所指向的值。
第一个和第二个参数由于按值传递,所以实参不受modify函数的影响。但指针所指向的值改变了。最后一个参数由于按引用传递,所以实参被modify函数改变了。
3.2代码二(Java版本)
class
Test
{
public
static
void
modify(
int
a, StringBuffer r1, StringBuffer r2)
{
System.
out
.println(
"in modify..."
);
a = 0;
r1 =
null
;
//1
r2.append(
" taste good"
);
System.
out
.println(
"a is "
+ a);
System.
out
.println(
"r1 is "
+ r1);
System.
out
.println(
"r2 is "
+ r2);
}
public
static
void
main(String args[])
{
int
val;
StringBuffer sb1, sb2;
val = 10;
sb1 =
new
StringBuffer(
"apples"
);
sb2 =
new
StringBuffer(
"pears"
);
System.
out
.println(
"val is "
+ val);
System.
out
.println(
"sb1 is "
+ sb1);
System.
out
.println(
"sb2 is "
+ sb2);
System.
out
.println();
System.
out
.println(
"calling modify"
);
//
按值传递所有参数
modify(val, sb1, sb2);
System.
out
.println(
"returned from modify"
);
System.
out
.println(
""
);
System.
out
.println(
"val is "
+ val);
System.
out
.println(
"sb1 is "
+ sb1);
System.
out
.println(
"sb2 is "
+ sb2);
}
}
运行结果:
val is 10
sb1 is apples
sb2 is pears
calling modify
in modify...
a is 0
r1 is null
r2 is pears taste good
returned from modify
val is 10
sb1 is apples
sb2 is pears taste good
代码声明了三个变量:一个整型变量和两个对象引用。将所有三个变量作为参数传递给modify()。
Modify()更改了所有三个参数的值:
将第一个参数(整数)设置为0。
将第一个对象引用r1设置为null。
保留第二个引用r2的值,但调用append()更改它所引用的对象
返回到main()时,整型的val没有改变。对象引用sb1也没有改变。
如果sb1是按引用传递的,正如许多人声称的那样,它将为null。但是,因为Java中按值传递所有参数,所以是将sb1的引用的一个副本传递给了modify()方法。
当modify()中将r1设置为null时,它只是对sb1的引用的一个副本进行了该操作,而不是像C++中那样对原始值进行操作。
第二个对象引用sb2打印出的是在modify()中设置的新字符串。是因为modify()中的引用副本和sb2指向同一个对象,所以对复制的引用所调用的方法更改的是同一个对象。
3.3经典的交换函数
3.3.1利用指针来实现
void swap(int *a, int *b)
{
int temp = *b;
*b = *a;
*a = temp;
}
int main (int argc, char** argv)
{
int val1, val2;
val1 = 10;
val2 = 50;
swap(&val1, &val2);
return 0;
}
3.3.2利用引用来实现
void swap(int &a, int &b)
{
int temp = b;
b = a;
a = temp;
}
int main (int argc, char** argv)
{
int val1, val2;
val1 = 10;
val2 = 50;
swap(val1, val2);
return 0;
}
3.3.3 Java利用引用来实现
class
Swap
{
public
static
void
swap(Integer a, Integer b)
{
Integer temp = a;
a = b;
b = temp;
}
public
static
void
main(String args[])
{
Integer a, b;
a =
new
Integer(10);
b =
new
Integer(50);
System.
out
.println(
"before swap..."
);
System.
out
.println(
"a is "
+ a);
System.
out
.println(
"b is "
+ b);
swap(a, b);
System.
out
.println(
"after swap..."
);
System.
out
.println(
"a is "
+ a);
System.
out
.println(
"b is "
+ b);
}
}
运行结果:
before swap...
a is 10
b is 50
after swap...
a is 10
b is 50
Java中的函数参数传值是按值传值!
3.4 Java版本的综合实例
class
MyInteger
{
private
int
inner
;
public
MyInteger(
int
inner)
{
this
.
inner
= inner;
}
public
void
setInt(
int
i)
{
inner
= i;
}
public
int
getInt()
{
return
inner
;
}
public
String toString()
{
return
""
+
inner
;
}
}
class
Swap
{
public
static
void
swap(MyInteger a, MyInteger b)
{
pl(
"swap"
);
MyInteger temp = a;
a = b;
b = temp;
}
public
static
void
swapI(MyInteger a, MyInteger b)
{
pl(
"swapI"
);
MyInteger temp = a;
a =
new
MyInteger(b.getInt());
b =
new
MyInteger(temp.getInt());
}
public
static
void
swapII(MyInteger a, MyInteger b)
{
pl(
"swapII"
);
int
temp = a.getInt();
a.setInt(b.getInt());
b.setInt(temp);
}
public
static
void
main(String[] args)
{
MyInteger a =
new
MyInteger(1);
MyInteger b =
new
MyInteger(2);
pl(a);
pl(b);
swap(a, b);
pl(a);
pl(b);
swapI(a, b);
pl(a);
pl(b);
swapII(a, b);
pl(a);
pl(b);
}
private
static
void
pl(Object a)
{
System.
out
.println(a);
}
}
运行结果:
1
2
Swap //交换的是实参的引用副本,所以对原来的调用函数没什么影响
1
2
swapI //操作的是实参的引用副本
1
2
swapII //操作的是引用副本所指向的内容,所以对调用函数会发生变化
2
1
总之,在函数参数传递的过程当中Java自动调用Object::clone(),想要进行深拷贝的调用,自己要显示的指明!
public
static
void
swap(MyInteger i, MyInteger j)
{
i.setValue(40);
j.setValue(50);
}
swap
((MyInteger)a.clone(),(MyInteger)b.clone());
3.5 思考下面的例子
package
hziee;
public
class
StringTest
{
static
void
func(String s)
{
s +=
"tail"
;
}
static
void
test()
{
String a =
"abc"
;
func(a);
System.
out
.println(a);
}
public
static
void
main(String[] args)
{
test();
}
}
看了上面的介绍内容应该明白是什么原因了吧?