Java 和 C++ 按值传递,按引用传递
Java and C++ pass by value and pass by reference
据说在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++等价物是什么?
在调用方法后,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 值传递给接受引用的方法。
- 何时应通过引用传递矢量参数而不是按值传递矢量参数?
- 棘手的按值传递和按引用递归问题传递
- 不同于按值传递和常量引用传递的程序集
- 按值传递变量与按引用传递变量具有相同的结果
- 为什么按值传递QStringView比引用常量更快?
- 获取 std::函数以推断按引用传递/按值传递
- C++/11 auto 关键字是在更有效时推导参数进行按引用传递,还是始终按值传递?
- 使用 enable_if 在按值传递与按引用传递之间更改函数声明
- C++按引用传递还是按值传递?
- 可移动但不可复制的对象:按值传递还是按引用传递?
- C++11 按 POD 类型的值传递比常量引用差
- 如何在 C++14 中编写用于调用 Fortran 函数的通用包装器(按引用调用 --按值调用>)
- 为什么编译器可以通过引用传递和值传递来重载函数
- 为什么要按 C++ 中的值传递对象
- C++:如何决定是按引用还是按值传递参数
- 迷失在通过引用、通过值传递的参数中
- 通过引用与通过值传递指向函数的指针,以及在指针链上使用delete
- c++按引用传递、值传递和Const问题
- 复制赋值运算符应该通过常量引用还是通过值传递
- 具有抽象基类的工厂模式 - 按引用或按值返回?范围界定和切片问题