我现在真的很困惑如何以及使用哪种方法从函数返回对象。我想要一些关于给定要求的解决方案的反馈。
情景A: 返回的对象将存储在一个变量中,该变量在其生命周期内无需修改。因此,
const Foo SomeClass::GetFoo() {
return Foo();
}
调用为:
someMethod() {
const Foo& l_Foo = someClassPInstance->GetFoo();
//...
}
Scneraio B: 返回的对象将存储在一个变量中,该变量将在其生命周期内进行修改。因此,
void SomeClass::GetFoo(Foo& a_Foo_ref) {
a_Foo_ref = Foo();
}
调用为:
someMethod() {
Foo l_Foo;
someClassPInstance->GetFoo(l_Foo);
//...
}
我在这里有一个问题:让我们说Foo不能有默认的构造函数。那么在这种情况下你会怎么处理呢,因为我们不能再写这个了:
Foo l_Foo
情景C:
Foo SomeClass::GetFoo() {
return Foo();
}
调用为:
someMethod() {
Foo l_Foo = someClassPInstance->GetFoo();
//...
}
我认为这不是推荐的方法,因为它会导致构建额外的临时工具。
你怎么看?另外,您是否建议使用更好的方法来处理此问题?答案 0 :(得分:16)
首先,让我们来看看这里发挥作用的事情:
(a)在用于初始化引用时延长临时的生命周期 - 我在this publication中由Andrei Anexandrescu了解到它。同样,它感觉很奇怪但很有用:
class Foo { ... }
Foo GetFoo() { return Foo(); } // returning temporary
void UseGetFoo()
{
Foo const & foo = GetFoo();
// ... rock'n'roll ...
foo.StillHere();
}
规则说当使用临时初始化引用时,临时的生命周期会延长,直到引用超出范围。 (this reply引用正典)
(b)返回值优化 - (wikipedia) - 本地的两个副本 - >返回值 - >在情况下可以省略本地 。这是一个令人惊讶的规则,因为它允许编译器改变可观察的行为,但很有用。
你有它。 C ++ - 奇怪但有用。
所以看看你的场景
场景A:您正在返回一个临时的,并将其绑定到引用 - 临时的生命周期延长到l_Foo的生命周期。
请注意,如果GetFoo
返回引用而不是临时引用,则无效。
场景B:工作,除了它强制一个构造 - 构造 - 复制周期(可能比单一构造要昂贵得多),以及你的问题提到需要一个默认的构造函数。
我不会使用该模式来创建对象 - 只是改变现有模式。
场景C:编译器可以省略临时副本(自RVO规则起)。遗憾的是没有保证 - 但现代编译器确实实现了RVO。
C ++ 0x中的Rvalue references允许Foo实现资源窃取构造函数,不仅可以保证副本的抑制,而且在其他场景中也可以派上用场。
(我怀疑有一个编译器实现rvalue引用而不是RVO。但是有些情况下RVO无法启动。)
这样的问题需要提及智能指针,例如shared_ptr
和unique_ptr
(后者是“安全”auto_ptr
)。他们也在C++ 0x。它们为创建对象的函数提供了另一种模式。
答案 1 :(得分:4)
在三种情景中,数字3是意识形态方法,也是您应该使用的方法。您不会支付额外的副本,因为编译器可以免费使用copy elision来避免副本。
Secnario A错了。你最终会得到一个对包含函数调用的语句完成时被销毁的临时文件的引用。好的,方案A没有错,但是你仍然应该使用方案C.
Secnario B工作得很好,但是:
让我们说Foo不能有默认的构造函数。那么在这种情况下你会怎么处理呢,因为我们不能再写这个了:
Foo l_Foo
。
Foo必须有一个默认的构造函数。即使你不给它一个,编译器也必须为你服务。假设您声明了private
构造函数,则无法使用此调用方法。您需要使Foo
默认可构造或使用Secnario C.