Java和C ++按值传递并通过引用传递

据说在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的值赋予与方法中相同的值,即调用者看到修改后的值?

对于值和引用,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引用的行为更密切相关,代价是可能必须通过一元&运算符获取对象地址(请参阅我的示例中的foo(&bar) ,需要使用->运算符来访问成员,以及使用运算符重载的一些额外复杂性。

另一个值得注意的区别是,由于引用参数的使用在语法上与副拷贝参数的使用密切相关,因此函数应该能够假设您通过引用传递的对象是有效的。 即使通常可以将引用传递给NULL ,但这是非常不鼓励的,因为唯一的方法是取消引用NULL ,它具有未定义的行为。 因此,如果您需要能够将NULL作为参数传递,则您更愿意按地址而不是引用传递参数。

大多数情况下,当你想要修改函数中的参数时,你会希望通过引用而不是地址传递,因为它更“C ++友好”(除非你需要NULL值),即使它不完全像Java做什么。

默认情况下,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. 

当应用于像&anOBject这样的变量时,&符号用于获取变量的地址,基本上给出一个指向存储变量的存储器中的位置的指针。

FooByValue()操作类似于您的示例,并且FooByReference()将保留函数调用后对参数所做的任何更改,因为参数不会被复制,它在内存中的实际位置将被共享。

 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 } }; 

请注意,作为引用或通过指针传递是一回事。 唯一的区别是您希望如何通过“。”访问数据。 或者通过“ – >”。 此外,您不能将null值传递给采用引用的方法。