C ++:如何通过ref或value来决定是否传递params?

时间:2012-02-25 07:30:33

标签: c++ performance struct pass-by-reference pass-by-value

使用C ++我如何决定是否应该通过值或引用/指针传递参数? (告诉我32位和64位的答案)让我们看看A.是否有2个32位的值更少或者相等,可以作为指向32位值的指针?

对我来说,B似乎总是应该超越价值。 C我认为我应该通过价值,但有人告诉我(但我没有看到证据)处理器不处理值而不是他们的比特,所以它是更多的工作。因此,如果我传递它们将是更多的工作来传递值因此byref更快?最后我扔了一个枚举。我认为枚举应该总是按价值

注意:当我用ref表示我指的是const引用或指针(不能忘记const ...)

struct A { int a, b; }
struct B { int a; }
struct C { char a, b; }
enum D   { a,b,c }
void fn(T a);

现在告诉我答案,如果我多次推送参数并且代码不使用尾调用? (假设这些值在4个左右的深度调用之前不会被使用)

9 个答案:

答案 0 :(得分:4)

忘记堆栈大小。如果你想改变它,你应该通过引用传递,否则你应该传递值。

通过允许函数意外地更改数据来防止引入的错误类型比浪费的堆栈空间的几个字节重要得多。

如果堆栈空间成为问题,请停止使用这么多级别(例如使用迭代解决方案替换递归解决方案)或扩展堆栈。除非您的结构庞大或者您在嵌入式世界中运行,否则四级递归通常不会繁重,

如果性能成为一个问题,找到一个更快的算法:-)如果那是不可能的,然后你可以看看通过引用传递,但你需要明白它打破了调用者和被叫方。如果你能忍受,那没关系。我一般不能: - )

值/引用二分法的目的是控制在语言级别作为参数传递的事物发生的事情,而不是控制语言的实现的工作方式。< / p>

答案 1 :(得分:3)

我通过引用传递所有参数以保持一致性,包括内置(当然,尽可能使用const)。

我在性能关键领域进行了测试 - 与内置组件相比,最坏情况下的损失是微不足道的。对于非内置函数,以及当调用很深时(作为泛化),引用可以快得多。这对我来说非常重要,因为我正在进行相当深入的TMP,其中函数体很小。

如果您计算指令,硬件是寄存器缺乏(例如嵌入式),或者该函数不适合内联,您可以考虑违反该约定。

不幸的是,您提出的问题比看上去要复杂得多 - 答案可能会因您的平台,ABI,调用约定,注册计数等而有很大差异。

答案 2 :(得分:1)

很大程度上取决于您的要求,但最佳做法是通过引用传递,因为它会减少内存占用量。

如果按值传递大对象,则会在内存中创建一个副本,并调用复制构造函数来复制它。

因此,它需要更多的机器周期,而且,如果您通过值传递,更改不会反映在原始对象中。

所以尝试通过引用传递它们。

希望这对你有所帮助。

问候,肯

答案 3 :(得分:1)

首先,引用和指针不一样。

通过指针

