How to return smart pointers (shared_ptr), by reference or by value?

Let's say I have a class with a method that returns shared_ptr .

What are the possible advantages and disadvantages of returning it by reference or by value?

Two possible hints:

  • Early destruction of the facility. If I return the shared_ptr by (const) link, the reference count does not increase, so I risk deleting the object when it goes out of scope in a different context (for example, another thread). It's right? What if the environment is single-threaded, can this situation also happen?
  • Cost. . Gradual value, of course, is not free. Should it be avoided when possible?

Thanks to everyone.

+58
c ++ return smart-pointers
May 17 '12 at 21:09
source share
2 answers

Return smart pointers by value.

As you said, if you return it by reference, you will not correctly increase the reference counter, which opens the risk of deleting something at the wrong time. This in itself should be sufficient reason for refusing the link. Interfaces must be reliable.

Cost concerns are currently troubling due to return value optimization (RVO), so you won’t follow the increment-increment-decrement sequence or something similar in modern compilers. Therefore, the best way to return shared_ptr is to simply return by value:

 shared_ptr<T> Foo() { return shared_ptr<T>(/* acquire something */); }; 

This is a dead-obvious RVO feature for modern C ++ compilers. I know that Visual C ++ compilers implement RVO even when all optimizations are disabled. And with the semantics of C ++ 11 relocation, this problem is even less relevant. (But the only way to be sure is to profile and experiment.)

If you are still not sure, Dave Abraham has an article in which argument is returned by value. Here I reproduce a fragment; I highly recommend you read the entire article:

Be honest: what does the following code look like:

 std::vector<std::string> get_names(); ... std::vector<std::string> const names = get_names(); 

Honestly, although I should know better, it makes me nervous. Basically, when get_names() returns, we should copy a vector from string s. Then we need to copy it again when we initialize names , and we need to destroy the first copy. If the vector has N string , each copy may require as many N + 1 memory allocations as an integer number of fuzzy cache access data> when copying the contents of a string.

Instead of confronting such an alarm, Ive often refused to pass by reference to avoid unnecessary copies:

 get_names(std::vector<std::string>& out_param ); ... std::vector<std::string> names; get_names( names ); 

Unfortunately, this approach is far from ideal.

  • Code increased by 150%
  • We had to abandon const -ness because there were mutating names.
  • As functional programmers like to remind us, mutation makes code more difficult to explain, undermining referential transparency and equational reasoning.
  • We no longer have strict semantics of meanings for names.

But is it really necessary to spoil our code in such a way as to increase efficiency? Fortunately, the answer turns out to be negative (and especially if you use C ++ 0x).

+73
May 17 '12 at 21:13
source share

As for any smart pointer (and not just shared_ptr), I don’t think it was ever acceptable to return a link to it, and I would very hesitate to pass them a link or a raw pointer. What for? Because you cannot be sure that it will not be copied from the link later. Your first point determines the reason why this should be a problem. This can happen even in a single-threaded environment. You do not need simultaneous access to data to put the semantics of bad copies into your programs. You really have no control over what your users do with the pointer when you pass it, so don't encourage abuse where your API users have enough rope to hang themselves.

Secondly, look at the implementation of a smart pointer, if possible. Construction and demolition must be damned close to insignificant. If this overhead is unacceptable, then do not use the smart pointer! But besides this, you will also need to study the concurrency architecture that you have, because mutually exclusive access to a mechanism that tracks pointer usage will slow you down than just creating a shared_ptr object.

Edit, 3 years later: with the advent of more modern functions in C ++, I would fine-tune my answer to take more cases when you just wrote a lambda that never lives outside the scope of the calling function and is not copied elsewhere. Here, if you want to keep the minimum overhead of copying a shared index, it will be fair and safe. What for? Because you can guarantee that this link will never be used incorrectly.

+17
May 17 '12 at 9:17 a.m.
source share



All Articles