参考或返回 - 最佳实践
例如我们有编码功能。使用的最佳实践是什么:
void Crypto::encoding(string &input, string &output)
{
//encoding string
output = encoded_string;
}
或者
string Crypto::encoding(string &input)
{
//encoding string
return encoded_string;
}
我们应该使用引用还是返回来返回字符串?据我所知,返回字符串需要一些时间来初始化将由返回指令返回的新字符串。当处理引用的变量时,我不会浪费时间来初始化一些新变量,我只是结束函数。
我们应该主要使用引用并使函数返回类型为 void 吗?或者,当我们想要返回两个或多个变量时,我们应该只通过引用返回数据,而当我们需要返回一个变量时,然后使用 return 指令?
For example we have encoding function. What is the best practice to use:
void Crypto::encoding(string &input, string &output)
{
//encoding string
output = encoded_string;
}
or
string Crypto::encoding(string &input)
{
//encoding string
return encoded_string;
}
Should we use reference or return for returning the string? As far as I know returning a string will take some time to initialize a new string that will be returned by return instruction. When working on a referenced variable I don`t waste time to initialize some new variable I just end the function.
Should we mostly use reference and make function return type void? Or we should only return data by reference when we want to return two or more variables and when we need return one variable then use return instruction?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
不要优化你没有衡量的东西。
通常,使用
return
返回计算结果会更好(更具可读性)。如果由于对象太胖而花费很长时间,您仍然可以恢复通过参考参数返回结果,但前提是您证明这将导致性能显着提高(测量它)。例如,如果您只对非常短的字符串进行编码,并且只是偶尔进行一次,则复制的开销可以忽略不计。Do not optimize what you did not measure.
Usually it is better (more readable) to return the result of your computation with
return
. If this takes to long because the object is too fat, you can still revert to return your result via reference parameters, but only after you proved that this will result in significant improvement of performance (measure it). E.g if you only ever encode very short strings and only do that once in a while, the overhead of copying is negligible.由于大多数现代编译器都具有 RVO 功能,因此通常会消除复制内容。即使没有 c++11,您也可以受益。
Copying things are usually eliminated since most modern compilers have RVO features. You can take the benefits even without c++11.
如果您的编译器支持 C++11 标准和r 值引用,那么按值返回 std::string 实际上非常有效。在此功能之前,答案可能会有所不同,因为您仅依赖于执行 RVO< 的编译器/a>.
我想说,使用返回值可能更自然,也意味着您可以将结果分配给常量局部变量或类成员以避免意外修改,例如
或者
只是确保您不返回通过 const 值,因为这会抑制移动语义。
If your compiler supports the C++11 standard and r-value references then returning a std::string by value is actually quite efficient. Prior to this feature the answers might have been a little different since you'd be relying only on the compiler performing RVO.
I would say that using a return value is probably more natural and also means you can assign the result to a constant local variable or class member to avoid accidental modification, e.g.
Or
Just ensure that you don't return by const value as this will inhibit move semantics.
我使用参考资料。这允许实现者做出并抽象选择,而不会给客户带来沉重的负担(有些情况很重要,有些则不会)。
我还使用它们来保持一致的风格 - 我不喜欢看到公共接口传递其实现的细节。
瞬态和副本可能很昂贵 - 它根据您传递的类型而有很大差异。按值返回表示类型应该是可简单构造的、可交换的、可复制的、可移动的。编译器可以在该领域(RVO/移动)进行一些重大优化,但您也可以做出明智的决策,以最大限度地减少实现中昂贵的操作。一旦您不再使用每个人都知道其复制特征的类型,那么选择如何返回就会变得非常复杂,所以我只是保持简单并倾向于引用。
传递引用还有一些其他好处,例如当客户端更喜欢使用所传递类型的子类时。
如果您需要优化的程序,另一个好处是:如果复制构造函数和operator= 不是微不足道的或不可能的,我通常会删除它们。通过可变引用传递允许您使用不可复制/可分配的类型。
在这个问题中使用的
std::string
的严格范围内:按值返回std::string
是很常见的,并且专门针对这种情况进行了许多优化- RVO、COW 和移动是一些值得注意的。正如 Voo 在下面的评论中提到的,按值返回通常更容易阅读。对于 std::string 和更高级别的程序,按值返回不太可能成为问题,但为了了解您所执行的标准库实现所涉及的成本,进行测量很重要如果性能很重要(您的问题表明可能是这种情况),则使用。一个重要的考虑因素是,如果您试图改进现有程序,请确保您了解实现的执行方式,并了解在性能很重要时如何最有效地使用类型。实现可能是针对实际使用而编写和优化的,这意味着在某些情况下它们可能会持悲观态度并猜测您,并且您提高性能的尝试可能已经实现,或者该类型的非常规使用可能< em>降低性能。
std::vector
的典型调整大小行为就是一个明显的例子。走高性能道路确实会增加大量时间和复杂性,这需要了解如何才能获得最佳结果,这显然会因您使用的实现以及所使用的类型而异。如果性能至关重要并且值得投入大量时间,那么学习您所使用的类型的操作是一项值得付出的努力,可以带来显着的收益。我还应该补充一点,我经常在低级别工作 - 性能至关重要和/或资源有限。可能有很多限制,包括没有异常、没有锁(也意味着没有堆分配)、最小抽象成本,甚至限制动态多态性的使用。即使对于 C++ 来说,它也可以被认为是一个要求相当高的领域。我选择核心低级部分的参考,但如果我知道程序将仅在更高级别的域或单元测试中使用,我会放宽该规则。
I use references. That allows the implementor to make and abstract the choice, without taxing the client heavily (some cases matter, some will not).
I also use them for consistent style - I don't like seeing public interfaces that pass by details of their implementation.
The transients and copies can be expensive - it varies greatly by the type you are passing. To return by value indicates the type should be trivially constructible, swappable, copyable, movable. The compiler can make some great optimizations in this area (RVO/move), but you can also make informed decisions to minimize expensive operations in your implementations. Once you're no longer using types everybody knows the copy characteristics for, then choosing how to return becomes very complex, so I just keep it simple and favor references.
Passing a reference has a few other benefits, such as when the client prefers to use a subclass of the type that is passed.
Another benefit if you need an optimized program: I will often delete the copy ctor and
operator=
if they are not trivial or possible. Passing by mutable reference allows you to work with types which are not copy/assignable.In the strict scope of
std::string
used in this question: Returning astd::string
by value is quite common, and many optimizations have been made specifically for this case - RVO, COW, and moves are some notable ones. As Voo mentioned in the comments below, returning by value is often easier to read. In the case ofstd::string
and higher level programs, returning by value is not likely to be an issue, but it's important to measure in order to understand the costs involved for the standard library implementations you are using if performance is important (which your question suggests may be the case).An important consideration is that if you are trying to improve upon an existing program, be sure you understand how the implementation executes, and learn how you can use the types most effectively when performance is important. Implementations may be written and optimized for actual use, which means that they may be pessimistic and second guessing you in some cases, and your attempts to improve performance may already be implemented or unconventional use of the type may degrade performance. Typical resizing behaviour of a
std::vector
is an obvious example. Taking the high performance road does add a lot of time and complexity regarding what you need to know to achieve the best results, and this obviously varies by implementations you use and also by the types you are using. If performance is critical and worth nontrivial time investments, learning the operation of the types you use is a worthwhile endeavor which can lead to significant gains.I should also add that I work in the low levels quite often - Where performance is critical and/or resources are limited. There can be many restrictions, including no exceptions, no locks (also implies no heap allocations), minimal abstraction costs, and even restricted use of dynamic polymorphism. It can be considered a fairly demanding domain, even for C++. I choose reference for the core low level pieces, but I will relax that rule if I know a program will be used only in higher level domains or unit tests.
在新标准 C++11 中,由于新的移动语义,您可以使用第二种变体。
不过,最有可能的是,您的编译器仍然只支持较旧的标准。
在这种情况下,您的第一个示例不会引起任何复制并且更好。
With the new standard C++11, you can use the second variant because of the new move semantics.
Most probably, though, your compiler still only supports an older standard.
In this case, your first example does not provoke any copying and is better.
我将郑重声明:可能两者都不是。
在我看来,您的
encode
看起来很像它可以/应该是一种通用算法,应该真正使用迭代器,而不是直接处理字符串。通过这种方式,您可以(例如)轻松地重复使用完全相同的代码,将数据直接从输入流(通过 std::istream_iterator)编码到输出流(通过 std ::ostream_iterator)。
这通常也消除了大多数有关效率的问题。
I'll go on record as saying: probably neither one.
Your
encode
looks to me a lot like it could/should be a generic algorithm that should really use iterators instead of dealing directly with strings at all.This way you can (for example) easily re-use exactly the same code to encode data directly from an input stream (via an
std::istream_iterator
) to an output stream (via anstd::ostream_iterator
).This also generally eliminates most questions about efficiency.
我更喜欢第二个版本,因为它看起来更像是一个数学函数。如果您只返回字符串,那么您应该具有良好的性能。
I like the second version better, because it looks more like a mathematical function. If you are only returning string you should be good performance wise.