4

在Java 中,所有包含适当对象的变量实际上都是引用(即指针)。因此,以这些对象作为参数的方法调用总是“通过引用”。调用修改对象状态的方法也会影响原始对象(在调用方)。

C++ 不同:这里的参数可以通过值传递或通过引用传递。在按值传递的对象上调用 mutator 方法会使原始对象不受影响。(我想按值调用会创建对象的本地副本)。

所以我对此的第一反应——从 Java 到 C++——是:在使用对象作为参数时总是使用指针。这给了我我对 Java 的期望。

但是,如果不需要修改方法主体中的对象,也可以使用“按值调用”。有什么理由要这样做吗?

4

8 回答 8

13

使用对象作为参数时总是使用指针

不,在 C++ 中总是通过引用传递,除非您的函数可以nullptr作为有效参数调用。如果函数不需要修改参数,则通过const引用传递。

按值传递参数有多种用途。

如果您的函数需要创建参数的副本,最好通过值传递来创建此副本,而不是在函数内创建副本。例如:

void foo( widget const& w )
{
  widget temp( w );
  // do something with temp
}

而是使用

void foo( widget w )  // copy is made here
{
  // operate on w itself
}

这样做还具有允许编译器在可能的情况下移动 widget的好处,这通常比创建副本更有效。

于 2012-04-22T15:49:07.247 回答
9

你错了,你应该通过指针传递。如果你想通过引用传递,那么......只需通过引用传递:

void foo(int& x)
{
   x = 3;
}

int main()
{
   int a = 0;
   foo(a);
   assert( a == 3 );
}

另外,请注意,按值传递保证您的变量不能在被调用的上下文中更改。虽然这样会通过const引用传递......

于 2012-04-22T15:43:09.473 回答
3

如果您按值将对象传递给函数,则该函数可以自由地将这些对象用作“工作”变量,而不会影响调用者。

于 2012-04-22T15:43:18.527 回答
3

在 Java 中,引用是垃圾收集的“智能指针”。

C++ 还使用了智能指针的概念,它在<memory>库中称为unique_ptrshared_ptr。shared_ptr 是引用计数的,因此可以以与 Java 引用相同的方式使用。unique_ptr 类似,除了不可复制且更轻量级。两者的好处是永远不需要使用delete关键字,并且能够依赖受异常保护的“指针”。

C++ 还支持引用的概念——这通常是传递对象的好选择(更好的是引用到const)。C++ 中的引用绑定到传递的对象类型,因此您需要&在函数签名中指定(使用引用符号)

#include <string>

void foo(std::string& bar)
{
    bar = "world";
}

void foo2(const std::string& bar)
{
    //passed by reference, but not modifyable.
}

int main()
{
    std::string str = "hello";
    foo(str);
    foo2(str);
}

至于“原始”指针——您几乎总是可以通过使用智能指针、引用、迭代器或按值传递来避免它们。简单的普通指针带有一个混合包,C++ 从 C 语言继承而来的“陷阱”——如果你有一个相当新的编译器,你根本不需要使用它们(除非你要做类似的事情重新发明轮子以用于内存管理、数据结构等的学习目的)

于 2012-04-22T15:49:41.520 回答
3

您通常按值传递,因为某些东西一个值,并且应该像一个值一样起作用。在许多情况下,通过 const 引用传递足够接近相同的值,值得考虑。在其他情况下,它不是。

按值传递也可以是一种优化。至少 IMO,这或多或少是次要的,但无论如何它可能很重要(尤其是在通过 const 引用传递和传递实际值之间进行选择时。

IMO,真正的问题应该是相反的方向:当你明确告诉它传递一个值时,为什么编译器要传递一个引用?答案是“过早优化”。Java 的设计者(提到您的示例,尽管它在这方面几乎不是独一无二的)决定他们知道最好不要让编译器按照指示去做。由于按值传递大对象可能很慢并且可能是错误的,因此他们决定根本不让它发生,即使它可能很快并且很可能正是预期的。

于 2012-04-22T15:52:17.340 回答
1

我认为您应该将函数签名中的变量类型视为调用者的合同。因此,如果您声明您的功能为:

void foo( int a );

然后你说我会复制你传入的值做任何我喜欢的事情,它不会被修改。

如果您声明如下:

void foo (int* a);

然后我可以修改a指向或确实修改指针

所以语义上的区别是你声明了你的函数的契约可以做什么,使用指针和引用(没有在引用或指针指向的对象上声明的 const )你可以修改变量。

在 C++ 中首选引用,因为它更清楚您的意图,并且您避免使用指向指针函数签名的 c 样式指针,这是在将指针传递给要修改指针指向的函数的函数时所必需的,这会导致错误和头在你意识到出了什么问题之前抓挠。

尽管指针作为参数仍然非常有用,尤其是当您需要测试指向对象的指针是否为空时,使用引用无法做到这一点。

于 2012-04-22T15:55:01.237 回答
1

通过引用传递时存在继承危险,您可能会在方法内部无意中更改传递给方法的值。在方法调用之后,您可以假设该方法没有更改对象,而实际上它确实更改了对象。

按值传递具有需要额外内存的负面影响(并且可能会产生轻微的性能开销),因为您制作了要传入的对象的副本,但好处是您可以确保传递给方法的对象不会在方法内部修改。

于 2012-04-22T16:38:03.350 回答
0

避免副作用很有帮助。如果您的程序需要这样的副作用,请使用引用调用。

于 2012-04-22T15:44:16.297 回答