问题
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?
回答1:
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.
回答2:
There are tradeoffs, and you pick the solution which matches what you want. Off the top of my head:
Initial size
vector
andunique_ptr<T[]>
allow the size to be specified at run-timearray
only allows the size to be specified at compile time
Resizing
array
andunique_ptr<T[]>
do not allow resizingvector
does
Storage
vector
andunique_ptr<T[]>
store the data outside the object (typically on the heap)array
stores the data directly in the object
Copying
array
andvector
allow copyingunique_ptr<T[]>
does not allow copying
Swap/move
vector
andunique_ptr<T[]>
have O(1) timeswap
and move operationsarray
has O(n) timeswap
and move operations, where n is the number of elements in the array
Pointer/reference/iterator invalidation
array
ensures pointers, references and iterators will never be invalidated while the object is live, even onswap()
unique_ptr<T[]>
has no iterators; pointers and references are only invalidated byswap()
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
andvector
are both Containersunique_ptr<T[]>
is not a Container
I do have to admit, this looks like an opportunity for some refactoring with policy-based design.
回答3:
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
.
回答4:
An std::vector
can be copied around, while unique_ptr<int[]>
allows expressing unique ownership of the array. std::array
, on the other hand, requires the size to be determined at compile-time, which may be impossible in some situations.
回答5:
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[]>
?
回答6:
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());
}
回答7:
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).
回答8:
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...
...
回答9:
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.
回答10:
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? :-)
回答11:
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.
回答12:
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.
回答13:
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.
回答14:
- You need your structure to contain just a pointer for binary-compatibility reasons.
- You need to interface with an API that returns memory allocated with
new[]
- Your firm or project has a general rule against using
std::vector
, for example, to prevent careless programmers from accidentally introducing copies - You want to prevent careless programmers from accidentally introducing copies in this instance.
There 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.
回答15:
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).
回答16:
If you need a dynamic array of objects that are not copy-constructible, then a smart pointer to an array is the way to go. For example, what if you need an array of atomics.
来源:https://stackoverflow.com/questions/16711697/is-there-any-use-for-unique-ptr-with-array