Returning a const reference to an object instead of a copy

前端 未结 12 560
天命终不由人
天命终不由人 2020-11-29 16:40

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

class foo
{
private:
    std::st         


        
相关标签:
12条回答
  • Actually, another issue specifically with returning a string not by reference, is the fact that std::string provides 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:

    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.

    0 讨论(0)
  • 2020-11-29 17:23

    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.

    Edit: Modern C++ (i. e. C++11 and up) supports Return Value Optimization, so returning things by value is no longer frowned upon. One should still be mindful of returning extremely large objects by value, but in most cases it should be ok.

    0 讨论(0)
  • 2020-11-29 17:23

    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.

    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.

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

    0 讨论(0)
  • 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 FAQ on this.

    0 讨论(0)
  • 2020-11-29 17:26

    Okay, so the differences between returning a copy and returning the reference are:

    • Performance: Returning the reference may or may not be faster; it depends on how std::string is 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.

    If you want it fast and safe use boost::shared_ptr. Your object can internally store the string as shared_ptr and 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).

    0 讨论(0)
  • 2020-11-29 17:30

    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.

    0 讨论(0)
提交回复
热议问题