【问题标题】:Java and C++ pass by value and pass by referenceJava 和 C++ 按值传递和按引用传递
【发布时间】:2011-05-07 15:40:08
【问题描述】:

据说在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 的 *(间接)和 &(引用)运算符。
  • @eggyal C 和 C++ 都没有引用运算符 - & 是运算符的地址。
  • @unapersson:有什么区别? :s
  • @eggyval 在 C++ 中,指针和引用的处理方式不同。 &foo 为您提供指向 foo 的指针,处理它需要您使用指针语法(例如,您必须使用 -> 运算符访问成员)。当你通过引用传递一个对象时,它就像一个值一样(你使用. 操作符来访问成员)。
  • @Alan,它发生在很多年前,当时我试图弄清楚面向对象的编程,当我看到它在汇编程序中的样子时,我终于成功了。要实现 Java 在 C++ 中所做的事情,您只需按值传递原语并按指针传递对象。由于没有明确的 GC,如果您碰巧创建了一些对象,您可能需要释放/删除对象。

标签: java c++


【解决方案1】:

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

另一个显着的区别是,由于引用参数的使用在语法上与复制参数的使用密切相关,因此函数应该能够假设您通过引用传递的对象是有效的。尽管通常可以传递对NULL 的引用,但非常不鼓励这样做,因为这样做的唯一方法是取消引用NULL,它具有未定义的行为。因此,如果您需要能够将NULL 作为参数传递,您会更喜欢通过地址而不是引用来传递参数。

大多数时候,当您想修改函数的参数时,您会希望通过引用而不是地址传递,因为它更“C++ 友好”(除非您需要 NULL 值),甚至虽然它并不完全像 Java 所做的那样。

【讨论】:

  • Java 可以称它们为对象引用。但它们更像 C++ 指针而不是引用,因为它们可以为 NULL。所以我只是将 Java 对象视为垃圾收集指针,它基本上等同于 C++ 共享指针(它只是一个更细粒度的垃圾收集指针)。
【解决方案2】:

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.

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

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

【讨论】:

  • 错了。在 C++ 中没有任何内容通过引用传递。这是一个普遍的误解。您按值传递引用/指针。如果它是通过引用传递的,您可以修改参数本身(而不仅仅是它指向/引用的对象)。
  • @Tamas 您似乎对“通过引用传递”一词有自己的私人含义。
  • @Tamas 是的,它确实如此,与 C++ 完全相同。您在链接到的代码中看到参数列表前面的var 吗?这与在 C++ 中将参数声明为引用完全相同。默认情况下不使用 var,而 Pascal 是按值传递的。你真的认为你不能用 C++ 写一个 swap() 函数????
  • 我错了。感谢您以尊重、冷静的方式解释它。
  • 不,我现在明白了。正如您第一次所说,我将 Java 引用语义与 C++ 中的引用语义混淆了。再次感谢您。
【解决方案3】:
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
 }
};

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

【讨论】:

  • 其实修改*arg会修改调用者中的数据。修改arg不会。
  • 是的,那是错误的。通过指针访问某些内容将更改所有用户的对象。使更改不可见的唯一方法是第二种方法,其中为 arg 调用复制构造函数,并且仅将副本提供给被调用者。
猜你喜欢
  • 2012-07-29
  • 2013-12-02
  • 2014-08-23
  • 2015-05-23
  • 2023-03-07
  • 2015-11-26
  • 2014-04-04
  • 2023-03-02
  • 1970-01-01
相关资源
最近更新 更多