The Smart Pointer That Makes Your C++ Applications Safer - std::unique_ptr

WEBINAR: On-demand webcast

How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017 REGISTER >


Due to its peculiar design and semantics, the standard smart pointer class auto_ptr was incompatible with other Standard Library components and was eventually deprecated. Shared_ptr was added to C++ programming in 2003 and has since replaced auto_ptr as the default smart pointer. However, it incurs noticeable performance overhead both in terms of size and speed. The C++0x Final Committee Draft (FCD) recently introduced another smart pointer called unique_ptr that combines the best of both worlds, it's very efficient (even more than auto_ptr), and yet it's fully compatible with the Standard Library, as is shared_ptr. That's not all, did you know that unique_ptr supports arrays too?

Performance Issues

The first question you'd ask yourself is probably: "Why do I need to learn how to use another smart pointer class when shared_ptr fits the bill?" In one word: performance. Shared_ptr uses reference counting to allow the sharing of a single resource by multiple objects. Additionally, its destructor and other member functions are virtual. These properties facilitate the customization of shared_ptr. However, the size of a typical shared_ptr object is 40 bytes, which is 10 times bigger than the size of a raw pointer on a 32-bit platform. The presence of virtual member functions means that in many cases, calls to member functions are resolved dynamically, incurring additional runtime overhead. These issues may not concern you if you're using shared_ptr sporadically. However, in time critical apps, or if you have containers that store a large number of shared_ptr objects, the performance overhead might be overwhelming. Unique_ptr gives you a safe and reliable smart pointer alternative that can compete with the size and speed of raw pointers. Here's how it's used.

Using Unique_ptr

Although unique_ptr is not a 100% source-compatible drop-in replacement for auto_ptr, everything you can do with auto_ptr, unique_ptr will do as well:

#include <utility>  //declarations of unique_ptr 
using std::unique_ptr;
  // default construction
unique_ptr<int> up; //creates an empty object
  // initialize with an argument
unique_ptr<int> uptr (new int(3));
double *pd= new double;
unique_ptr<double> uptr2 (pd);
  // overloaded * and ->
*uptr2 = 23.5;
unique_ptr<std::string> ups (new std::string("hello"));
int len=ups->size();

Reset() releases the owned resource and optionally acquires a new resource:

unique_ptr<double> uptr2 (pd);
uptr2.reset(new double); //delete pd and acquire a new pointer
uptr2.reset(); //delete the pointer acquired by the previous reset() call

If you need to access the owned pointer directly use get():

void func(double*);

Unique_ptr lets you install a custom deleter if necessary. A deleter is a callable entity (a function, function object etc.) that the smart pointer's destructor will invoke to deallocate its resource. Unique_ptr's default deleter calls delete. If the resource isn't an object allocated by new you'll need to install a different deleter. For instance, a pointer allocated by malloc() requires a deleter that calls free():

#include <cstdlib>
int* p=(int*)malloc(sizeof(int));
unique_ptr<p, free> intptr; //initialized with a custom deleter

Swap() exchanges the resources and deleters of the two objects:

unique_ptr<double, std::free> up1 ((double*)malloc(sizeof(double));
unique_ptr<double> up2;

Unique_ptr has implicit conversion to bool. This lets you use unique_ptr object in Boolean expressions such as this:

if (up1) //implicit conversion to bool
  cout<<"an empty smart pointer"<<endl;

Array Support

Unique_ptr can store arrays as well. A unique_ptr that owns an array defines an overloaded operator []. Obviously, the * and -> operators are not available. Additionally, the default deleter calls delete[] instead of delete:

unique_ptr<int[]> arrup (new int[5]);
cout<<*arrup<<endl; //error, operator * not defined 
unique_ptr<char[], std::free> charup ((char*)( malloc(5));

Compatibility with Containers and Algorithms

You can safely store unique_ptr in Standard Library containers and let algorithms manipulate sequences of unique_ptr objects. For containers and algorithms that move elements around internally instead of copying them around, unique_ptr will work flawlessly. If however the container or algorithm uses copy semantics for its value_type you'll get a compilation error. Unlike with auto_ptr, there is no risk of a runtime crash due to a move operation disguised as copying:

vector > vi;
vi.push_back(unique_ptr(new int(0)));  // populate vector 
vi.push_back(unique_ptr(new int(3)));
vi.push_back(unique_ptr(new int(2)));
sort(v.begin(), v.end(), indirect_less());  //result: {0, 2, 3}


In conclusion, unique_ptr is the safe equivalent of auto_ptr. Its small memory footprint and runtime efficiency make it a useful replacement for raw pointers. If you don't need to share resources, unique_ptr is the right choice for you.


  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • The hunger for IIoT-enabled solutions is driving companies to seek out reliable, secure IIoT platforms that can handle industrial-grade IoT capabilities. What features and capabilities should companies expect in an IIoT platform? Until now, developing an IIoT solution has required the costly, time-intensive effort of platform building, as developers create technology stacks from scratch to handle the hardware, firmware, software, edge computing, analytics, business systems integration, and more. This …

  • MongoDB has the flexibility, adaptability and extensibility to embrace widely varying data types and rapid design/deployment cycles. Combining MongoDB with our 5100 ECO Enterprise SSD brings amazing results in smaller, simpler deployments compared to legacy storage. In this technical brief, we compare two MongoDB test clusters, each using the Linux Logical Volume Manager (LVM) for RAID configuration: 5100 ECO 3-node cluster: Two Micron 5100 ECO (1.92TB) per node configured as a software RAID 0 (LVM) Legacy …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date