Who’s the Smartest of ‘Em All? Get to Know std::unique_ptr

td::auto_ptr was a move semantics pioneer. However, at that time, C++ didn’t have the facilities for supporting move operations that were both safe and efficient. After years of distilling, C++0x brings you a superior alternative called unique_ptr. unique_ptr offers the same runtime and size efficiency of auto_ptrin addition to code safety and many other goodies, including array handling, and STL compatibility.


Your program needs a smart pointer that implements the strict ownership semantics but you cannot use auto_ptrsafely with STL containers and algorithms.


Replace auto_ptr with the new unique_ptrclass template.

Presenting the Problem
Suppose your program calls std::sort() to sort a sequence of auto_ptrobjects:

vector > vi;//..populate visort(vi.begin(), vi.end(), indirect_less());

Depending on the underlying implementation of sort(), the above call could work perfectly—or it might cause a runtime crash. The problem is that some implementations of sort()pick an element out of the sequence, storing a local copy thereof:

value_type pivot_elem = *midpoint;//not a copy operation!

sort() assumes that pivot_elem and *midpoint are equivalent. However when value_type is an auto_ptr, this assumption fails because what appears to be a copy operation is in fact a move operation. Consequently, the algorithm fails. There’s nothing amiss with the implementation of sort(). Rather, auto_ptris the culprit.

Because of such bugs, using auto_ptrwith Standard Library containers and algorithms is forbidden. What other alternative do you have?

Just Like auto_ptr, But Better
unique_ptrticks the right boxes. It implements a strict ownership semantics, it’s slim and it’s fast, with a memory footprint identical to that of a native pointer.

If you’ve used auto_ptr before you will notice that unique_ptrhas an almost identical interface:

#include  using namespace std;unique_ptr up1; //default constructionunique_ptr up2(new int(9)); //initialize with pointer*up2 = 23; //dereferenceup2.reset(); //resetassert (sizeof(auto_ptr)==sizeof(unique_ptr));assert (sizeof(auto_ptr)==sizeof(unique_ptr&lT>));

The main difference between auto_ptr and unique_ptr becomes visible in move operations. While auto_ptr sometimes disguises move operations as copy-operations, unique_ptr will not let you use copy semantics when you’re actually moving an lvalue unique_ptr:

auto_ptr ap1(new int);auto_ptr ap2=ap1; //OK but unsafe: move                       //operation in disguise unique_ptr up1(new int);unique_ptr up2=up1; //compilation error: private                         //copy ctor inaccessible

Instead, you must call move() when moving operation from an lvalue:

unique_ptr up2 = std::move(up1);//OK

This makes unique_ptrsafe for usage with containers and algorithms.

Containers and Algorithms
If a container of unique_ptr objects moves elements internally instead of copying them, your code will work without problems. If, however, a container or an algorithm uses copy semantics for its value_typeyou will get a compilation error. Thus, you won’t encounter a run time error due to a move operation being mistaken for a copy:

vector > vi;v.push_back(unique_ptr(new int(8)));  v.push_back(unique_ptr(new int(4)));v.push_back(unique_ptr(new int(1)));sort(v.begin(),v.end(),indirect_less()); //result {1,4,8}

The Sink and Source Idiom
A source is a function that acquires a resource and passes the ownership of that resource to another function called a sink. A sink uses the resource and when it’s done, it disposes of that resource. You can return unique_ptrby value from a source:

unique_ptr source(int i){ return unique_ptr(new int(i));}void sink(unique_ptr);sink(source(5));//OK, implicit move from rvalue

Again, when moving from an lvalue you must use move()explicitly:

unique_ptr up=source(5);sink(up); //compilation error, implicit move from lvaluesink(move(up)); //OK

To generalize, an implicit move from an rvalue unique_ptr is always safe and therefore may use copy syntax. Moving from an lvalue unique_ptr requires an explicit move()call.

Customizable Deleters
A deleter is a function that the smart pointer uses for deallocating its resource. By default, unique_ptr‘s deleter uses plain delete, as expected. However, certain resources require special cleanup operations. Here’s how you install a custom deleter that has no effect:

struct nop_deleter{ template  void operator()(T*) {}; // do nothing};int main(){ int n=0; // stack memory. Safe, deleter does nothingunique_ptr pi(&n); *pi = 10; assert(sizeof(pi)==sizeof(int*)); //zero space overhead}

In the following example, a custom deleter calls std::free() to deallocate an object allocated using std::malloc():

#include double* d =(double*) std::malloc(sizeof(double));//deleter calls free(), not delete unique_ptr pd(d, std::free); 

Arrays
The interface of smart pointers to single objects differs fundamentally from that of smart pointers to arrays:

  • A single object smart pointer often supports derived-to-base conversions while array smart pointers must not support such conversions.
  • A dereference operator for a single object makes sense but for an array it makes less sense because it will dereference only the first element.
  • Operator [] makes sense only for an array smart pointer.

The designers of unique_ptr solved these discrepancies by creating a partial specializationfor arrays:

unique_ptr arrup (new int[3]);arrup[0]=1;arrup[1]=2;arrup[2]=3;

The trailing [] after the type in the unique_ptr declaration indicates that the “array version” (no dereference, no conversions, indexing supported) shall be used. As expected, the default deleter here is delete[].

Elegant Smart
unique_ptr isn’t just a safer alternative to the deprecated auto_ptr. It offers services that auto_ptrdoesn’t support, including safe usage with containers and algorithms, customizable deleters, and array handling. Surprisingly, this versatility doesn’t exact a performance toll; you only pay for what you’re actually using.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

The Latest

positive contribution tech

Technology’s Positive Contributions to Society

Technology has and continues to improve our lives. From the business world to the world of medicine, and our day-to-day lives, you can’t go a day without interacting with at least one form of technology. While some fear technology may be going too far, there are many ways in which

How to Choose From The Best Big Data Platforms in 2023

How to Choose From The Best Big Data Platforms in 2023

As big data continues to become increasingly popular in the business world, companies are always looking for better ways to process and analyze complex data. The process critically depends on the platform that manages and analyzes the data. In this article, we will provide a guide to help you choose

Why transparent code is a good idea

Why Transparent Code is a Good Idea

Code is used to make up the apps and software we use every day. From our favorite social media platforms to our online banking services, code is the framework used to build these tools that help make our lives easier. Code is complex. Software today requires large teams of programmers

The Role of WordPress Hosting in Website Speed and Performance

The Role of WordPress Hosting in Website Performance

The term “WordPress hosting” refers to a specific type of web hosting service that offers hardware and software configurations tailored to the needs of WP sites. It’s important to remember that a WP hosting package is not required to host WordPress webpages. WP web pages are also compatible with standard

Data Privacy vs. Data Security: What you Should Know

Data Privacy vs. Data Security: What you Should Know

Data privacy and data security are often used interchangeably, but they are two completely different things. It’s important to understand the difference for anyone who handles sensitive information, such as personal data or financial records. In this article, we’ll take a closer look at data privacy vs. data security. We’ll