- Overview
-
Table of Contents
- Special Member Functions: Constructors, Destructors, and the Assignment Operator
- Operator Overloading
- Memory Management
- Templates
- Namespaces
- Time and Date Library
- Streams
- Object-Oriented Programming and Design Principles
- The Standard Template Library (STL) and Generic Programming
- Exception Handling
- Runtime Type Information (RTTI)
- Signal Processing
- Creating Persistent Objects
- Bit Fields
- New Cast Operators
- Environment Variables
- Variadic Functions
- Pointers to Functions
- Function Objects
- Pointers to Members
- Lock Files
- Design Patterns
- Dynamic Linking
- Tips and Techniques
- Five Things You Need to Know About C++11 Unions
- A Tour of C99
- A Tour of C1X
-
C++0X: The New Face of Standard C++
- Reference Wrapper
- The Performance Technical Report
- auto for the People
- Ironing Templates' Syntactic Wrinkles
- Visual C++ Becomes ISO Compliant
- A Garbage Collector for C++
- C99 Core Features in C++0X
- The <code>shared_ptr</code> Class
- The shared_ptr Class, II
- Lambda Expressions and Closures, Part I
- Lambda Expressions and Closures, Part II
- Lambda Expressions and Closures, Part III
- The Type Traits Library, Part I
- The Type Traits Library, Part II
- The Type Traits Library, Part III
- finally Revisited
- The Any Library
- The nullptr Keyword Proposal
- Delegating Constructors
- The Explicit Conversion Operators Proposal
- Conditionally-Supported Behavior
- The weak ptr Class Template, Part I
- The weak ptr Class Template, Part II
- POD Types Revisited
- The rvalue Reference Proposal, Part I
- The rvalue Reference Proposal, Part II
- Proposal for New String Algorithms
- Concepts, Part I
- Concepts, Part II
- constexpr: Generalized Constant Expressions
- The <u>constexpr</u> Proposal: Constructors
- Strongly-Typed enum Types
- C++09: The Road Ahead
- C++09: Proposals by Statuses
- Changing Undefined Behavior to Diagnosable Errors
- New Character Types
- The __func__ Predeclared Identifier is Coming to C++
- Static Assertions
- The extern template Proposal
- Variadic Templates, Part I
- Variadic Templates, Part II
- Variadic Templates, Part III -- Critique
- Using unique_ptr, Part I
- Using unique_ptr, Part II
- Unrestricted Unions, Part I
- Unrestricted Unions, Part II
- Unrestricted Unions, Part III
- Types With No Linkage as Template Arguments
- New Initialization Syntax
- Initializer Lists and Sequence Constructors
- New Standard Library Algorithms
- Class Member Initializers
- Inheriting Constructors
- Introducing Attributes
- The Removal of Concepts From C++0x
- The Future of C++0x, Part I
- The Future of C++0X, Part II
- The Debate About Attributes, Part I
- The Debate About Attributes, Part II
- The Debate About Attributes, Part III
- The Debate About Attributes, Part IV
- Forward Declarations of Enum Types
- The SCARY Iterators Proposal, Part I
- The SCARY Iterators Proposal, Part II
- Heading for Deprecation: <tt>export</tt>, Exception Specification and <tt>register</tt>
- The Rejection of the Unified Function Syntax Proposal
- Rvalue References as Object Members
- FCD Approved
- The Debate on noexcept, Part I
- The Debate on noexcept, Part II
- The Debate on noexcept, Part III
- About-face -- [[Attributes]] to Be Replaced with Keywords
- Will Delegating Constructors Be Removed From C++0x?
- Rvalue References: Past, Present and Future, Part I
- Rvalue References: Past, Present and Future, Part II
- Rvalue References: Past, Present and Future, Part III
- A Move in the Right Direction, Part I
- A Move in the Right Direction, Part II
- New Keywords for Inheritance Control, Part I
- New Keywords for Inheritance Control, Part II
- FDIS Approved
- C++0x Concurrency
- The Reflecting Circle
- We Have Mail
- The Soapbox
- Numeric Types and Arithmetic
- Careers
- Locales and Internationalization
Using unique_ptr, Part II
Last updated Jan 1, 2003.
In the last part of this series I will explain why unique_ptr usage in generic containers and algorithms is safe, show how to implement the source and sink idiom with unique_ptr, discuss unique_ptr's customizable deleters, and demonstrate how unique_ptr handles arrays.
Blocking The "Bad Guys"
In the first part of this series I explained what might happen when auto_ptr is used in generic code. In short, the problem is that what outwardly appears to be a copy operation is actually a move operation in disguise. When such a disguised move operation takes an lvalue auto_ptr as its righthand side operand, that auto_ptr is effectively emptied, or pilfered. The program doesn't know that that auto_ptr has been pilfered though; it dereferences the pilfered auto_ptr and crashes.
The design of unique_ptr prevents such accidents. Class unique_ptr defines two copy constructors:
template <class T> class unique_ptr
{
public:
unique_ptr(unique_ptr&& up); // rvalues bind here
private:
unique_ptr(const unique_ptr&); // lvalues bind here
};
Although they look like copy-constructors, these are in fact move constructors. They transfer the resource from the source argument to *this. Notice however that while the first move constructor is public, the second one is private. This arrangement allows you to move from rvalues with copy syntax using the move constructor which binds to rvalues (unique_ptr(unique_ptr&& u)), while blocking the copy from lvalues by making the copy constructor that binds to lvalues (unique_ptr(const unique_ptr&)) private.
Now I'll explain it in simpler words. The bad guys are move operations that take a righthand side lvalue argument. They are blocked because the move constructor that intercepts lvalues is private. Moving from an rvalue (which is safe) is intercepted by the public move constructor that takes an rvalue reference.
Why is Moving From An Rvalue Safe?
To make the distinction between rvalues and lvalues simpler, replace these terms with "unnamed objects" and "named objects", respectively. When you move from an unnamed object (e.g., an object returned by value from a function) the unnamed object is destroyed immediately after the move operation, so your code can't access the pilfered object accidentally. In contrast, when you move from a named object, the program might accidentally access that object later, not knowing it has been emptied. It's the latter kind of move operations that unique_ptr disables.
Safe Usage With Containers and Algorithms
The conclusion drawn from the previous paragraph is that you can use unique_ptr safely in generic code. If a container or an algorithm moves from an lvalue element using copy syntax (remember the bad guys?), you will get a compilation error, not a runtime crash. If however the container or algorithm moves elements instead of copying, it will work perfectly:
vector<unique_ptr<char> > vc;
v.push_back(unique_ptr<char>(new int('c')));
v.push_back(unique_ptr<char>(new int('b')));
v.push_back(unique_ptr<char>(new int('a')));
sort(v.begin(),v.end(),indirect_less()); //result {a,b,c}
Sinks and Sources
Implementing the source and sink idiom with unique_ptr is straightforward. The source function allocates a resource and wraps it in a unique_ptr returned by value:
unique_ptr<int> source(int i)
{
return unique_ptr<int>(new int(i));
}
void sink(unique_ptr<int>);
sink(source(2));//Fine, implicit move from rvalue
Notice however that when a "bad guys" type of move takes place, you must use an explicit move() call:
unique_ptr<int> up1=source(2); sink(up1); //compilation error: implicit move from lvalue sink(move(up1)); //OK, explicit move
Customizing A Deleter
unique_ptr lets you customize its deleter. Recall that a deleter is a callable entity that a smart pointer's destructor will invoke to deallocate its resource. The default deleter of unique_ptr calls delete. However, when the resource isn't an object allocated by new you can override the default. For example, binding a unique_ptr to an object allocated by malloc() requires a deleter that calls free():
int* pi=(int*)malloc(sizeof(int)) unique_ptr<pu, std::free> myptr; *myptr=5;
Handling Arrays
As opposed to what most programmers believe, it's not enough to replace delete with delete[] in order to make a smart pointer handle arrays properly. The interface of smart pointers to single objects differs fundamentally from that of smart pointers to arrays in at least three crucial aspects:
- A single object smart pointer usually supports derived-to-base conversions while array smart pointers must not support such conversions.
- Defining a dereference operator for a single object makes sense but not for an array as it will dereference only the first element.
- The [] operator makes sense only in an array smart pointer.
In other words, a unique_ptr class for handling arrays must have a different implementation from that of the single object unique_ptr. The designers of unique_ptr solved this problem by defining a partial specialization for unique_ptr<T[]>:
unique_ptr <int[]> intarr (new int[4]); //array version of unique_ptr arrup[0]=10; arrup[1]=20; arrup[2]=30; arrup[3]=40;
The trailing [] after the type T indicates that the array specialization of unique_ptr (no dereference, no conversions, has indexing) shall be used. The default deleter for the array specialization of unique_ptr calls delete[].
