// C ++示例
#include <iostream>
using namespace std;
int doHello (std::string&);
int main() {
std::string str1 = "perry";
cout << "String=" << str1 << endl;
doHello(str1);
cout << "String=" << str1 << endl; // prints pieterson
return 0;
}
int doHello(std::string& str){
str = "pieterson";
cout << "String=" << str << endl;
return 0;
}
在上面的情况中,正如预期的那样,当str参考被修改时,字符串'str1'引用被修改
// Java示例
public class hello {
public static void main(String args[]){
String str1 = "perry";
System.out.println("String=" + str1);
doHello(str1);
System.out.println("String=" + str1); // does not prints pieterson
}
public static void doHello(String str){
str = "pieterson";
System.out.println("String = " + str);
}
}
在Java中,String str和String str1是最初引用相同String的两个不同对象,因此当我们在doHello()中更改str引用时,str1引用不会被更改。
我们如何使用字符串,List,Vector,其他对象等集合在Java中实现C ++样式功能。
更新
感谢Jon的精彩解释,我相信任何Java初学者肯定都会遇到这个问题。 让我解释一下使用列表时遇到的问题。
//bad doHello()
void doHello(List inputList) {
inputList = getListFromAnyFunction(); // wrong, didnt work
}
// good doHello
void doHello(List inputList) {
inputList.addAll(getListFromAnyFunction()); // worked
}
感谢Powell和Harshath的解释和代码示例。
答案 0 :(得分:13)
Java根本没有通过引用传递(您在C ++代码中使用它)。引用按值传递。 (str
和str1
的值根本不是对象,它们是引用 - 它确实有助于使这两个概念保持分离。)
通常,如果需要,您将使用返回值返回新引用:
str1 = doHello(str1);
请注意,String与List等略有不同,因为字符串是不可变的。要修改集合(以及任何可变集合),您不需要创建新集合,只需通过原始引用修改对象:
public static void addHello(List<String> items)
{
items.add("Hello");
}
然后你可以这样称呼它:
List<String> list = new ArrayList<String>();
addHello(list);
System.out.println(list.get(0)); // "Hello"
改变现有对象和更改变量值以引用不同对象之间的区别至关重要。如果您想单独保留现有集合并创建一个新集合,则必须明确地执行此操作:
public static List<String> withHello(List<String> items)
{
List<String> newList = new ArrayList<String>(items);
newList.add("Hello");
return newList;
}
然后你会这样称呼它:
List<String> empty = new ArrayList<String>();
List<String> newList = withHello(empty);
System.out.println(empty.size()); // Prints 0
System.out.println(newList.size()); // Prints 1
这是否能满足您的一切需求?
答案 1 :(得分:13)
在C ++中,有两种参数传递形式, 按值调用 和 按引用调用 。 (按值调用的更好名称是按新L值调用,但我离题了。)
在Java程序中,所有参数都按值传递。 [有些人认为Java通过引用传递对象,但是他们错了 - 请看下面的长答案 - Java传递对象的引用值,但这根本不是一回事]
Java对象的值(以及传递的内容)是对象的指针(令人困惑,但准确地说,称为对象的引用),以及调用Java对象方法,例如obj.meth()
,在调用那里找到的对象的方法之前,取消引用obj
的指针值。
在C ++中,同样的调用看起来像:obj->meth()
。
如果你操作对象(例如,通过调用它上面的方法)作为参数传递给Java中的方法,这可能会修改对象,如果调用者仍然有一个包含该对象的变量,它可以看到这些变化。如果只是分配给(本地)参数变量,只需更新存储在局部变量中的指针 - 对象不受影响,调用者不会注意到这一点。
因此,对您的问题的简短回答是Java按值传递,而C ++的调用引用机制在Java中不可用。
要了解这里发生了什么,我们需要了解变量和参数传递的机制。
一般而言,编程语言中的变量是与值相关联的标识符。表示在运行时处理的值的变量(我们在这里忽略宏变量,“编译掉”的常量等等)必须有 两个 与它们相关的值。这是一个解释原因的例子:
int a = 0;
a = a + 1;
a
是变量,在行a = a + 1;
中,它被称为两次。一次,(在=
的右侧),值0
表示一次,(在左侧) =
)a
值的位置是指(即,其中 a
的整数值存储)。只有访问该位置,程序才能更新包含在其中的值。
与变量关联的这两个值称为 R值和 L值。 (这些名字是由Christopher Strachey发明的;我是粉丝。)语言中的其他结构根据上下文指代这些值中的一个或另一个。例如:
a[x-1] = x;
指的是x
的R值两次(即使一个位于=
的左侧),但仅指a
的L值(或至少一个数组a
}的元素。
在大多数编程语言中,我们明确地操纵变量的R值,并让编译器(从程序的形式)自动管理L值。我们必须对它们有所了解(在C中,特别是对于数组,我们假设很多关于L值),但大多数时候我们不需要知道机制。
看看这个电话:
int p=1;
obj.meth(p);
…
void meth(int a) {…a…}
这里的参数会发生什么变化?首先计算p
的R值,然后,当'{1}}'输入'时,构造新的变量meth
。它被赋予一个新的L值(这个位置通常在某种堆栈上),并且参数的R值存储在该位置。
现在,当a
出现在a
时,它具有meth
(最初)具有的相同R值和新的L值。对p
的R值的更改对变量a
没有影响。这称为按值调用(更恰当地说,按新L值调用)。
看看这个C ++调用:
p
此处计算参数int p=1;
obj.meth(p);
…
void meth(int& a) {…a…}
的L值,并且当构造变量p
时,它将“给定”相同的 L值作为{{ 1}}。然后在a
的正文中使用p
就像使用a
一样。例如,分配将改变meth
中的存储值以及(显然)p
中的值。
Java不会这样做。
答案 2 :(得分:1)
用java编写的代码等同于这个C ++代码:
int main() {
std::string *s1;
s1 = new std::string("perry");
std::cout << s1->c_str() << std::endl;
doMojo( s1 );
std::cout << s1->c_str() << std::endl; // not pieterson
}
void doMojo( std::string *str ) {
str = new std::string("pieterson");
std::cout << str->c_str() << std::end;
}
或许现在你看到会发生什么。 C ++引用实际上包围了指针..因此,如果没有引用,您的代码将如下所示:
int main() {
std::string *s1;
s1 = new std::string("perry");
std::cout << s1->c_str() << std::endl;
doMojo( s1 );
std::cout << s1->c_str() << std::endl; // now pieterson!
}
void doMojo( std::string *&str ) {
str = new std::string("pieterson");
std::cout << str->c_str() << std::end;
}
注意无害的&符号:
void doMojo( std::string *&str ) {
这真的是秘密。这个,以及解释java字符串不变性的前一个答案应该会让你了解java和c ++引用之间的区别。
干杯,
JRH。
答案 3 :(得分:0)
java中的字符串是不可变的,一旦创建就无法修改。在doHello()中使用赋值实际上创建了一个新的字符串对象,而不是修改现有的字符串对象。结果,2个java引用(一个在main中,另一个在doHello中)分别对2个不同的字符串进行字符串引用。这就是为什么你会在C ++和Java之间看到不同结果的原因。