C++ 返回对对象的常量引用而不是副本

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/134731/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 13:07:11  来源:igfitidea点击:

Returning a const reference to an object instead of a copy

c++const

提问by Rob

Whilst refactoring some code I came across some getter methods that returns a std::string. Something like this for example:

在重构一些代码时,我遇到了一些返回 std::string 的 getter 方法。例如这样的事情:

class foo
{
private:
    std::string name_;
public:
    std::string name()
    {
        return name_;
    }
};

Surely the getter would be better returning a const std::string&? The current method is returning a copy which isn't as efficient. Would returning a const reference instead cause any problems?

肯定吸气剂会更好地返回const std::string&? 当前方法正在返回一个效率不高的副本。返回 const 引用会导致任何问题吗?

采纳答案by Dima

The only way this can cause a problem is if the caller stores the reference, rather than copy the string, and tries to use it after the object is destroyed. Like this:

这可能导致问题的唯一方法是调用者存储引用而不是复制字符串,并在对象销毁后尝试使用它。像这样:

foo *pFoo = new foo;
const std::string &myName = pFoo->getName();
delete pFoo;
cout << myName;  // error! dangling reference

However, since your existing function returns a copy, then you would not break any of the existing code.

但是,由于您现有的函数返回一个副本,因此您不会破坏任何现有代码。

回答by Ogre Psalm33

Actually, another issue specificallywith returning a string notby reference, is the fact that std::stringprovides access via pointer to an internal const char*via the c_str()method. This has caused me many hours of debugging headache. For instance, let's say I want to get the name from foo, and pass it to JNI to be used to construct a jstring to pass into Java later on, and that name()is returning a copy and not a reference. I might write something like this:

实际上,另外一个问题特别是与返回的字符串通过引用,是这样的事实std::string提供了通过指针访问内部const char*经由c_str()方法。这让我在调试过程中头疼了好几个小时。例如,假设我想从 foo 中获取名称,并将其传递给 JNI 以用于构造一个 jstring 以便稍后传递给 Java,这name()将返回一个副本而不是一个引用。我可能会这样写:

foo myFoo = getFoo(); // Get the foo from somewhere.
const char* fooCName = foo.name().c_str(); // Woops!  foo.name() creates a temporary that's destructed as soon as this line executes!
jniEnv->NewStringUTF(fooCName);  // No good, fooCName was released when the temporary was deleted.

If your caller is going to be doing this kind of thing, it might be better to use some type of smart pointer, or a const reference, or at the very least have a nasty warning comment header over your foo.name() method. I mention JNI because former Java coders might be particularly vulnerable to this type of method chaining that may seem otherwise harmless.

如果您的调用者打算做这种事情,最好使用某种类型的智能指针或常量引用,或者至少在您的 foo.name() 方法上有一个令人讨厌的警告注释标题。我提到 JNI 是因为以前的 Java 编码人员可能特别容易受到这种在其他方面看起来无害的方法链的影响。

回答by paercebal

One problem for the const reference return would be if the user coded something like:

const 引用返回的一个问题是,如果用户编码如下:

const std::string & str = myObject.getSomeString() ;

With a std::stringreturn, the temporary object would remain alive and attached to str until str goes out of scope.

有了std::string回报,直到海峡进入的范围之临时对象将仍然活着,并连接到海峡。

But what happens with a const std::string &? My guess is that we would have a const reference to an object that could die when its parent object deallocates it:

但是一个会发生什么const std::string &?我的猜测是,当它的父对象释放它时,我们会有一个对可能死亡的对象的 const 引用:

MyObject * myObject = new MyObject("My String") ;
const std::string & str = myObject->getSomeString() ;
delete myObject ;
// Use str... which references a destroyed object.

So my preference goes to the const reference return (because, anyway, I'm just more confortable with sending a reference than hoping the compiler will optimize the extra temporary), as long as the following contract is respected: "if you want it beyond my object's existence, they copy it before my object's destruction"

所以我更喜欢 const 引用返回(因为,无论如何,我只是更喜欢发送引用而不是希望编译器优化额外的临时文件),只要遵守以下约定:“如果你想要它超越我的对象的存在,他们在我的对象销毁之前复制它”

回答by rlerallut

Some implementations of std::string share memory with copy-on-write semantics, so return-by-value can be almost as efficient as return-by-reference andyou don't have to worry about the lifetime issues (the runtime does it for you).

std::string 的一些实现使用 copy-on-write 语义共享内存,因此按值返回几乎与按引用返回一样有效,并且您不必担心生命周期问题(运行时给你)。

If you're worried about performance, then benchmark it(<= can't stress that enough) !!! Try both approaches and measure the gain (or lack thereof). If one is better and you really care, then use it. If not, then prefer by-value for the protection it offers agains lifetime issues mentioned by other people.

如果您担心性能,请对其进行基准测试(<= 强调不够)!!!尝试两种方法并测量增益(或缺乏)。如果一个更好,并且您真的很在意,那就使用它。如果没有,那么更喜欢按价值来保护它提供的其他人提到的终生问题。

