C ++参考和Java参考

时间:2009-06-11 07:38:37

标签: java c++

// 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的解释和代码示例。

4 个答案:

答案 0 :(得分:13)

Java根本没有通过引用传递(您在C ++代码中使用它)。引用按值传递。 (strstr1的值根本不是对象,它们是引用 - 它确实有助于使这两个概念保持分离。)

通常,如果需要,您将使用返回值返回新引用:

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之间看到不同结果的原因。