ned
ned

Reputation: 13

Creating unique ptrs from a raw ptr of another unique ptr

What happens when I create a unique pointer using the raw pointer of another unique pointer? In the example below, what would happen when create second class is called? I thought that first_obj_ would give up ownership when second_obj_ is created, but first_obj_ doesn't give up ownership when get is called.

class SecondClass {
     SecondClass(Object* obj) : second_obj_(obj) {}
     .....
     private:
          std::unique_ptr<Obj> second_obj_;
}

class FirstClass {
     FirstClass() {
          std::unique_ptr<Obj> first_obj_ = std::make_unique<Obj>();
      }
      std::unique_ptr<SecondClass> CreateSecondClass() {
           std::make_unique<Obj>(first_obj_.get());      
       }
     .....
     private:
          std::unique_ptr<Obj> first_obj_;
          SecondClass second_class;
}

Upvotes: 0

Views: 2107

Answers (2)

Yksisarvinen
Yksisarvinen

Reputation: 22304

You should use release() if you want to give up ownership.

std::unique_ptr cannot know what you will do with the pointer extracted with get() and in particular it cannot know that you decided to give it to another std::unique_ptr.

If two std::shared_ptrs will store the same pointer, you will get double delete of the object, which is Undefined Behaviour.


However, if you want to pass ownership from one std::unique_ptr to another, you should probably use move semantics instead:

class Obj {};

struct SecondClass {
    SecondClass(std::unique_ptr<Obj>&& obj) : second_obj_(std::move(obj)) {}
private:
    std::unique_ptr<Obj> second_obj_;
};

struct FirstClass {
    FirstClass() {
        first_obj_ = std::make_unique<Obj>();
    }

    std::unique_ptr<SecondClass> CreateSecondClass() {
        return std::make_unique<SecondClass>(std::move(first_obj_));  
        //note that first_obj_ points to nullptr now. Only SecondClass owns the Obj.
    }
private:
    std::unique_ptr<Obj> first_obj_;
};

Upvotes: 1

Remy Lebeau
Remy Lebeau

Reputation: 596713

What happens when I create a unique pointer using the raw pointer of another unique pointer?

BAD THINGS CAN HAPPEN!

You end up with 2 unique_ptr objects that both think they are the exclusive owner of the raw pointer, and so both objects will try to manage and ultimately free the owned memory. To avoid that, you have to pass ownership from one unique_ptr to the other, or else give at least one of the unique_ptrs a custom deleter that does not free the memory (in which case, you shouldn't be using 2 unique_ptr objects to begin with).

In the example below, what would happen when create second class is called?

Both first_obj_ and second_obj_ end up holding pointers to the same Object in memory, and both of them will try to delete it.

I thought that first_obj_ would give up ownership when second_obj_ is created

No, because unique_ptr::get() simply returns a copy of the raw pointer, it does not release ownership of the pointer. If you want that, use unique_ptr::release() instead:

class SecondClass {
public:
    SecondClass(Object* obj) : second_obj_(obj) {}
    ...
private:
    std::unique_ptr<Object> second_obj_;
};

class FirstClass {
public:
    FirstClass() : first_obj_(std::make_unique<Object>()) {}

    std::unique_ptr<SecondClass> CreateSecondClass() {
        return std::make_unique<SecondClass>(first_obj_.release());
    }
    ...
private:
    std::unique_ptr<Object> first_obj_;
};

Otherwise, you can simply std::move one unique_ptr into the other:

class SecondClass {
public:
    SecondClass(std::unique_ptr<Object> obj) : second_obj_(std::move(obj)) {}
    ...
private:
    std::unique_ptr<Object> second_obj_;
};

class FirstClass {
public:
    FirstClass() : first_obj_(std::make_unique<Object>()) {}

    std::unique_ptr<SecondClass> CreateSecondClass() {
        return std::make_unique<SecondClass>(std::move(first_obj_));
    }
    ...
private:
    std::unique_ptr<Object> first_obj_;
};

If you truly want FirstClass and SecondClass to share the same pointer, then use std::shared_ptr instead of std::unique_ptr:

class SecondClass {
public:
    SecondClass(std::shared_ptr<Object> obj) : second_obj_(obj) {}
    ...
private:
    std::shared_ptr<Object> second_obj_;
};

class FirstClass {
public:
    FirstClass() : first_obj_(std::make_shared<Object>()) {}

    std::unique_ptr<SecondClass> CreateSecondClass() {
        return std::make_unique<SecondClass>(first_obj_);
    }
    ...
private:
    std::shared_ptr<Object> first_obj_;
};

Upvotes: 2

Related Questions