std::unique_ptr
has support for arrays, for instance:
std::unique_ptr<int[]> p(new int[10]);
but is it needed? probably it is more convenient to use std::vector
or std::array
.
Do you find any use for that construct?
Some people do not have the luxury of using std::vector
, even with allocators. Some people need a dynamically sized array, so std::array
is out. And some people get their arrays from other code that is known to return an array; and that code isn't going to be rewritten to return a vector
or something.
By allowing unique_ptr<T[]>
, you service those needs.
In short, you use unique_ptr<T[]>
when you need to. When the alternatives simply aren't going to work for you. It's a tool of last resort.
There are tradeoffs, and you pick the solution which matches what you want. Off the top of my head:
Initial size
vector
and unique_ptr<T[]>
allow the size to be specified at run-timearray
only allows the size to be specified at compile timeResizing
array
and unique_ptr<T[]>
do not allow resizingvector
doesStorage
vector
and unique_ptr<T[]>
store the data outside the object (typically on the heap)array
stores the data directly in the objectCopying
array
and vector
allow copyingunique_ptr<T[]>
does not allow copyingSwap/move
vector
and unique_ptr<T[]>
have O(1) time swap
and move operationsarray
has O(n) time swap
and move operations, where n is the number of elements in the arrayPointer/reference/iterator invalidation
array
ensures pointers, references and iterators will never be invalidated while the object is live, even on swap()
unique_ptr<T[]>
has no iterators; pointers and references are only invalidated by swap()
while the object is live. (After swapping, pointers point into to the array that you swapped with, so they're still "valid" in that sense.)vector
may invalidate pointers, references and iterators on any reallocation (and provides some guarantees that reallocation can only happen on certain operations).Compatibility with concepts and algorithms
array
and vector
are both Containersunique_ptr<T[]>
is not a ContainerI do have to admit, this looks like an opportunity for some refactoring with policy-based design.
One reason you might use a unique_ptr
is if you don't want to pay the runtime cost of value-initializing the array.
std::vector<char> vec(1000000); // allocates AND value-initializes 1000000 chars
std::unique_ptr<char[]> p(new char[1000000]); // allocates storage for 1000000 chars
The std::vector
constructor and std::vector::resize()
will value-initialize T
- but new
will not do that if T
is a POD.
See Value-Initialized Objects in C++11 and std::vector constructor
Note that vector::reserve
is not an alternative here: Is accessing the raw pointer after std::vector::reserve safe?
It's the same reason a C programmer might choose malloc
over calloc
.
Scott Meyers has this to say in Effective Modern C++
The existence of
std::unique_ptr
for arrays should be of only intellectual interest to you, becausestd::array
,std::vector
,std::string
are virtually always better data structure choices than raw arrays. About the only situation I can conceive of when astd::unique_ptr<T[]>
would make sense would be when you're using a C-like API that returns a raw pointer to a heap array that you assume ownership of.
I think that Charles Salvia's answer is relevant though: that std::unique_ptr<T[]>
is the only way to initialise an empty array whose size is not known at compile time. What would Scott Meyers have to say about this motivation for using std::unique_ptr<T[]>
?
Contrary to std::vector
and std::array
, std::unique_ptr
can own a NULL pointer.
This comes in handy when working with C APIs that expect either an array or NULL:
void legacy_func(const int *array_or_null);
void some_func() {
std::unique_ptr<int[]> ptr;
if (some_condition) {
ptr.reset(new int[10]);
}
legacy_func(ptr.get());
}
In a nutshell: it's by far the most memory-efficient.
A std::string
comes with a pointer, a length, and a "short-string-optimization" buffer. But my situation is I need to store a string that is almost always empty, in a structure that I have hundreds of thousands of. In C, I would just use char *
, and it would be null most of the time. Which works for C++, too, except that a char *
has no destructor, and doesn't know to delete itself. By contrast, a std::unique_ptr<char[]>
will delete itself when it goes out of scope. An empty std::string
takes up 32 bytes, but an empty std::unique_ptr<char[]>
takes up 8 bytes, that is, exactly the size of its pointer.
The biggest downside is, every time I want to know the length of the string, I have to call strlen
on it.
A common pattern can be found in some Windows Win32 API calls, in which the use of std::unique_ptr<T[]>
can come in handy, e.g. when you don't exactly know how big an output buffer should be when calling some Win32 API (that will write some data inside that buffer):
// Buffer dynamically allocated by the caller, and filled by some Win32 API function.
// (Allocation will be made inside the 'while' loop below.)
std::unique_ptr<BYTE[]> buffer;
// Buffer length, in bytes.
// Initialize with some initial length that you expect to succeed at the first API call.
UINT32 bufferLength = /* ... */;
LONG returnCode = ERROR_INSUFFICIENT_BUFFER;
while (returnCode == ERROR_INSUFFICIENT_BUFFER)
{
// Allocate buffer of specified length
buffer.reset( BYTE[bufferLength] );
//
// Or, in C++14, could use make_unique() instead, e.g.
//
// buffer = std::make_unique<BYTE[]>(bufferLength);
//
//
// Call some Win32 API.
//
// If the size of the buffer (stored in 'bufferLength') is not big enough,
// the API will return ERROR_INSUFFICIENT_BUFFER, and the required size
// in the [in, out] parameter 'bufferLength'.
// In that case, there will be another try in the next loop iteration
// (with the allocation of a bigger buffer).
//
// Else, we'll exit the while loop body, and there will be either a failure
// different from ERROR_INSUFFICIENT_BUFFER, or the call will be successful
// and the required information will be available in the buffer.
//
returnCode = ::SomeApiCall(inParam1, inParam2, inParam3,
&bufferLength, // size of output buffer
buffer.get(), // output buffer pointer
&outParam1, &outParam2);
}
if (Failed(returnCode))
{
// Handle failure, or throw exception, etc.
...
}
// All right!
// Do some processing with the returned information...
...
I have used unique_ptr<char[]>
to implement a preallocated memory pools used in a game engine. The idea is to provide preallocated memory pools used instead of dynamic allocations for returning collision requests results and other stuff like particle physics without having to allocate / free memory at each frame. It's pretty convenient for this kind of scenarios where you need memory pools to allocate objects with limited life time (typically one, 2 or 3 frames) that do not require destruction logic (only memory deallocation).
I faced a case where I had to use std::unique_ptr<bool[]>
, which was in the HDF5 library (A library for efficient binary data storage, used a lot in science). Some compilers (Visual Studio 2015 in my case) provide compression of std::vector<bool>
(by using 8 bools in every byte), which is a catastrophe for something like HDF5, which doesn't care about that compression. With std::vector<bool>
, HDF5 was eventually reading garbage because of that compression.
Guess who was there for the rescue, in a case where std::vector
didn't work, and I needed to allocate a dynamic array cleanly? :-)
I can't disagree with the spirit of the accepted answer strongly enough. "A tool of last resort"? Far from it!
The way I see it, one of the strongest features of C++ compared to C and to some other similar languages is the ability to express constraints so that they can be checked at compile time and accidental misuse can be prevented. So when designing a structure, ask yourself what operations it should permit. All the other uses should be forbidden, and it's best if such restrictions can be implemented statically (at compile time) so that misuse results in a compilation failure.
So when one needs an array, the answers to the following questions specify its behavior: 1. Is its size a) dynamic at runtime, or b) static, but only known at runtime, or c) static and known at compile time? 2. Can the array be allocated on the stack or not?
And based on the answers, this is what I see as the best data structure for such an array:
Dynamic | Runtime static | Static
Stack std::vector unique_ptr<T[]> std::array
Heap std::vector unique_ptr<T[]> unique_ptr<std::array>
Yep, I think unique_ptr<std::array>
should also be considered, and neither is a tool of last resort. Just think what fits best with your algorithm.
All of these are compatible with plain C APIs via the raw pointer to data array (vector.data()
/ array.data()
/ uniquePtr.get()
).
P. S. Apart from the above considerations, there's also one of ownership: std::array
and std::vector
have value semantics (have native support for copying and passing by value), while unique_ptr<T[]>
can only be moved (enforces single ownership). Either can be useful in different scenarios. On the contrary, plain static arrays (int[N]
) and plain dynamic arrays (new int[10]
) offer neither and thus should be avoided if possible - which should be possible in the vast majority of cases. If that wasn't enough, plain dynamic arrays also offer no way to query their size - extra opportunity for memory corruptions and security holes.
One additional reason to allow and use std::unique_ptr<T[]>
, that hasn't been mentioned in the responses so far: it allows you to forward-declare the array element type.
This is useful when you want to minimize the chained #include
statements in headers (to optimize build performance.)
For instance -
myclass.h:
class ALargeAndComplicatedClassWithLotsOfDependencies;
class MyClass {
...
private:
std::unique_ptr<ALargeAndComplicatedClassWithLotsOfDependencies[]> m_InternalArray;
};
myclass.cpp:
#include "myclass.h"
#include "ALargeAndComplicatedClassWithLotsOfDependencies.h"
// MyClass implementation goes here
With the above code structure, anyone can #include "myclass.h"
and use MyClass
, without having to include the internal implementation dependencies required by MyClass::m_InternalArray
.
If m_InternalArray
was instead declared as a std::array<ALargeAndComplicatedClassWithLotsOfDependencies>
, or a std::vector<...>
, respectively - the result would be attempted usage of an incomplete type, which is a compile-time error.
new[]
std::vector
, for example, to prevent careless programmers from accidentally introducing copiesThere is a general rule that C++ containers are to be preferred over rolling-your-own with pointers. It is a general rule; it has exceptions. There's more; these are just examples.
To answer people thinking you "have to" use vector
instead of unique_ptr
I have a case in CUDA programming on GPU when you allocate memory in Device you must go for a pointer array (with cudaMalloc
).
Then, when retrieving this data in Host, you must go again for a pointer and unique_ptr
is fine to handle pointer easily.
The extra cost of converting double*
to vector<double>
is unnecessary and leads to a loss of perf.
They may be the rightest answer possible when you only get to poke a single pointer through an existing API (think window message or threading-related callback parameters) that have some measure of lifetime after being "caught" on the other side of the hatch, but which is unrelated to the calling code:
unique_ptr<byte[]> data = get_some_data();
threadpool->post_work([](void* param) { do_a_thing(unique_ptr<byte[]>((byte*)param)); },
data.release());
We all want things to be nice for us. C++ is for the other times.
unique_ptr<char[]>
can be used where you want the performance of C and convenience of C++. Consider you need to operate on millions (ok, billions if you don't trust yet) of strings. Storing each of them in a separate string
or vector<char>
object would be a disaster for the memory (heap) management routines. Especially if you need to allocate and delete different strings many times.
However, you can allocate a single buffer for storing that many strings. You wouldn't like char* buffer = (char*)malloc(total_size);
for obvious reasons (if not obvious, search for "why use smart ptrs"). You would rather like unique_ptr<char[]> buffer(new char[total_size]);
By analogy, the same performance&convenience considerations apply to non-char
data (consider millions of vectors/matrices/objects).
std::shared_ptr<T[]>
, but there should be, and probably will be in C++14 if anyone could be bothered to write up a proposal. In the mean time, there's alwaysboost::shared_array
. – Pseudonymstd::shared_ptr
<T[]> is in c++17 now. – 陳 力