You know what they say about making assumptions...

你知道他们对假设的看法...

回答by Frank

Okay, so the differencesbetween returning a copy and returning the reference are:

好的,所以返回副本和返回引用之间的区别是:

  • Performance: Returning the reference may or may not be faster; it depends on how std::stringis implemented by your compiler implementation (as others have pointed out). But even if you return the reference the assignment after the function call usually involves a copy, as in std::string name = obj.name();

  • Safety: Returning the reference may or may not cause problems (dangling reference). If the users of your function don't know what they are doing, storing the reference as reference and using it after the providing object goes out of scope then there's a problem.

  • 性能:返回引用可能更快也可能不会更快;这取决于std::string您的编译器实现是如何实现的(正如其他人指出的那样)。但即使您返回引用,函数调用后的赋值通常也涉及一个副本,如std::string name = obj.name();

  • 安全:返回引用可能会也可能不会导致问题(悬空引用)。如果您的函数的用户不知道他们在做什么,将引用存储为引用并在提供对象超出范围后使用它,那么就会出现问题。

If you want it fast and safeuse boost::shared_ptr. Your object can internally store the string as shared_ptrand return a shared_ptr. That way, there will be no copying of the object going and and it's always safe (unless your users pull out the raw pointer with get()and do stuff with it after your object goes out of scope).

如果你想快速安全地使用boost::shared_ptr。您的对象可以在内部将字符串存储为shared_ptr并返回一个shared_ptr. 这样,就不会复制对象,并且它始终是安全的(除非您的用户get()在对象超出范围后取出原始指针并对其进行处理)。

回答by Kristopher Johnson

I'd change it to return const std::string&. The caller will probably make a copy of the result anyway if you don't change all the calling code, but it won't introduce any problems.

我将其更改为返回 const std::string&。如果您不更改所有调用代码,调用者可能无论如何都会复制结果,但这不会引入任何问题。

One potential wrinkle arises if you have multiple threads calling name(). If you return a reference, but then later change the underlying value, then the caller's value will change. But the existing code doesn't look thread-safe anyway.

如果您有多个线程调用 name(),则会出现一种潜在问题。如果您返回一个引用,但随后更改了基础值,则调用者的值将更改。但是现有的代码无论如何看起来都不是线程安全的。

Take a look at Dima's answer for a related potential-but-unlikely problem.

看看 Dima 对相关的潜在但不太可能的问题的回答。

回答by Airsource Ltd

It is conceivable that you could break something if the caller really wanted a copy, because they were about to alter the original and wanted to preserve a copy of it. However it is far more likely that it should, indeed, just be returning a const reference.

可以想象,如果调用者真的想要副本,您可以破坏某些内容,因为他们即将更改原件并想要保留它的副本。然而,它更有可能实际上只是返回一个 const 引用。

The easiest thing to do is try it and then test it to see if it still works, provided that you have some sort of test you can run. If not, I'd focus on writing the test first, before continuing with refactoring.

最简单的方法是尝试一下,然后测试它是否仍然有效,前提是您有某种可以运行的测试。如果没有,我会先专注于编写测试,然后再继续重构。

回答by christopher_f

Does it matter? As soon as you use a modern optimizing compiler, functions that return by value will not involve a copy unless they are semantically required to.

有关系吗?一旦您使用现代优化编译器,按值返回的函数将不会涉及副本,除非它们在语义上需要。

See the C++ lite FAQon this.

请参阅C++ lite FAQ

回答by 17 of 26

Odds are pretty good that typical usage of that function won't break if you change to a const reference.

如果您更改为 const 引用,那么该函数的典型用法不会中断,可能性非常大。

If all of the code calling that function is under your control, just make the change and see if the compiler complains.

如果调用该函数的所有代码都在您的控制之下,只需进行更改并查看编译器是否会报错。

回答by Brett Hall

I normally return const& unless I can't. QBziZ gives an example of where this is the case. Of course QBziZ also claims that std::string has copy-on-write semantics which is rarely true today since COW involves a lot of overhead in a multi-threaded environment. By returning const & you put the onus on the caller to do the right thing with the string on their end. But since you are dealing with code that is already in use you probably shouldn't change it unless profiling shows that the copying of this string is causing massive performance problems. Then if you decide to change it you will need to test thouroughly to make sure you didn't break anything. Hopefully the other developers you work with don't do sketchy stuff like in Dima's answer.

我通常返回 const& 除非我不能。QBziZ 举例说明了这种情况。当然 QBziZ 还声称 std::string 具有写时复制语义,这在今天很少正确,因为 COW 在多线程环境中涉及大量开销。通过返回 const & 你把责任放在调用者身上,用他们的字符串做正确的事情。但是,由于您正在处理已经在使用的代码,因此您可能不应该更改它,除非分析表明该字符串的复制会导致大量的性能问题。然后,如果您决定更改它,则需要彻底测试以确保没有损坏任何东西。希望与您合作的其他开发人员不要像 Dima 的回答那样做粗略的事情。