如果其中任何/部分适用,则按指针传递参数:

  1. 传递的元素可以为null。
  2. 资源在被调用函数内部分配,调用者负责释放这样的资源。在这种情况下,请记住为该资源提供free()函数。
  3. 该值属于变量类型,例如void*。当它的类型在运行时或根据使用模式(或隐藏实现 - 即Win32 HANDLE)确定时,例如线程过程参数。 (这里支持c ++模板和std :: function,只有在你的环境不允许的情况下才使用指针。
  4. 通过引用传递

    如果其中任何/部分适用,则按引用传递参数:

    1. 大部分时间。 (更喜欢通过const引用传递)
    2. 如果您希望调用者可以看到对传递的参数的修改。 (除非使用const引用)。
    3. 如果传递的参数永远不为null。
    4. 如果您知道传递的参数类型是什么,并且您可以控制函数的签名。
    5. 通过副本

      如果其中任何/部分适用,请传递副本:

      1. 一般尽量避免这种情况。
      2. 如果要对传递的参数的副本进行操作。即你知道被调用的函数无论如何都会创建一个副本。
      3. 原始类型小于系统的指针大小 - 因为它与const ref相比没有性能/内存差异。
      4. 这很棘手 - 当你知道该类型实现了一个移动构造函数(例如C ++ 11中的std :: string)。然后看起来好像你正在复制。
      5. 这三个列表中的任何一个都可以更长,但这些 - 我会说 - 是基本的经验法则。

答案 4 :(得分:0)

我的完整问题对我来说有点不清楚,但是当你使用价值或参考价值时,我可以回答。

当通过值传递时,您将参数的完整副本放入调用堆栈中。这就像你在函数调用中创建一个局部变量,用你传入的函数初始化。

当通过引用传递时,你......好吧,通过引用传递。主要区别在于您可以修改外部对象。

减少通过引用传递的大对象的内存负载是有好处的。对于基本数据类型(例如32位或64位整数),性能可以忽略不计。

通常,如果您打算使用C / C ++,您应该学会使用指针。将对象作为参数传递几乎总是通过指针(vs引用)传递。绝对必须使用引用的少数实例位于复制构造函数中。你也想在运营商中使用它,但这不是必需的。

答案 5 :(得分:0)

按值复制对象通常是一个坏主意 - 更多的CPU来执行构造函数;为实际对象提供更多内存。使用const来阻止修改对象的功能。函数签名应该告诉调用者引用的对象可能会发生什么。

intcharpointers等内容通常按值传递。

对于您概述的结构,通过值传递并不重要。你需要进行分析才能找到答案,但是在程序的宏观方案中,你最好去其他地方寻找提高CPU和/或内存性能的方法。

答案 6 :(得分:0)

在您担心优化之前,我会考虑您是否需要值或引用语义。通常,如果您希望调用的方法能够修改参数,则可以通过引用传递。你可以在这种情况下传递一个指针,就像你在C中一样,但惯用的C ++倾向于使用引用。

没有规则说小型或枚举应始终按值传递。有很多代码传递int&个参数,因为它们依赖于通过引用传递的语义。此外,您应该记住,对于任何相对较小的数据类型,您都不会注意到通过引用传递和按值传递速度的差异。

那就是说,如果你有一个非常大的结构,你可能不想制作它的大量副本。这是const引用很方便的地方。请记住,虽然C ++中的const没有严格执行(即使它被认为是不好的做法,但你总是可以const_cast离开它。没有理由在const int&上传递int,但有理由将const ClassWithManyMembers&传递给ClassWithManyMembers

如果您打算将它们视为值,那么您列出的所有结构都可以通过值传递。考虑一下,如果你调用一个带有struct Rectangle{int x, y, w, h}类型参数的函数,这与独立传递这4个参数是一样的,这真的不是什么大问题。通常,您应该更担心复制构造函数必须执行的工作 - 例如,按值传递vector可能不是一个好主意,因为它必须动态分配内存并遍历列表你不知道它的大小,并调用更多的副本构造函数。

虽然你应该记住这一切,但一个好的一般规则是:如果你想要refence语义,请通过refence。否则,按值传递内在函数,通过const引用传递其他东西。

此外,C ++ 11引入了r值引用,这进一步使事情复杂化。但这是一个不同的话题。

答案 7 :(得分:0)

这些是我使用的规则:

  • 原生类型:
    • 当它们是输入参数时的值
    • 通过非const引用,当它们是强制输出参数时
  • 结构或类:
    • 当它们是输入参数时由const引用
    • 通过非const引用,当它们是输出参数时
  • 对于数组:
    • 当const指针是输入参数时(const适用于数据,而不是指针,即const TYPE *
    • 当它们是输出参数时指针(const适用于数据,而不是指针)

我发现很少有人需要对上述规则作出例外处理。我想到的一个例外是一个可选的struct或class参数,在这种情况下引用不起作用。在这种情况下,我使用const指针(输入)或非const指针(输出),这样你也可以传递0。

答案 8 :(得分:0)

如果您想要副本,请按值传递。如果要更改它并且希望在函数外部看到这些更改,则按引用传递。如果你想要速度并且不想改变它,请通过const引用。