简体   繁体   English

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

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

I am fairly new to C++ and I know of three ways of returning a local variable and all have their downsides: 我对C ++相当陌生,我知道三种返回局部变量的方法,但都有缺点:

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

Clearly not a good idea. 显然不是一个好主意。

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

No chance of a null pointer or dangling reference but a performance hit. 空指针或悬空引用没有机会,但会降低性能。

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

No chance of a null pointer but surely this violates the basic rules of OO design. 空指针没有机会,但是肯定会违反OO设计的基本规则。 Another object will have to delete this - but why should it have to? 另一个对象将不得不删除它-但是为什么必须删除它呢? The implemenation of the getPerson() method has nothing to do with it. getPerson()方法的实现与它无关。

So, I am looking for an alternative. 因此,我正在寻找替代方案。 I have heard of shared pointers and smart pointers (standard and Boost) but I'm not sure whether any of them are designed to deal with this problem. 我听说过共享指针和智能指针(标准指针和Boost指针),但是我不确定它们中的任何一个是否旨在解决此问题。 What do you guys suggest? 你们有什么建议?

Option #2: return by value. 选项2:按值返回。

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

There is no performance hit here. 这里没有性能受到影响。 This copy may be (and probably will be) elided by your compiler. 您的编译器可能会(并且可能会)删除此副本。 In fact, even if you turn off your compiler's copy elision optimizations, with a C++11 compiler this will be considered as a move first. 实际上,即使您关闭了编译器的复制删除优化,对于C ++ 11编译器,这也将被视为先行一步。

In fact, even if you then do Person p = getPerson() , which would normally involve two copies, both may be elided. 实际上,即使您随后执行Person p = getPerson()通常会涉及两个副本), 可能会忽略两者

See §12.9/31: 参见§12.9/ 31:

in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value 在具有类返回类型的函数的return语句中,当表达式是具有与函数返回类型相同的cv不合格类型的非易失性自动对象(函数或catch子句参数除外)的名称时,通过将自动对象直接构造到函数的返回值中,可以省略复制/移动操作

And §12.9/32: 和§12.9/ 32:

When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. 当满足或将要执行复制操作的省略标准时,除非源对象是函数参数,并且要复制的对象由左值指定,否则重载分辨率为复制选择了构造函数首先执行,就好像该对象是由右值指定的。

No chance of a null pointer or dangling reference but a performance hit. 空指针或悬空引用没有机会,但会降低性能。

Actually, no performance hit at all. 实际上,根本不影响性能。 See for example here: Want Speed? 参见示例: 想要速度吗? Pass by Value . 价值传递

Compiler can easily optimize that, with strategies called copy elision and the named return value optimization (check out the link for that). 编译器可以使用称为复制删除和命名返回值优化的策略轻松地对此进行优化(请参阅该链接)。

You shouldn't worry too much about a performance hit here: 您不必担心此处的性能下降:

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

The copy you are worried about will most likely be elided in what is called return value optimization (RVO) . 您担心的副本很可能会被隐藏在所谓的返回值优化(RVO)中 The C++ standard allows compilers to make this optimization, even if it breaks the as-if rule. C ++标准允许编译器进行此优化,即使它违反了as-if规则。 I haven't come across a compiler that wouldn't elide a copy in this kind of expression for a long time: 我很长一段时间都没有遇到过不会编译掉这种表达式的副本的编译器:

Person p = getPerson();

In C++11, even in the absence of copy elision, this would be a candidate for a move construction. 在C ++ 11中,即使没有复制省略,也可以作为移动构造的候选对象。 This could be an extremely cheap operation, but that really depends on the type in question. 可能是一个非常便宜的操作,但实际上取决于所讨论的类型。 In any case, copy elision is hard to avoid. 无论如何,都很难避免复制省略。

See this related post . 请参阅此相关文章

See this demo . 看到这个演示

