2

Suppose I have the following code, which is a simplified example of my problem:

#include <string>
#include <iostream>
#include <memory>


class A{
  public:
  std::string name() { return "class A"; }
};

class B{
  public:
  B(){
    m_a = std::make_shared<A>();
  }

  std::shared_ptr<A> get_a() { return m_a; }

  private:
  std::shared_ptr<A> m_a;
};

std::shared_ptr<A> foo()
{
  B b;
  return b.get_a();
}

int main()
{
  auto a = foo();
  auto name = a->name();
  std::cout << name;
  return 1;
}

I was wondering is it safe to do so? "b" which is an instance of B will be released at the end of function foo. "a" in the main function is a shared_ptr of B::m_a. Is it safe to use "a" after "b" is released?

Many thanks in advance!

Song
  • 397
  • 1
  • 5
  • 9
  • Maybe duplicates ? https://stackoverflow.com/questions/10643563/how-to-return-smart-pointers-shared-ptr-by-reference-or-by-value and https://stackoverflow.com/questions/974964/best-practice-when-returning-smart-pointers ? – coincoin Nov 08 '15 at 17:04
  • Please post compilable code. I.e. without syntax errors, with a correct `main` and with `#include`s. – Christian Hackl Nov 08 '15 at 17:19
  • I have read the two questions however I decided to post a new question because I want to make sure it is safe(or not safe) to do so event the "parent" class who hold the shared_ptr as member variable goes out of scope. – Song Nov 08 '15 at 17:21
  • 1
    If that wouldn't be safe then I'm not sure I understand the point of shared_ptr's in the first place. – juzzlin Nov 08 '15 at 17:28
  • Christian and decltype_auto, you are right, I just edited the code to make it compilable. – Song Nov 08 '15 at 17:31

3 Answers3

5

That foo

shared_ptr<A> foo()
{
  B b;
  return b.get_a();
}

does not return a reference to a b member, but a standonly copy (of a copy, if not RV optimized) of the member; and the shared_ptr a

auto a = foo();

saves the A heap instance from destruction until scope end.

Thus I consider this safe, the Bs, although creators, are after all just one user of possibly many of the shared assets they create and publish.

In other words - the last one turns off the light, not the one who turned it on.

live at Coliru

decltype_auto
  • 1,706
  • 10
  • 19
1

Yes, it's safe. If this was not safe, then std::shared_ptr would be pretty useless.

"b" which is an instance of B will be released at the end of function foo.

Yes, but its std::shared_ptr<A> is copied before that.

Let's look at what the last line of the function really does:

return b.get_a();

get_a() produces a std::shared_ptr<A>, which is copied to a temporary object. This temporary std::shared_ptr<A> object is then copied again to become the result of the call in main.

(Actual copies may even be elided due to return-value optimisation, but that doesn't change anything. If anything, it makes the safety even easier to understand.)

Only then is b destroyed, but the std::shared_ptr<A> in it has already been copied by that time.

The std::shared_ptr<A> instance within b is destroyed, too, but the memory for the A object is not freed. That's the whole point of std::shared_ptr -- it knows how often it was copied and frees dynamically allocated memory only when the last copy is destroyed.

"a" in the main function is a shared_ptr of B::m_a.

No. It's a std::shared_ptr<A>, but that's it. It has nothing to do with the original B::m_a anymore.

Is it safe to use "a" after "b" is released?

Yes, because there is no lasting relationship between a and b.


Basically, you are questioning the safety a fundamental feature of C++, namely returning values from functions. Returning a std::shared_ptr is not different with regards to safety than returning a std::string or an int. That's always what happens when you return by value from a function: The to-be-returned value is copied, the original is destroyed, the copy lives on.

Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
0

It is safe.

shared_ptr creates a tracked variable on the heap. This makes the object B more complex, as even when it is created on the stack, it allocates an A from the heap. Possibly impacting performance.

When all users of the tracked data have stopped accessing it, then the shared_ptr<> is deleted.

mksteve
  • 12,614
  • 3
  • 28
  • 50