If a function returns a std::unique_ptr, is it reasonable to assume each returned value is to a new object?

621 views Asked by At

I'm learning about std::unique_ptr, trying to grok what it represents.

Given a function (out of my control) that returns a unique_ptr, is it implied/well understood that each invocation returns a unique_ptr that points to a new object (different than any prior invocation)?

By way of example, the following code produces a double-free on exit, and I hope that I correctly understand why: unique_ptrs delete their underlying object on destruction; therefore two unique_ptrs encapsulating the same memory/object would cause a double-free on destruction of the second. Therefore, would the following implementation of function getUniquePtr() be commonly/implicitly understood to be unreasonable?

// main.cpp
#include <memory>
#include <iostream>

std::unique_ptr<int> getUniquePtr() {
  static int* p = new int(42);
  return std::unique_ptr<int>(p);
}

class PtrOwner {
public:
  std::unique_ptr<int> p_;
};

int main( int argc, char* argv[] ) {
  PtrOwner po1;
  PtrOwner po2;
  po1.p_ = getUniquePtr();
  po2.p_ = getUniquePtr();

  return 0;
}
2

There are 2 answers

0
Brian Bi On BEST ANSWER

It should be assumed that, if a function returns std::unique_ptr<T>, then the returned smart pointer points to an object that is not currently managed by anyone else. This does not necessarily mean that it always refers to a different object. So long as this convention is followed, double-free bugs will be avoided. If this convention is violated, double-free bugs will occur.

For example, if you see some function like this:

std::unique_ptr<T> foo(std::unique_ptr<T> arg);

This function might, potentially, return std::move(arg) under some circumstances or it might destroy arg and return some other pointer. (You have to read the documentation to know what it does). This implies that you could do something like this:

auto t = std::make_unique<T>();
t = foo(std::move(t));
t = foo(std::move(t));

In this case, foo might just return the same pointer value twice, and this is perfectly safe. This example seems silly, but hopefully it gets my point across.

0
P. Saladin On

Yes, your assumptions are (mostly) correct. A unique_ptr owns the object exclusively, which implies that no two unique_ptrs should point to the same object at any given time.

As others have pointed out, this does not guarantee that multiple invocations of the same function returns different objects. This is because you can pass ownership around (by moving it), so if you pass ownership back to a function, as in Brians answer, it may well return the same object again, without braking any rules.

If you decide to manage an object by using smart pointers, try to avoid new and delete altogether. To create an object T that is owned by a unique_ptr, use make_unique<T>. This avoids the error of creating multiple unique_ptr to the same object. A unique_ptr is not copyable for exactly this reason. In other words, your getUniquePtr should be implemented like this:

return std::make_unique<int>(42);

One of the few reasons to construct a smart pointer from a raw pointer (as opposed to using make_unique) is when you have existing code or a library that works with raw pointers, which you cannot change.