为什么要在C++中按值传递对象?

Rod*_*ddy 15 c++ calling-convention

可能重复:
在C++中通过值传递或通过常量引用传递是否更好?

我知道在C++中传递值,指针和引用的区别,我会考虑在C++中按值(而不是const引用)传递对象几乎总是编程错误.

void foo(Obj o); ... // Bad

void foo(const Obj &o); ... // Better
Run Code Online (Sandbox Code Playgroud)

唯一可以考虑通过值而不是const引用传递的地方的唯一情况是对象小于引用的位置,因此传递值更有效.

但是,这肯定是构建编译器以确定的那种东西吗?

为什么C++实际上需要传递值并通过const引用传递,并且 - 编译器是否允许自动将调用转换为(和来自)const引用(如果适用)?

(似乎有100个C++调用约定问题,询问(比如)值和引用之间的差异 - 但我找不到一个问"为什么?".)

Dav*_*eas 10

通过值传递的时间可能比通过const引用更好的问题对于不同版本的标准有不同的答案.

在好的旧C++ 03和几年前,建议通过const引用传递任何不适合寄存器的东西.在这种情况下,答案是:

  • 因为Obj适合寄存器并通过值传递并通过值将更有效

仍然在C++ 03中,在过去的几年里(看起来很荒谬,因为看起来有些文章推荐这差不多10年了,但是没有真正的共识),

  • 如果函数需要复制,那么在接口中这样做允许编译器执行copy-elision,如果复制的源是临时的,那么它可以更有效.

随着新的C++ 11标准的批准,以及增加编译器对rvalue-references的支持,在许多情况下即使复制也无法被删除,并且

  • 如果函数需要复制,即使复制不能被删除,并且对于支持它的类型,内容也会被移动(通常术语中的对象将被移动,但它只是被移动的内容),这比内部复制更有效率.

至于为什么两个不同的呼叫惯例,他们有不同的目标.按值传递允许函数修改参数的状态而不会干扰源对象.此外,源对象的状态也不会干扰该函数(考虑多线程环境,以及在函数仍在执行时修改源的线程).


Mar*_*k B 6

当然,C++具有pass-by-value的一个原因是因为它从C继承了它,并且删除它可能会破坏代码以获得少量收益.

其次,正如您所注意到的,对于小于参考值的类型,传递值会降低效率.

然而另一个不那么明显的情况是,如果你有一个函数需要它的参数的副本由于某种原因:

void foo(const Obj& obj)
{
    if(very_rare_check()) return;

    Obj obj_copy(obj);
    obj_copy.do_work();
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下请注意您正在强制复制.但是假设您使用另一个按值返回的函数的结果调用此函数:

Obj bar() { return Obj(parameters); }
Run Code Online (Sandbox Code Playgroud)

并称之为: foo(bar());

现在当你使用const引用版本时,编译器最终会生成两个对象:临时对象和副本对象foo.但是,如果按值传递,编译器可以将所有临时值优化到by-value参数所使用的位置foo.

有一篇很棒的文章,关于这一点并在http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/上移动语义

最后,实现某些运算符的规范方法是使用pass-by-value来避免运算符内的副本:

Obj operator+(Obj left, const Obj& right)
{
    return left += right;
}
Run Code Online (Sandbox Code Playgroud)

注意这是如何让编译器在参数中生成副本而不是在操作符的代码本身中强制复制或临时对象.


小智 4

如果我想在函数内对对象执行操作而不影响原始对象,我将按值传递:

A minus(A b){
    b.val=-b.val;
    return b;
}
Run Code Online (Sandbox Code Playgroud)