20

In my method a Player object is created like:

Player player(fullName,age);

My teacher gave us a piece of code with a constructor that takes a shared_ptr to a player object.

//constructor of the class
SomeClass(const std::shared_ptr<Socket> client, std::shared_ptr<Player> player)

Lets say we want to call the constructor of SomeClass and pass the player object we created on stack.

Is it ever safe/possible/good to create a shared_ptr from a stack object?

To make the question more understandable lets say we have two big code projects and we want to merge them so a method from one project is called from another one, should we rewrite all the files to use shared_ptr or stack objects exclusivly (for the methods that needs to be connected) or should we just create a shared_ptr to the stack object.

Why im not sure of the result:

What if the scope where the stackobject is created ends but the shared_ptr is still used and vise versa.

The stackobject gets deleted when out of scope or does it stay alive because there is still a reference to the object (in another class though)?

The shared_ptr goes out of scope and tries to delete the object, can it even though the stackobject is refering to it?

Note: I know I could just use the following and pass player

shared_ptr<Player> player{ new Player {fullName,age} };
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
Sven van den Boogaart
  • 11,833
  • 21
  • 86
  • 169
  • Possible duplicate of [Set shared\_ptr to point existing object](http://stackoverflow.com/questions/24049155/set-shared-ptr-to-point-existing-object) – Zereges Aug 09 '16 at 15:58
  • 1
    Side note if you are in c++11 or newer, it is a good habit to use `make_shared` when you can. in this case: `auto player = std::make_shared(fullName, age);` It is more efficient in most cases and safer. – Mitchell Tracy May 22 '17 at 14:35

6 Answers6

37

Is it ever safe/possible/good to create a smart_ptr from a stack object?

Safe? Only if you can guarantee that the stack which created that object will only be ended after all shared_ptr's that pseudo-own it.

Possible? Sure: pass shared_ptr's constructor a deleter object that does nothing:

auto sptr = shared_ptr<Player>(&player, [](Player *) {});

When the last shared_ptr is destroyed, the deleter will be called and nothing will be deleted.

Good? Not really. As noted above, safety is not something that can be universally guaranteed in such code. Depending on your code structure, this may be legitimate. But it requires great care.

This SomeClass is expecting to claim ownership of a resource; that's why it's taking a shared_ptr. You're kind of lying to it by passing it a shared_ptr that doesn't really own the object it references. That means the onus is on you and your code structure to not violate the promise you made to SomeClass that it would have shared control over that object's lifetime.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
10

The purpose of a shared pointer is to manage the lifetimes of dynamically created objects. As long as there is any shared pointer that points at an object, that object must still exist; when the last shared pointer that points at an object is destroyed, that object gets destroyed.

Stack objects have a fundamentally different lifetime: they exist until the code exits from the scope in which they were created, and then they are destroyed.

The two notions of lifetime are incompatible: there is no way a shared pointer can ensure that a stack object that has gone out of scope still exists.

So don't mix the two.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
6

Is it ever safe/possible/good to create a shared_ptr from a stack object?

I agree with @Nicolas Bolas that it is not safe. But it may be safe to create a shared_ptr from a copy of a stack object

shared_ptr<Player> playerPtr(new Player(player));

if Player is copy-able of course.

Semmel
  • 1,040
  • 10
  • 14
  • 1
    Why is copying not a more-accepted answer? I'm currently working with an object that can only be created using static functions that return an instance of an object. To my knowledge, the only way to get a shared pointer to this is to use `std::make_shared(Foo::create(args))` which I _believe_ would call Foo::create, returning a Foo on the stack, then copy-construct the Foo into a shared_ptr. – cdgraham Mar 18 '19 at 20:32
  • @cdgraham Becuase this answer is essentially unrelated to the question. OP already has the object constructed, why would he, in this situation, want to copy-construct another object? – FreelanceConsultant Dec 02 '22 at 15:52
  • @FreelanceConsultant "…why would he, in this situation, want to copy-construct another object?" In order to create a shared_ptr safe to work with – I guess? Anyway, darune's answer is even better: Don't copy, but `std::move(player)`. – Semmel Dec 03 '22 at 16:23
3

It's not safe to create a shared pointer to a stack object, because the stack object is due for destruction as soon as its containing function returns. Local objects are allocated and deallocated implicitly and automatically and trying to intervene is surely invoking many kinds of undefined behavior.

alter_igel
  • 6,899
  • 3
  • 21
  • 40
2

Use move semantics to create the shared_ptr

std::shared_ptr<Player> player_shared_ptr{ std::make_shared(std::move(player)) };

In this way, a copy is avoided. You may need to implement move constructor though on relevant classes for this approach to work. Most/all std objects support move semantics out of the box (eg. string, vector, etc.)

darune
  • 10,480
  • 2
  • 24
  • 62
1

Safe is a strong word. However, You can make the code safer by defining a StackObjectSharedPtr, forcing the shared_ptr instanciated type to include a "special" StackObjectDeleter

using PlayerStackSP = std::shared_ptr <Player, StackObjectDeleter> ;

class StackObjectDeleter {
public:
    void operator () (void*) const {}
};

Player player(fullName,age);
std::shared_ptr<PlayerStackSP, StackObjectDeleter> player(&player, StackObjectDeleter());

The StackObjectDeleter replaces the default_delete as the deleter object. default_delete simply calls delete (or delete []). In case of StackObjectDeleter, nothing will happen.

This is a step further of @Nicol Bolas's answer.

Daniel Heilper
  • 1,182
  • 2
  • 17
  • 34