C++中返回局部变量的引用和标准指针的替代方案是什么

What are the alternatives to references and standard pointers for returning local variables in C++?

本文关键字:指针 是什么 方案 标准 返回 局部变量 引用 C++      更新时间:2023-10-16

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

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

显然不是个好主意。

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

没有空指针或悬空引用的机会,但性能会受到影响。

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

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

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

选项#2:按值返回。

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

这里没有性能上的冲击。此副本可能会(也可能会)被编译器删除。事实上,即使您关闭了编译器的副本省略优化,对于C++11编译器来说,这也将被视为首先采取的行动。

事实上,即使您执行Person p = getPerson()(通常会涉及两个副本),这两个也可能被忽略。

参见§12.9/31:

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

以及§12.9/32:

当满足或将满足省略复制操作的标准时,除非源对象是函数参数,并且要复制的对象是由左值指定的,否则首先执行重载解析以选择复制的构造函数,就好像对象是由右值指定的一样。

没有空指针或悬空引用的机会,但性能会受到影响。

事实上,一点表演都没有成功。例如,请参阅此处:想要速度?传递值。

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

您不必太担心这里的性能问题:

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

您担心的副本很可能会在所谓的返回值优化(RVO)中被忽略。C++标准允许编译器进行这种优化,即使它打破了规则,就好像一样。我很久没有遇到过一个编译器不会在这种表达式中删除副本:

Person p = getPerson();

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

请参阅此相关帖子。

请参阅此演示。

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

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

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

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

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

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

您可能想要做的是返回对类成员变量的引用或指针,只要类对象在作用域中或具有有效的生存期,这些变量就会保持其生存期。

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

std::unique_ptr<Person> getPerson()
{
    return std::unique_ptr<Person>(new Programmer);
}

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

另一种选择是不归还任何东西。

告诉对象该做什么:

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

总的来说,不需要吸气器。努力避免它们,你会发现你的设计发生了令人愉快的变化,可测试,合理。