我是C ++的新手,我知道有三种返回局部变量的方法,并且都有其缺点:
Person& getPerson()
{
Person bob;
return bob;
}
显然不是一个好主意。
Person getPerson()
{
Person bob;
return bob;
}
没有空指针或悬空引用的可能性,但是性能命中。
Person* getPerson()
{
return new Person();
}
没有空指针的可能性,但这肯定违反了OO设计的基本规则。另一个对象将不得不删除 - 但为什么要这样做? getPerson()方法的实现与它无关。
所以,我正在寻找另一种选择。我听说过共享指针和智能指针(标准和Boost),但我不确定它们中是否有任何一个是为解决这个问题而设计的。你们有什么建议?
答案 0 :(得分:6)
选项#2:按值返回。
Person getPerson()
{
Person bob;
return bob;
}
这里没有任何表现。您的编译器可能会(可能会)删除此副本。事实上,即使您使用C ++ 11编译器关闭编译器的复制省略优化,也会首先将其视为移动。
事实上,即使你做了Person p = getPerson()
,通常也会涉及两个副本,两个都可能被删除。
见§12.9/ 31:
在具有类返回类型的函数中的
return
语句中,当表达式是具有相同cv-unqualified的非易失性自动对象(函数或catch子句参数除外)的名称时键入函数返回类型,通过将自动对象直接构造为函数的返回值,可以省略复制/移动操作
§12.9/ 32:
当满足或将满足复制操作的省略标准时,除了源对象是函数参数,并且要复制的对象由左值指定,重载决策以选择构造函数首先执行复制,就像对象是由右值指定一样。
答案 1 :(得分:4)
没有空指针或悬空引用的可能性,但是性能命中。
实际上,根本没有表现。 例如,请参见:Want Speed? Pass by Value。
编译器可以轻松地优化它,使用称为复制省略和命名返回值优化的策略(查看链接)。
答案 2 :(得分:3)
你不应该过分关注这里的表现:
Person getPerson()
{
Person bob;
return bob;
}
您担心的副本很可能会被称为return value optimization (RVO)。 C ++标准允许编译器进行此优化,即使它打破了 as-if 规则。我没有遇到过编译器长时间不会在这种表达式中删除副本:
Person p = getPerson();
在C ++ 11中,即使没有复制省略,这也是移动构建的候选者。这个可能是一个非常便宜的操作,但这实际上取决于所讨论的类型。无论如何,复制省略很难避免。
请参阅this demo。
答案 3 :(得分:2)
正如其他人已经指出的那样,返回值优化有助于最大限度地减少简单返回值所带来的性能损失。
移动语义(C ++ 11的新特性)在这方面也有帮助 - 返回表达式几乎是“xvalue”的规范示例,它有资格将其值从源移动到目标而不是复制。特别是对于主要由指向真实数据的指针的类型(例如,向量),这可能非常有益,因为它基本上允许浅拷贝而不是深拷贝(即,而不是制作整个矢量的副本,它最终只会复制指针。
shared_ptr或unique_ptr也可以在这里工作。 shared_ptr基本上是一个引用计数指针,因此(pre-C ++ 11)它允许您通过在返回过程中递增引用计数来保持对象处于活动状态,然后再次递减它。至少在单线程环境中,这通常非常便宜 - 通常比制作数据副本便宜。
unique_ptr执行大致相似的操作,但没有增加和减少引用计数的开销。基本的区别在于,它不是简单地复制,而是移动指针以避免复制。
其中任何一个都可以工作,但很明显在大多数情况下最好的只是返回值(如果有意义的话,添加一个移动构造函数和/或将赋值运算符移动到你正在使用的类型)。
答案 4 :(得分:0)
一旦函数执行完成,局部变量就会超出范围 - 它们的生命周期结束。因此,通常返回引用或指向局部变量的指针并不是一个好主意。
您可能想要做的是返回类成员变量的引用或指针,只要类对象在范围内或具有有效的生命周期,它们就会保持其生命周期。
答案 5 :(得分:0)
如果您需要返回多态对象,我建议使用唯一指针:
std::unique_ptr<Person> getPerson()
{
return std::unique_ptr<Person>(new Programmer);
}
答案 6 :(得分:0)
我知道我经常谈论这件事。
另一种选择是不返回任何东西。
告诉对象该做什么:
总体上不需要吸气剂。努力避免它们,你会发现你的设计变得愉快,可测试,合理。