【问题标题】:C++ References and Java ReferencesC++ 参考和 Java 参考
【发布时间】:2010-11-02 01:30:59
【问题描述】:

//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 的引用不会改变。

我们如何在 Java 中使用字符串、List、Vector 等集合、其他对象来实现 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 的解释和代码示例。

【问题讨论】:

    标签: java c++


    【解决方案1】:

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

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

    str1 = doHello(str1);
    

    请注意,String 与 List 等略有不同,因为字符串是不可变的。要修改集合(嗯,任何可变集合),您无需创建新集合,只需通过原始引用修改 object

    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
    

    这能满足您的所有需求吗?

    【讨论】:

    • 这有点错误,严格来说是正确的,Java 透明地通过引用传递所有对象(任何不是基本类型,例如 int 但不是 Integer),并且,如您所知,您不会t 有任何指针。通过这种方式,您可以使用空“指针”异常来使 Java 程序崩溃,即使该语言没有它们。我做过的最令人困惑的项目之一是结合了 Java 和 C++ 的项目,两者看起来相似,但需要以一种微妙不同的方式思考问题。
    • 我不同意 Chris 的观点。引用类型表达式的值是一个引用,并且该引用按值传递给方法。当然,这可以实现为指针(在这种情况下,它按值传递指针,这与按引用传递对象仍然不同)。不幸的是,“NullPointerException”类基本上被命名了。声称 Java 通过引用传递对象在过去引起了很大的混乱。知道“通过引用”真正含义的人然后期望能够编写“交换”方法等。
    • 请参阅javadude.com/articles/passbyvalue.htm 和许多 Stack Overflow 问题和答案以了解更多信息:)
    • (特别是,我会说很难声称 Java 在第 8.4.1 节中使用传递引用,正如上面链接的文章中引用的那样:当方法或构造函数被调用(第 15.12 节),实际参数表达式的值初始化新创建的参数变量 [...]”看到说“实际参数表达式的值初始化新创建的参数变量”的位?这基本上是定义pass-by-value:计算表达式,传递值。
    • 实际上 - 让“感谢链接”以避免混淆;)
    【解决方案2】:

    参数传递、引用调用和值调用(简答)

    在 C++ 中有两种参数传递形式,按值调用按引用调用。 (call-by-value 更好的名字是 call-by-new-L-value,但我离题了。)

    在 Java 程序中,所有参数都按值传递。 [有些人认为 Java 通过引用传递对象,但他们错了——请参阅下面的长答案——Java 传递对象的引用值,但这根本不是一回事.]

    Java 对象的(以及传递的内容)是一个指向对象的指针(虽然令人困惑,但准确地说,称为对对象的引用),并且调用 Java 对象方法(例如 obj.meth())会在调用找到的对象上的方法之前取消引用 obj指针 值。

    在 C++ 中,相同的调用看起来像:obj-&gt;meth()

    如果您操作作为参数传递给 Java 中的方法的对象(例如,通过调用其上的方法),这可能会修改该对象,如果调用者仍然有一个保存该对象的变量,它可以看到这些变化。如果您只是分配给(局部)参数变量,您只需更新存储在局部变量中的指针——对象不受影响,调用者不会注意到这一点。

    所以,对您的问题的简短回答是 Java 按值传递,而 C++ 的按引用调用机制在 Java 中不可用。

    参数、变量和按值调用(长答案)

    要了解这里发生了什么,我们需要进一步了解变量和参数传递的机制。

    变量

    一般来说,编程语言中的变量是与值相关联的标识符。表示在运行时操作的值的变量(我们在这里忽略宏变量、“编译掉”的常量等等)必须有两个 与它们相关的值。这是一个解释原因的示例:

    int a = 0;
    a = a + 1;
    

    a 是变量,在a = a + 1; 行中它被两次引用。一次,(在=右侧)值0 一次,(在=左侧a 的值的 位置 是指(即 a 的整数值在哪里存储)。只有访问该位置,程序才能更新其中包含的值。

    与变量相关的这两个值称为R-valueL-value。 (这些名称是由 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 值,然后,当'输入'meth 时,构造一个新变量a。它被赋予一个新的 L 值(这个位置通常在某种堆栈上),并且参数的 R 值存储在该位置中。

    现在,当a 出现在meth 中时,它具有与p(最初)相同的 R 值和一个新的 L 值。 a 的 R 值的更改对变量 p 没有影响。这称为 call-by-value(更准确地说,call-by-new-L-value)。

    引用调用

    看看这个 C++ 调用:

    int p=1;
    obj.meth(p);
    …
    void meth(int& a) {…a…}
    

    这里计算了参数p 的L 值,并在构造变量a 时“给定”了与p 相同的L 值。那么在meth 的主体中对a 的任何使用就像对p 的使用一样。例如,赋值将更改 p 中的存储值以及(显然)a 中的值。

    Java 不这样做。

    【讨论】:

      【解决方案3】:

      你用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.

      【讨论】:

        【解决方案4】:

        java中的字符串是不可变的,一旦创建就不能修改。在 doHello() 中使用赋值实际上是创建一个新的字符串对象,而不是修改现有的。结果,2个java引用(一个在main中,另一个在doHello中)分别对2个不同字符串的字符串引用。这就是为什么您会在 C++ 和 Java 之间看到不同的结果。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多