What is unique_ptr

beginner c++14 memory

Related: Shared Pointer and The Heap

In C++ std::unique_ptr (pronounced “unique pointer”) is one of the “smart pointer” classes available in the Standard Library. What makes std::unique_ptr “smart” is that it always calls delete on the pointer it holds onto on destruction as compared to regular pointers, which do not. std::unique_ptr implements the dereference operator (*) and the arrow operator (->) in order to behave like a pointer even though it’s really an object from a library.

The Standard Library also provides a helper function for creating unique pointers called std::make_unique(). std::make_unqiue() calls new for you, passing all parameters to the type’s constructor. This function is useful because objects might throw exceptions during construction and if they do we still need to call delete on the pointer we received from new otherwise we will leak the pointer. std::make_unique() correctly handles these cases. This is why it’s considered best practice to always use std::make_unique() instead of calling new. Here’s an example:

#include <memory>
#include <iostream>

struct Person {
  Person(std::string name, int age) 
    : name(std::move(name)), age(age) {
  }

  ~Person() {
    std::cout << name << " says bye!\n";
  }

  std::string name;
  int age;
};

void describe_person(const Person& p) {
  std::cout << p.name << " is " << p.age << "\n";
}

int main() {
  // Best to use std::make_unique
  auto maria = std::make_unique<Person>("Maria", 40); 
  // You can also pass any pointer to std::unique_ptr's constructor
  Person* p = new Person("Nushi", 12);
  std::unique_ptr<Person> nushi(p);
  
  // unique_ptr can be used just like regular pointers
  // because of operator overloading
  describe_person(*maria);
  describe_person(*nushi);

  nushi->age += 1;
  describe_person(*nushi);
}
Maria is 40
Nushi is 12
Nushi is 13
Nushi says bye!
Maria says bye!

unique_ptr Can’t Be Copied

Because std::unique_ptr calls delete on the pointer it holds it’s said to own that pointer. It’s also incorrect to call delete on the same pointer twice in C++. To prevent this “double free” from happening we cannot copy unique pointers, but we can instead move them with std::move. Moving a unique pointer object moves the ownership of the internal pointer from one to another. Here’s an example:

#include <memory>
#include <iostream>

struct Pet {
  explicit Pet(std::string name) : name(std::move(name)) {
  }

  std::string name;
};

struct Person {
  Person(std::string name, int age, std::unique_ptr<Pet> pet) 
    : name(std::move(name)), age(age), pet(std::move(pet)) {
  }

  std::string name;
  int age;
  std::unique_ptr<Pet> pet;
};

void describe_person(const Person& p) {
  std::cout << p.name << " is " << p.age << "\n";
  if (p.pet) {
    std::cout << p.name << " has a pet named " 
              << p.pet->name << "\n";
  }
}

int main() {
  auto scout = std::make_unique<Pet>("Scout");
  auto nushi = std::make_unique<Person>("Nushi", 12, 
                                        std::move(scout));

  describe_person(*nushi);
}
Nushi is 12
Nushi has a pet named Scout

get()

Sometimes an API demands a pointer be passed as a parameter. std::unique_ptr provides a get() method for just such a purpose. get() returns the underlying pointer, but be careful, the ownership of the pointer still belongs to the std::unique_ptr object.

#include <memory>
#include <iostream>

struct Pet {
  explicit Pet(std::string name) : name(std::move(name)) {
  }

  std::string name;
};

struct Person {
  Person(std::string name, int age, std::unique_ptr<Pet> pet) 
    : name(std::move(name)), age(age), pet(std::move(pet)) {
  }

  std::string name;
  int age;
  std::unique_ptr<Pet> pet;
};

void describe_person(Person* p) {
  std::cout << p->name << " is " << p->age << "\n";
  if (p->pet) {
    std::cout << p->name << " has a pet named " 
              << p->pet->name << "\n";
  }
}

int main() {
  auto scout = std::make_unique<Pet>("Scout");
  auto nushi = std::make_unique<Person>("Nushi", 12, 
                                        std::move(scout));

  describe_person(nushi.get());
}
Nushi is 12
Nushi has a pet named Scout


For more C++ By Example, click here.