Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

shared_ptr and unique_ptr conversion

Tags:

c++

I'm in the situation where I'm not sure which type of smart pointer to use because I'm not sure of all the use cases for my class. I could just use shared pointers but I'm not fond of the idea of passing shared pointers everywhere in my code when I don't necessarily need shared ownership. This article by Herb Sutter says that when in doubt, use unique_ptr and convert to shared_ptr when you have to. This is what I'd like to do but I'm unclear as to how this is supposed to be done, consider this example:

class Example
{
    public:
        Example(): _ptr(std::make_unique<Node>()) {}

        std::unique_ptr<Node>& getPtr() 
        {
            return _ptr;
        }

    private:
        // I am unsure if I will eventually need shared ownership or not
        std::unique_ptr<Node> _ptr;
};

Example* example = new Example();


// Some function somewhere
void f()
{
    // I've decided I need shared ownership, converting
    std::shared_ptr<Node> ptr(std::move(example->getPtr()));

    // Oops, example is no longer valid...
}

If someone has a better idea of how to deal with situations like this I'd be glad to hear it.

like image 836
navark Avatar asked May 01 '26 01:05

navark


1 Answers

I think you are asking a kind of optimization question. You want Example to use unique_ptr because it has simpler and more efficient semantics (paraphrasing your referenced article). But, when the need arises, you wish to allow the pointer to be converted to shared_ptr.

Example should simply provide an interface for that, and itself needs to convert from unique_ptr to shared_ptr, when its user invokes that interface. You could use state pattern to capture whether the instance is in unique_ptr mode or shared_ptr mode.

class Example
{
    struct StateUnique;
    struct StateShared;
    struct State {
        State (std::unique_ptr<State> &s) : _state(s) {}
        virtual ~State () = default;
        virtual Node & getPtr () = 0;
        virtual std::shared_ptr<Node> & getShared() = 0;
        std::unique_ptr<State> &_state;
    };
    struct StateUnique : State {
        StateUnique (std::unique_ptr<State> &s)
            : State(s), _ptr(std::make_unique<Node>()) {}
        Node & getPtr () { return *_ptr.get(); }
        std::shared_ptr<Node> & getShared() {
            _state = std::make_unique<StateShared>(*this);
            return _state->getShared();
        }
        std::unique_ptr<Node> _ptr;
    };
    struct StateShared : State {
        StateShared (StateUnique &u)
            : State(u._state), _ptr(std::move(u._ptr)) {}
        Node & getPtr () { return *_ptr.get(); }
        std::shared_ptr<Node> & getShared() { return _ptr; }
        std::shared_ptr<Node> _ptr;
    };
public:
    Example(): _state(std::make_unique<StateUnique>(_state)) {}
    Node & getNode() { return _state->getPtr(); }
    std::shared_ptr<Node> & getShared() { return _state->getShared(); }
private:
    std::unique_ptr<State> _state;
};

If the state machine looks scary (which it should, since it is over-engineered), then you can just maintain two pointers in the Example, and your methods which need to test which one it needs to use.

class Example
{
public:
    Example(): _u_node(std::make_unique<Node>()) {}
    Node & getNode() { return _u_node ? *_u_node.get() : *_s_node.get(); }
    std::shared_ptr<Node> & getShared() {
        if (_u_node) _s_node = std::move(_u_node);
        return _s_node;
    }
private:
    std::unique_ptr<Node> _u_node;
    std::shared_ptr<Node> _s_node;
};
like image 131
jxh Avatar answered May 02 '26 14:05

jxh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!