0

我对 C++ 相当陌生,我知道返回局部变量的三种方法,它们都有其缺点:

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

显然不是一个好主意。

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

没有机会出现空指针或悬空引用,但会影响性能。

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

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

所以,我正在寻找替代方案。我听说过共享指针和智能指针(标准和 Boost),但我不确定它们中的任何一个是否旨在处理这个问题。你们有什么建议?

4

7 回答 7

6

选项#2:按值返回。

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

这里没有性能受到影响。您的编译器可能(并且可能会)忽略此副本。事实上,即使您关闭编译器的复制省略优化,对于 C++11 编译器,这也将被视为先行一步。

事实上,即使你这样做Person p = getPerson(),通常会涉及两个副本,两者都可能被省略。

见§12.9/31:

return具有类返回类型的函数的语句中,当表达式是具有与函数返回类型相同的 cv 非限定类型的非易失性自动对象(函数或 catch 子句参数除外)的名称时,可以通过将自动对象直接构造到函数的返回值中来省略复制/移动操作

和§12.9/32:

当满足或将满足复制操作的省略标准时,除了源对象是函数参数的事实,并且要复制的对象由左值指定时,选择复制的构造函数的重载决策是首先执行好像对象是由右值指定的。

于 2013-02-26T20:46:20.390 回答
4

没有机会出现空指针或悬空引用,但会影响性能。

实际上,根本没有性能受到影响。例如,请参见此处:想要速度?按值传递

编译器可以通过称为复制省略和命名返回值优化的策略轻松优化它(查看链接)。

于 2013-02-26T20:45:50.743 回答
3

您不必太担心这里的性能影响:

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

您担心的副本很可能会在所谓的返回值优化 (RVO)中被忽略。C++ 标准允许编译器进行这种优化,即使它违反了as-if规则。很长一段时间以来,我都没有遇到过不会在这种表达式中省略副本的编译器:

Person p = getPerson();

在 C++11 中,即使没有复制省略,这也是移动构造的候选者。这可能是一个非常便宜的操作,但这实际上取决于所讨论的类型。无论如何,复制省略是难以避免的。

请参阅此相关帖子

请参阅此演示

于 2013-02-26T20:46:08.973 回答
2

正如其他人已经指出的那样,返回值优化有助于最大限度地减少简单返回值对性能的影响。

移动语义(C++11 新增)在这方面也有帮助——返回表达式几乎是“xvalue”的规范示例,它有资格将其值从源移动到目标,而不是复制。特别是对于主要由指向真实数据的指针组成的类型(例如向量),这可能是非常有益的,因为它本质上允许浅拷贝而不是深拷贝(即,而不是制作整个向量的副本,它只会复制指针)。

shared_ptr 或 unique_ptr 也可以在这里工作。shared_ptr 基本上是一个引用计数指针,因此(C++11 之前的版本)它允许您通过在返回过程中增加引用计数,然后再减少它来保持对象的活动状态。至少在单线程环境中,这通常相当便宜——通常比制作数据副本更便宜。

unique_ptr 做了大致相似的事情,但没有增加和减少引用计数的开销。基本区别在于,它不是使复制变得便宜,而是移动指针以完全避免进行复制。

其中任何一个都可以工作,但很明显,在大多数情况下,它们中最好的就是只返回值(如果有意义,添加一个移动构造函数和/或移动赋值运算符到你正在使用的类型)。

于 2013-02-26T20:57:02.627 回答
0

一旦函数执行完成,局部变量就会超出范围 - 它们的生命周期结束。因此,通常返回对局部变量的引用或指针并不是一个好主意。

您可能想要做的是返回对类成员变量的引用或指针,只要类对象在范围内或具有有效的生命周期,它们就会保持它们的生命周期。

于 2013-02-26T20:49:36.920 回答
0

如果您需要返回多态对象,我建议使用唯一指针:

std::unique_ptr<Person> getPerson()
{
    return std::unique_ptr<Person>(new Programmer);
}
于 2013-02-26T21:23:52.723 回答
0

我知道我经常谈论这个。

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

告诉对象要做什么:

  • 显示自己,使用这个渲染器
  • 使用这个序列化器序列化自己(实现可以是 xml、数据库、json、网络)
  • 这次更新你的状态
  • 用控件装饰自己,使用这个控件创建器(创建滑块、下拉列表、复选框等)

整体上不需要吸气剂。努力避免它们,你会发现你的设计令人愉快地改变、可测试、合理。

于 2013-02-26T22:02:07.437 回答