【问题标题】:Value or reference semantics in this situation?在这种情况下是值还是引用语义?
【发布时间】:2010-12-12 12:05:15
【问题描述】:

对于在某些情况下是选择值还是引用语义,我没有感觉(但我希望如此)。有什么我可以应用的经验法则吗?

我通常为除内置数据类型(char、int、bool、double 等)以外的所有内容选择引用。但是,有时不可能从函数返回引用,所以我不得不使用指针。下面的函数就是一个例子:

Foo bar()
{
    Foo f;
    f.do_stuff();
    return f;
}

我会使用 boost::shared_ptr 来存储 Foo 对象,但这使得使用该对象非常难看。我目前正在研究一个返回一个几乎不会超过 10 个元素的双端队列的函数(这是我的假设,我无法确定)。按值返回可以吗?我的考虑是否属于过早优化?

【问题讨论】:

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


【解决方案1】:

按值返回很好,因为大多数编译器会优化额外的副本(这称为返回值优化,或者在您的情况下称为返回值优化)。

但惯用的方式是

void bar(Foo& out)
{
   out.do_stuff();
}

【讨论】:

  • @fhd:这种方法有两个缺点:您必须确保 out 处于您想要的状态,并且输入起来更麻烦(而且不太自然)。我通常会按价值返回并依赖 NRVO。
  • @fhd:然而,这种方法有一个优点,就是您不会在函数内部构造对象,因此您可以避免可能破坏异常安全的构造失败。跨度>
【解决方案2】:

在任何情况下,不要返回任何通过引用或指针分配在堆栈上的东西(即局部变量,例如这里的 f)。

【讨论】:

  • @Alex:因为它是在堆栈上分配的,所以当函数返回时,基于堆栈的变量被释放并且您的指针或引用变得无效。
  • @sashan:措辞不清楚(甚至是错误的)。它应该是“不要返回任何东西......”。我投了反对票,如果@Ofir 编辑了它的答案,我将取消反对票。
  • @Alexandre:因为在您的函数完成后,该对象将与堆栈帧一起销毁,因此您最终会得到一个悬空指针。
  • @Alex:我认为 Ofir 的意思很明显,所以我编辑了答案。
【解决方案3】:

你总是可以这样做的:

Foo& bar(Foo& f)
{
    f.do_stuff();
    return f;
}

并像这样使用它:

Foo f;
bar(f);

这里的缺点是您不能保证bar() 会收到Foo 对象的新副本。如果这很重要,您必须将其修改为:

Foo& bar(Foo& f)
{
    f = Foo();
    f.do_stuff();
    return f;
}

但是,如果它确实获得了新副本,则会进行不必要的初始化。或者,您可以在doSomething() 之前检查f,如果不符合预期则抛出异常。

【讨论】:

    【解决方案4】:

    当您想要维护任何变量或对象的状态时使用引用(即,在被调用例程对变量/对象进行了一些处理之后,将相同的变量重新返回给调用例程)以及在传递的情况下大数据到函数,否则会有大容量数据的双重副本。

    同样在某些需要防止按位复制对象的情况下,在这种情况下,我们将类的复制构造函数设为私有。

    【讨论】:

      【解决方案5】:

      我的正常规则是:

      1. 始终使用按值传递。

        简单吧?参考很烂。他们不应该被介绍。

      2. 如果您有“数据结构之类的值”,请传递它。如果您有“类似数据结构的对象”,请传递一个指针(引用语义,按值传递)。

        通常很清楚类型是什么:值或对象。如果你可以改变它,它就是一个对象。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-06-06
        • 1970-01-01
        • 2011-01-08
        • 2019-01-20
        • 1970-01-01
        • 2019-10-15
        • 2013-12-23
        相关资源
        最近更新 更多