As others have already pointed out, return value optimization helps minimize the performance hit from simply returning a value. 正如其他人已经指出的那样,返回值优化有助于最小化简单返回值所带来的性能损失。

Move semantics (new with C++11) can also help in this regard -- a return expression is pretty much the canonical example of an "xvalue", which is eligible to have its value moved from the source to the destination, rather than copied. 移动语义学(C ++ 11中的新增功能)在这方面也可以提供帮助-返回表达式几乎是“ xvalue”的典型示例,它有资格将其值从源移动到目标,而不是复制。 Especially for a type (eg, vector) that mostly consists of a pointer to the real data, this can be extremely beneficial, as it allows essentially a shallow copy instead of a deep copy (ie, instead of making a copy of the entire vector, it only ends up copying the pointer). 特别是对于主要由指向实际数据的指针组成的类型(例如向量),这可能是非常有益的,因为它本质上允许使用浅表副本而不是深表副本(即,而不是对整个向量进行复制) ,只会结束复制指针)。

A shared_ptr or unique_ptr can work here as well. shared_ptr或unique_ptr也可以在这里工作。 A shared_ptr is basically a reference counted pointer, so (pre-C++11) it lets you keep the object alive by just incrementing a reference count during the return process, then decrementing it again afterwards. shared_ptr本质上是一个引用计数的指针,因此(C ++ 11之前的版本)它使您可以通过在返回过程中仅增加引用计数,然后再将其递减来保持对象活动。 At least in a single-threaded environment, this is generally pretty cheap -- often cheaper than making a copy of the data. 至少在单线程环境中,这通常很便宜-通常比制作数据副本便宜。

A unique_ptr does roughly similar things, but without the overhead of incrementing and decrementing a reference count. unique_ptr做大致相似的事情,但是没有增加和减少引用计数的开销。 The basic difference is that instead of a making copying cheap, it moves the pointer to avoid doing a copy at all. 基本的区别在于,它使指针移动以完全避免进行复制,而不是使复制变得便宜。

Any of these can work, but pretty clearly the best of them in most cases is to just return the value (and if it makes sense, add a move constructor and/or move assignment operator to the type you're working with). 这些方法中的任何一种都可以使用,但是很明显,在大多数情况下,它们中的最佳方法是仅返回值(如果有意义,请向要使用的类型添加move构造函数和/或move赋值运算符)。

Local variables go out of scope - their lifetime ends - once the function execution is complete. 一旦函数执行完成,局部变量将超出范围-它们的生命周期结束。 Therefore, generally it is not a good idea to return references or pointers to local variables. 因此,通常不建议返回对局部变量的引用或指针。

What you might want to do is to return references or pointers to class member variables, which maintain their lifetime as long as the class object is in scope or has a valid lifetime. 您可能想做的是返回对类成员变量的引用或指针,只要类对象在作用域内或具有有效的生存期,它们就可以维持其生存期。

Should you ever need to return polymorphic objects, I recommend using unique pointers: 如果您需要返回多态对象,我建议使用唯一的指针:

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

I know I bang on about this a lot. 我知道我对此事很感兴趣。

Another alternative is to not return anything. 另一种选择是不返回任何内容。

Tell the object what to do: 告诉对象该怎么做:

  • display yourself, using this renderer 使用此渲染器显示自己
  • serialise yourself using this serialiser (implementation could be xml, database, json, network) 使用此序列化器序列化自己(实现可以是xml,数据库,json,网络)
  • update your state for this time 这次更新您的状态
  • decorate yourself with controls, using this control creator (creates sliders, dropdown lists, checkboxes, etc) 使用此控件创建者为控件装饰自己(创建滑块,下拉列表,复选框等)

No need for getters on the whole. 总体上不需要吸气剂。 Make efforts to avoid them and you'll find your designs pleasantly changed, testable, reasonable. 尽力避免它们,您会发现您的设计令人愉快地更改,可测试,合理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM