什么时候应该从类方法返回对象的引用

时间:2009-01-30 07:09:41

标签: c++ reference

从类方法返回引用的最佳实践是什么?是否要在没有引用的情况下返回基本类型,而要通过引用返回的类对象。您推荐的任何文章,最佳实践文章。

7 个答案:

答案 0 :(得分:6)

我假设通过类方法你的意思是成员函数。而“通过引用返回”表示“返回对成员数据的引用”。这主要是为了返回对本地的引用,这显然是错误的。

什么时候应该返回对成员数据的引用,以及何时返回数据?

默认情况下,您应该返回数据本身(也就是“按值”)。这避免了返回引用时的几个问题:

  • 用户存储引用并依赖于成员的生存期,而不考虑包含对象(您的对象)将存在多长时间。导致悬空指针。

  • 用户代码依赖于确切的返回类型。例如,您使用vector<T>来实现(这就是您的getter返回的内容)。出现“vector<T> foo = obj.getItems()”等用户代码。然后你改变你的实现(和getter)来使用deque<T> - 用户代码中断。如果按值返回,则只需使getter创建一个局部向量,从成员deque中复制数据,然后返回结果。小型收藏品非常合理。 [*]

那么什么时候应该返回引用呢?

  • 当返回的对象很大(Image)或不可复制(boost::signal)时,您可以考虑它。但是,与往常一样,您可以选择更多的OOP模式,让您的课程,而不是让悬挂。在Image案例中,您可以提供drawCircle成员函数,而不是返回Image&并让您的用户在其上绘制一个圆圈。
  • 当您的数据在逻辑上属于您的用户时,您只是为他持有它。考虑std集合:vector<T>::operator[]引用返回给T,因为这是我想要的:我的确切对象,而不是它的副本。

[*]有一种更好的方法可以确保面向未来的代码。而不是返回一个向量(通过ref by value)返回一对迭代器到你的向量 - 一个开始和一个结束。这样,您的用户就可以使用双端队列或向量执行他们通常使用的所有内容,但这与实际实现无关。为此,Boost提供boost::iterator_pair。作为一个特权,它也有operator []重载,所以你甚至可以做“int i = obj.getItems()[5]”而不是“int i = obj.getItems().begin()[5]”。

此解决方案适用于任何允许您一般处理类型的情况。例如,如果您保留Dog成员,但您的用户只需要知道它是Animal(因为他们只调用eat()sleep()),请返回动物引用/指向免费商店分配的狗副本的指针。然后当你决定狗是弱者并且你真的需要一只狼来实现时,用户代码不会破坏。

这种信息隐藏不仅可以确保未来的兼容性。它还有助于保持您的设计清洁。

答案 1 :(得分:4)

重载赋值运算符(如=,+ =, - =等)是一个很好的例子,通过引用返回很有意义。这种方法显然会返回大对象,并且您不想返回指针,因此返回引用是最好的方法。像指针一样工作,看起来像按值返回。

答案 2 :(得分:2)

引用是伪装的指针(所以它在32位机器上是4个字节,在64位机器上是8个字节)。所以经验法则是:如果复制对象比返回指针更昂贵,请使用指针(或引用,因为这是相同的事情)。

哪些类型的复制成本更高取决于体系结构,编译器,类型本身等。在某些情况下,复制16字节的对象比返回指向它的指针更快(例如,如果一个对象映射到SSE寄存器,或类似的情况)。

现在,当然,返回对局部变量的引用是没有意义的。因为函数退出后局部变量将消失。所以通常你会返回成员变量,全局/静态变量或动态分配对象的引用/指针。

在某些情况下,想要返回对象的指针/引用,即使复制对象很昂贵也是如此。大多数情况下,当您不想将调用代码绑定到原始对象的生命周期中时。

答案 3 :(得分:1)

我建议远离返回引用,原因与Iraimbilanja指出的相同,但在我看来,通过对成员数据使用共享指针(例如,boost,tr1)可以获得非常好的结果并使用它们作为回报。这样您就不必复制对象,但仍然可以管理生命周期问题。

class Foo
{
private:
    shared_ptr<Bar> _bar;
public:
    shared_ptr<Bar> getBar() {return _bar;}
};

通常复制Bar的成本大于构建新的shared_ptrs的成本,如果不是这样的话,它仍然值得用于终身管理。

答案 4 :(得分:1)

Scott Meyers的书 Effective C ++ ,有几个与此主题相关的项目。我肯定会查看标题为“当必须返回对象时不要尝试返回引用”的项目。这是第1版或第2版中的第23项,或第3版中的#21。

答案 5 :(得分:0)

如果NULL是可能的返回值,则该方法必须返回指针,因为您无法返回对NULL的引用。

答案 6 :(得分:-1)

按值返回基本类型,除非您想让调用者访问实际成员。

通过引用返回类对象(甚至是std :: string)。