据说在Java方法中,参数是通过值传递的,对于基元和对象都是如此,对象的引用是按值传递的。为了说明考虑代码:
class Test {
private static void dateReplace (Date arg) {
arg = new Date (arg.getYear(), arg.getMonth(), arg.getDate() +1);
System.out.println ("arg in dateReplace: " + arg);
}
public static void main(String[] args) {
Date d1 = new Date ("1 Apr 2010");
dateReplace(d1);
System.out.println ("d1 after calling dateReplace: " + d1);
}
}
this will print:
arg in dateReplace: Fri Apr 02 00:00:00 2010
d1 after calling dateReplace: Thu Apr 01 00:00:00 2010.
可以提供相同结果的C ++等价物是什么?
什么是C ++等价物,在调用方法后将d1的值赋予与方法中相同的值,即调用者看到修改后的值?
答案 0 :(得分:5)
对于值和引用,C ++与Java没有相同的语义。首先,每种类型都有可能通过复制或引用或地址传递(但是您可以通过隐藏复制构造函数来防止类型通过复制传递)。
与Java的“引用”传递最密切相关的传递类型是指针。以下是三个例子:
void foo(std::string bar); // by copy
void foo(std::string& bar); // by reference
void foo(std::string* bar); // by address
作为旁注,对于大于指针大小的类型,通过副本传递总是比通过引用或指针传递更昂贵。出于这个原因,您可能还经常更喜欢通过const
引用,这将允许您无需复制即可读取对象。
void foo(const std::string& bar); // by const reference
但是,这一切都很棘手,你需要了解Java的细微之处,才能正确地决定你在C ++中想要什么。在Java中,您实际上并没有通过引用传递对象:您正在通过复制传递对象引用。也就是说,如果将新对象分配给参数,则父作用域的对象不会更改。在C ++中,这更接近地通过地址传递对象而不是通过引用。以下是一个重要的例子:
// Java using "object references":
public static void foo(String bar)
{
bar = "hello world";
}
public static void main(String[] argv)
{
String bar = "goodbye world";
foo(bar);
System.out.println(bar); // prints "goodbye world"
}
// C++ using "references":
void foo(std::string& bar)
{
bar = "hello world";
}
int main()
{
std::string bar = "goodbye world";
foo(bar);
std::cout << bar << std::endl; // prints "hello world"
}
// C++ using pointers:
void foo(std::string* bar)
{
bar = new std::string("goodbye world");
delete bar; // you don't want to leak
}
int main()
{
std::string bar = "goodbye world";
foo(&bar);
std::cout << bar << std::endl; // prints "hello world"
}
换句话说,当您在C ++中使用引用时,您实际上正在处理您传递的相同变量。您对它所做的任何更改,甚至是分配,都会反映到父范围。 (这部分是由于C ++如何处理赋值运算符。)使用指针,您得到的行为与Java引用的行为更密切相关,代价是可能需要通过一元{{1}获取对象地址。 }}运算符(请参阅我的示例中的&
),需要使用foo(&bar)
运算符来访问成员,以及使用运算符重载的一些额外复杂性。
另一个值得注意的区别是,由于引用参数的使用在语法上与副拷贝参数的使用密切相关,因此函数应该能够假设您通过引用传递的对象是有效的。尽管通常可以将引用传递给->
,但这是非常不鼓励的,因为唯一的方法是取消引用具有未定义行为的NULL
。因此,如果您需要能够将NULL
作为参数传递,则您更愿意按地址而不是引用传递参数。
大多数情况下,当你想要修改函数中的参数时,你需要通过引用而不是地址来传递,因为它更“C ++友好”(除非你需要NULL
值) ,尽管它与Java的确不完全相同。
答案 1 :(得分:1)
默认情况下,C ++按值传递。您可以通过编写函数来接受引用,或通过传递指针显式地通过引用来隐式传递。
考虑以下三个例子:
FooByValue(Foo arg)
{
//arg is passed by value.
}
FooByReference(Foo & arg)
{
//arg is passed by reference.
//changes made to arg will be seen by the caller.
}
FooByPointer(Foo * arg)
{
//arg is passed by reference with pointer semantics.
//changes made to the derefenced pointer (*arg) will be seen by the caller.
}
然后您可以按如下方式调用上述内容 Foo anObject;
FooByValue(anObject); //anObject will not be modified.
FooByRefence(anOBject); //anOBject may be modified.
FooByPointer(&anObject); //anObject may be modified.
&amp;应用于像&anOBject
这样的变量的符号用于获取变量的地址,基本上给出了一个指向存储变量的内存位置的指针。
FooByValue()
与您的示例类似,FooByReference()
将在函数调用后保留对参数的任何更改,因为参数不会被复制,它在内存中的实际位置将被共享。 / p>
答案 2 :(得分:0)
class Test {
void dateReplaceReference(Date& arg) {
// arg is passed by reference, modifying arg
// will modify it for the caller
}
void dateReplaceCopiedArgument(Date arg) {
// arg is passed by value, modifying arg
// will not modify data for the caller
}
void dateReplaceByPointer(Date* arg) {
// arg is passed as pointer, modifying *arg
// will modify it for the caller
}
};
请注意,作为引用或指针传递是一回事。唯一的区别是您希望如何通过“。”访问数据。或者通过“ - &gt;”。此外,您无法将null值传递给参考的方法。