Rashida
Rashida

Reputation: 91

Object destruction when function getting ownership of unique_ptr throws exception

If function Foo() transfers ownership of std::unique_ptr to function Bar() and say Bar() throws an exception, object contained in std::unique_ptr will get destroyed.

How can one handle a case where Foo() might want to keep the ownership with itself in such cases.

class A
{
public:
    std::unique_ptr<SomeData> getSomeData(...);
};

class B
{
public:
    pushToQ(std::unique_ptr<SomeData>);
    doSomething()
    ...
    popFromQ();
    ...
};

Now if B::pushToQ() throws QueueFullException I will loose the data generated by getSomeData() which I might not want.

Upvotes: 0

Views: 453

Answers (4)

Barry
Barry

Reputation: 302767

You're either transferring ownership - or you're not. If you want to transfer, then you shouldn't care if Bar can throw and kill your object:

// i do not care what bar does with ptr, i am done with it
bar(std::move(ptr));

If you might want to retain ownership maybe, then transferring ownserhip is the wrong solution. Either you want to pass your unique_ptr by reference, or maybe just pull out the raw pointer, or maybe even just use a shared_ptr. It depends on your use-case. But there's no half-way ownership transfer.

Here are some examples. Which is preferred is entirely up to you:

bar(std::unique_ptr<Data>& ptr) {
    // not transferring, caller still owns the data
    something_that_might_throw();
    something_that_might_not();

    // ok got here?
    std::unique_ptr<Data> myPtr = std::move(ptr);
    // MINE NOW!!!
}

The above is "the ONLY exception safe solution to moving a unique_ptr into a container in an exception safe manner with a strong guarantee that the system won't be corrupted" (from MGetz)

Or:

bar(Data* ptr) {
    // it really doesn't matter, caller always owns the data
    // just don't go doing something like this

    std::unique_ptr<Data> awful_idea(ptr);

    // now two separate people both think they own the data
    // and will both try to delete it. That'll work the first time...
}

Or a strictly better version so you can't mess it up unless you try really hard

bar(Data& data) {
    // well clearly the caller owns it
}

Or:

bar(std::shared_ptr<Data> ptr) {
    // we both share the data
    // it won't get deleted until both bar() and caller delete it
}

Upvotes: 6

Casey
Casey

Reputation: 42554

In the called function, store the unique_ptr in the thrown exception object. If the callee - or some other function up the call stack - wants to save it, it can then be extracted. If not, it will be safely destroyed.

Upvotes: 1

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385104

Then don't transfer the ownership.

Pass a SomeData& into Bar(), instead.

Upvotes: 2

Slava
Slava

Reputation: 44238

You can use std::shared_ptr instead of std::unique_ptr and release your instance if pushToQ() succeded, keep your copy otherwise.

Upvotes: 1

Related Questions