C++&引用;“最佳”;参数传递法 我今天编写了一个C++类,我写了一个函数,它把一个参数当作一个引用而不是一个指针,这是我很少做过的。我总是通过指针。所以我正要把它改回去,然后我意识到——我不知道我是否应该,或者它是否重要

C++&引用;“最佳”;参数传递法 我今天编写了一个C++类,我写了一个函数,它把一个参数当作一个引用而不是一个指针,这是我很少做过的。我总是通过指针。所以我正要把它改回去,然后我意识到——我不知道我是否应该,或者它是否重要,c++,performance,coding-style,parameter-passing,C++,Performance,Coding Style,Parameter Passing,所以我转向你们。我有三种传递参数的方法: //1: By pointer Object* foo(Object* bar) {…} //2: By reference Object& foo(Object& bar) {…} //3: By value (just for completeness) Object foo(Object bar) {…} 假设#3是出于性能原因(是的,我知道编译器在这方面做得很好,但仍然如此),那么其他两个或多或少是等效的 那么,“最好”的方

所以我转向你们。我有三种传递参数的方法:

//1: By pointer
Object* foo(Object* bar) {…}

//2: By reference
Object& foo(Object& bar) {…}

//3: By value (just for completeness)
Object foo(Object bar) {…}
假设#3是出于性能原因(是的,我知道编译器在这方面做得很好,但仍然如此),那么其他两个或多或少是等效的

那么,“最好”的方法是什么?指针?推荐人?两者的某种结合?或者这有什么关系?技术上的原因是最好的,但风格上的原因同样好


更新:我接受了YeenFei的答案,因为它解决了让我得到答案的差异(即使我当时故意忽略了他的建议——我喜欢选择NULL…)。但每个人都提出了很好的观点,尤其是GMan(在评论中)和Nemo,在回答性能和价值传递时。如果你在这里寻找答案,请全部核对

如果你自己设计所有东西,任何时候都可以参考。惯用的现代C++几乎不应该在任何地方都有原始指针。动态分配的对象应该在资源管理容器中移动(
shared_ptr
unique_ptr
,或
弱_ptr
,如果适用),但对于大多数操作,传递(const)引用是传递需要修改的参数或重载类型参数的主要方式。不要忘记,如果您有可移动的类型,按值传递可能是一个可行的选项。

按指针传递和按引用传递实际上是相同的,只是语法不同。我更喜欢通过指针传递,因为它使事情变得明确:

Object bar;
ptr_foo(&bar); // bar may change

ref_foo(bar); // can bar change? Now I need to go look at the prototype...

val_foo(bar); // bar cannot change. (Unless you use references here and there)

传递值和指针之间唯一的技术偏好,正如您所提到的,是类是否足够大,从而使其传递速度变慢。

如果预期参数有效,我建议通过引用传递参数。这将是一个由设计优化和节省您从

引用不能为空,而指针可以为空。
如果您处理的是指针,那么在使用它们之前,您需要验证给定指针是否有效(非空),无论它是原始形式还是包装在托管容器(共享的)中。

因此我将为选择提供理由#3。考虑下面的代码:

struct Foo {
    int x;
    int y;
};

Foo
add(Foo a, Foo b)
{
    Foo result;
    result.x = a.x + b.x;
    result.y = a.y + b.y;
    return result;
}

Foo
add2(Foo &a, Foo &b)
{
    Foo result;
    result.x = a.x + b.x;
    result.y = a.y + b.y;
    return result;
}
尝试检查生成的程序集。请注意,
add
几乎完全是注册操作,安排得很好。请注意,
add2
是如何在没有任何重新排序的情况下进行大量内存访问的

我写了一个
main
,它调用了这些函数100亿次。结果如何
add
耗时22秒,而
add2
耗时26秒。即使对于这个简单的示例,传递值版本的性能也提高了10-20%

好的,结构很简单。但功能也是如此。函数越复杂,传递值版本越可能更快,因为编译器知道这两个参数不会“重叠”。这对优化是一个巨大的好处

当然,这个决定应该主要基于函数的语义:是否需要NULL作为合法值?如果是这样,显然你需要一个指针。是否需要修改对象?然后使用指针或引用

但是,如果不需要修改对象,则更愿意按值传递它们,除非对象很大和/或具有非平凡的复制构造函数(例如std::string)。若by value真的太慢,请按常量的引用或指向常量的指针传递

但是,不要低估按值传递的潜在速度优势,它源自寄存器与内存和指令重新排序的优势。请注意,随着每一代CPU的出现,这些优势变得更加明显。

使用:

  • 未修改对象时的常量引用
  • 如果对象已修改或可以为空,则为指针
  • 如果对象很小,并且您关心性能,或者如果您需要在函数中复制对象,则为。这允许编译器选择复制/移动参数的最佳方式
  • std::如果所有权转移到函数,则为唯一

  • 您可以查看库,它会自动将类型转换为最佳参数类型。

    由于您省略了函数体,我猜您认为它是不相关的。我向你保证这不是最好的,为了什么?如果要引用对象,请使用引用。如果要指向某个对象,或者可能根本没有对象(null),请使用指针。如果您想要自己的副本,请使用一个值。无论原因是什么,性能都不是其中之一。不同意性能不是问题。事实上,对于小物体,#3几乎肯定会是最快的。。。别名问题往往会导致指针和引用生成比值慢的代码。通常要慢得多。嗯,复制构造给定对象总是比指针/引用慢。@YeenFei,如果对象不包含太多数据(例如单个int),则传递值的速度更快。@Mark Ransom,这是一个有趣的点(尽管我们都知道设计的对象在大多数情况下都比指针/引用大得多)。你介意让我看一看解释原因的文章吗?除了每个接受指针的函数都必须处理null之外,如果你的程序要正确的话。@YeenFei:你完全错了。尽管参数本身的传递速度可能更快,但生成的代码几乎肯定会更糟,而且会弥补更多。由于别名关注的内存容量过高/存储量是C++优化的阿基里斯的后端。如果不采取措施防止引用,那么引用可能是空的(即坏的)。但你的观点是不必检查参考资料