C ++返回类型,当我不知道它是否是临时的

时间:2011-06-28 11:50:16

标签: c++ reference virtual const return-value

假设Foo是一个相当大的数据结构。如果我不知道继承的类是否会在内部存储const的实例,我应该如何编写一个返回Foo实例的Foo虚函数;因此,允许通过引用返回。如果我不能在内部存储它,我的理解是我不能返回const引用它,因为它将是一个临时的。它是否正确?这两个选项是:

virtual Foo foo() const { ... }
virtual Foo const & foo() const { ... }

这是一个related question,但是从不同的角度来看。

4 个答案:

答案 0 :(得分:9)

作为优化问题,您对值返回和const引用返回之间的区别感兴趣,但事实并非如此。每次返回一个不同的值与每次返回一个引用之间存在根本不同的含义,很可能是同一个对象,很可能会被修改:

const Foo &a = myobj.foo();
myobj.modify_the_foo();
const Foo &b = myobj.foo();
a == b; // do you want this to be true or false?

调用者需要知道它是什么,因为程序员需要知道其含义,并且因为编译器需要知道调用约定,所以不能将它们混合在同一虚函数的不同覆盖中。如果某些派生类想要做一个,有些想要做另一个,那就是运气不好,他们不能,不止一个可以返回int而另一个float

您可能会返回shared_ptr。这样,“想要”返回引用的派生类可以创建一个shared_ptr,其中删除器不执行任何操作(但要注意 - 如果原始对象被销毁,shared_ptr将会悬挂,而这不是你通常期望从返回的shared_ptr得到什么。所以如果Foo比它来自的对象更有意义,那么这个类动态分配它会更好,通过{保持它{1}},并返回该副本,而不是无操作删除程序)。 “想要”返回值的派生类每次都可以分配一个新的类。由于shared_ptr“相当大”,所以希望Foo和动态分配的成本与创建返回的新值相比不会太痛苦。

另一种可能性是将shared_ptr转换为引用相当大的数据结构的小型pImpl样式类。如果涉及的所有内容都是不可变的,那么“想要返回引用”的情况可以共享多个Foo实例之间的大数据结构。即使不是,你也可以考虑写时复制。

答案 1 :(得分:3)

我看到你没有将C ++ 0x列为标记,但作为任何有需求且可以访问C ++ 0x的人的参考,也许最好的方法是返回std::unique_ptr<>。< / p>

答案 2 :(得分:1)

这是一种方式:

struct K 
 { 
 int ii; 
 };

class I 
  { 
  virtual K &get_k(int i)=0; 
  };

class Impl : public I 
  { 
  K &get_k(int i) { kk.ii = i; return kk; } 
  K kk; 
  };

使用它的原因是你使用K kk;在与数据成员相同的对象内。 Impl类的构造函数也可能很有用。

EDiT:更改代码的格式

答案 3 :(得分:1)

如果您不知道派生类是否可以存储该对象,则无法通过引用返回。所以对你的问题的严格回答是你必须按价值回归。

其他答案建议返回指针或智能指针,这也会起作用。但是,没有存储对象的客户端必须执行动态分配,这可能比移动或复制一个相当大的对象要慢。

如果您主要关注的是避免复制,那么您可以通过使界面不那么好来实现这一目标:

virtual void foo(Foo& putReturnvalueHere) const { ... }

将您之前返回的值分配给传入的引用。这需要Foo已经构建。如果这是不可接受的,你可以传入指向一个未构造的内存区域的指针,该内存区域将保存一个Foo,然后使用placement new来构建一个foo到该内存区域:

virtual void foo(Foo* unconstructedFoo) const { ... }

除非你知道自己在做什么,否则我不会推荐最后一个想法,你必须要有最佳表现。如果性能非常重要,您可能需要考虑首先避免虚拟函数调用。