在C ++中返回局部变量的引用和标准指针有哪些替代方法?

时间:2013-02-26 20:44:40

标签: c++ pointers boost reference smart-pointers

我是C ++的新手,我知道有三种返回局部变量的方法,并且都有其缺点:

Person& getPerson()
{
   Person bob;
   return bob;
}

显然不是一个好主意。

Person getPerson()
{
   Person bob;
   return bob;
}

没有空指针或悬空引用的可能性,但是性能命中。

Person* getPerson()
{
   return new Person();
}

没有空指针的可能性,但这肯定违反了OO设计的基本规则。另一个对象将不得不删除 - 但为什么要这样做? getPerson()方法的实现与它无关。

所以,我正在寻找另一种选择。我听说过共享指针和智能指针(标准和Boost),但我不确定它们中是否有任何一个是为解决这个问题而设计的。你们有什么建议?

7 个答案:

答案 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 related post

请参阅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)

我知道我经常谈论这件事。

另一种选择是不返回任何东西。

告诉对象该做什么:

  • 使用此渲染器显示您自己
  • 使用此序列化程序自行序列化(实现可以是xml,数据库,json,网络)
  • 此时更新您的州
  • 使用此控件创建器(创建滑块,下拉列表,复选框等)使用控件装饰自己

总体上不需要吸气剂。努力避免它们,你会发现你的设计变得愉快,可测试,合理。