在getter函数中返回const引用或副本?

时间:2010-02-02 07:15:45

标签: c++ const return-value

默认情况下 更好的是从getter函数返回副本(1)或引用(2)?

class foo {
public:
    std::string str () { // (1)
        return str_;
    }

    const std::string& str () { // (2)
        return str_;
    }

private:
    std::string str_;
};

我知道2)可能更快,但不必因(N)RVO。 1)关于悬挂引用更安全,但对象可能会过时或者永远不会存储引用。

当你写一堂课并且不知道(但)表现和生活问题是否重要时,你的默认是什么?

附加问题:当成员不是普通字符串而是向量时,游戏会改变吗?

8 个答案:

答案 0 :(得分:21)

默认情况下,它确实取决于您对行为的期望。

您是否希望调用者能够看到对str_ unbeknownst(一句话!)所做的更改?然后你需要传回一个参考。如果你有一个refcounted数据成员并返回它,可能会很好。

如果您希望来电者获得副本,请执行1)。

答案 1 :(得分:18)

我的经验法则是返回简单基本数据类型的副本,例如int,string等。对于更复杂的结构,复制可能更昂贵(就像你提到的向量)我更喜欢返回const-reference。 / p>

答案 2 :(得分:10)

在这种情况下,编译器将无法执行(N)RVO。 (命名)返回值优化是一种优化,其中编译器在返回值的位置创建函数自动变量,以避免必须复制:

std::string f()
{
   std::string result;
   //...
   return result;
}

当编译器看到上面的代码时(并假设如果存在任何其他返回,它也将返回result变量)它知道变量result具有唯一可能的命运被复制返回临时然后销毁。然后,编译器可以完全删除result变量,并使用return temporary作为唯一变量。我坚持:编译器不会删除返回临时,它会删除本地函数变量。 return temporary是完成编译器调用约定所必需的。

当您返回类的成员时,该成员必须存在,并且调用约定要求返回的对象位于特定位置(通常是堆栈地址)。编译器无法在返回的对象位置上创建方法属性,也无法进行复制。

答案 3 :(得分:7)

我正在返回一个引用,因为字符串看起来并不“复制”给我。它是一种复杂的数据类型,具有动态内存管理等等。

“如果你想让调用者得到一个副本,你应该按值返回”这个论点没有实际意义,因为它根本不排除副本。呼叫者仍然可以执行以下操作并获得副本

string s = obj.str();

您需要在调用方显式创建一个引用,以便之后能够直接引用数据成员 - 但为什么要这样做呢?肯定有足够的用户定义类型,便宜复制

  • 智能指针
  • 迭代
  • 所有非类型。

答案 4 :(得分:3)

如果不是完全糟糕的设计,则返回对对象内部的引用作为其公共接口的一部分可能是代码气味。

在返回对公共接口中的内部对象的引用之前,设计器应该暂停。这样做可以将您班级的用户与您的部分设计结合起来。通常它是完全没必要的,有时它表明需要进一步的设计工作。正如评论者所指出的那样,有时候这是必要的。

答案 5 :(得分:1)

如果没有特殊原因使用值类型作为返回值,我总是返回一个const引用。如果我需要(或期望需要)一个(可写)副本,我会在返回的类中添加一个copy ctor和一个赋值运算符(如果尚未提供)。对于使用的想法:

const MyClass & ref = container.GetAt( 1234 ); // need only reference
MyClass copy = container.GetAt( 1234 ); // get writable copy 

实际上这很直接,不是吗?

答案 6 :(得分:0)

  1. 如果它是一个小的基本类型 - 像int和long这样的原型和它们的包装器以及其他基本的东西,比如'Point' - 返回一个副本

  2. 如果是字符串或任何其他复杂类型 - 返回引用。

答案 7 :(得分:0)

返回const-reference的唯一问题是我通常会对非基本类型执行的操作是,没有什么可以阻止调用者删除“const”,然后修改值。

就个人而言,我建议这样的代码是个bug。如果他们知道你正在返回一个引用并继续抛弃const那么它就在他们头上。