Proper way to create unique_ptr that holds an allocated array
Using the T[]
specialisation:
std::unique_ptr<unsigned char[]> testData(new unsigned char[16000]());
Note that, in an ideal world, you would not have to explicitly use new
to instantiate a unique_ptr
, avoiding a potential exception safety pitfall. To this end, C++14 provides you with the std::make_unique
function template. See this excellent GOTW for more details. The syntax is:
auto testData = std::make_unique<unsigned char[]>(16000);
Is there any use for unique_ptr with array?
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.
How to make an array that holds unique_ptrs?
std::unique_ptr<int[]> arr;
arr = std::make_unique<int[]> (5);
At this point you have a unique_ptr
to an array of int
. This sounds like it is exactly what you want.
arr[0] = *new int(1);
But this is questionable. It dynamically allocates a single int
, assigns 1 to the allocated int
, then it assigns the value, 1, at the allocated int
into the array at element 0. the allocated int
is left hanging with nothing pointing at it, and is now exceptionally difficult to `delete. This is a memory leak.
delete &arr[0]; // malloc error, want to avoid "delete"
And as you've seen, this is fatal. Rather than attempting to delete
the leaked int
, delete
has been invoked with a pointer to the array stored in the unique_ptr
. Eventually the unique_ptrwill try to
delete` the array and fail because it's already gone.
Based on comments, OP intends
std::unique_ptr<int*[]> arr;
arr = std::make_unique<int*[]> (5);
arr[0] = new int(1);
delete arr[0];
But I'd like to talk them out of this idea. Let's look at their end goal: a templated class
template <class TYPE>
class MyVector
{
std::unique_ptr<TYPE[]> arr; // array of whatever type
public:
MyVector(size_t size): arr(std::make_unique<TYPE[]> (size))
{
}
TYPE& operator[](size_t index)
{
return arr[index];
}
// note the complete lack of growing, shrinking and other vector goodness
// as they are not needed for this example.
};
We can use this class with just about anything.
int main()
{
// vector of int
MyVector<int> vec(5);
vec[0] = 1;
// vector of pointer to int (yuck)
MyVector<int*> vec2(5);
vec2[0] = new int(1);
delete vec2[0];
// vector of smart pointer to int (also yuck, but less yuck)
MyVector<std::unique_ptr<int>> vec3(5);
vec3[0] = std::make_unique<int>(1);
// vector of std::string
MyVector<std::string> vec4(5);
vec4[0] = "I am the very model of a modern major general...";
}
If the user of the vector want it to contain pointers, they can say so. There is no reason to force the user to use a pointer.
Proper syntax for defining a unique_ptr array of class objects with a constructor
The answer below is based on a previous version of the question, in which the array size appeared to be a compile-time constant. If the size of the created array is not a compile-time constant, then it is impossible to pass arguments to the constructors of the elements. In that case std::vector
is probably a better choice than array-std::unique_ptr
.
It works the same as always for arrays, using aggregate initialization:
std::unique_ptr<MyClass[]> arr(new MyClass[]{
{...},
{...},
{...},
{...},
{...}});
where ...
are replaced by the argument lists for the constructors of the five elements.
Or if you cannot use list-initialization for the elements, e.g. because that would unintentionally use a std::initializer_list
constructor:
std::unique_ptr<MyClass[]> arr(new MyClass[]{
MyClass(...),
MyClass(...),
MyClass(...),
MyClass(...),
MyClass(...)});
std::make_unique
would usually be preferred for creating std::unique_ptr
s, but there is at the moment no overload which allows passing arguments to the constructors of the individual array elements.
If you want to pass the same argument list to each element, a simple solution given that the type is copy-constructible would be to declare one instance and then copy-construct the elements from this instance:
MyClass instance(arguments);
std::unique_ptr<MyClass[]> arr(new MyClass[]{instance, instance, instance, instance, instance);
Otherwise you could write a template function that expands these repeated items for you. (Might add example later.)
Is it true that unique_ptr which points to an array will automatically free dynamic memory after calling release()?
I looked a little in this, and I'm guessing this was just a mistake on the author's part. cppreference.com makes no reference to any array specialization for release
. Just to make sure, I went ahead and checked the source code for libc++ (LLVM's implementation of the standard library). This is the implementation for std::unique_ptr<T[], Deleter>::release
. As you can see, it does not call delete[]
. My guess is the author meant to write up.reset();
, as this does free the memory.
unique_ptr alternative for dynamically allocated array
Using std::unique_ptr
would require allocation using new[]
.
Which is never (or almost never) the best solution. In this
case, just use std::vector
. In pre-C++11, pass &array[0],
; in C++11, you can be clearer, and pass
array.size()array.data(), array.size()
. (This also has the advantage that
you don't have to special case empty vectors.)
Create an array of objects using std::unique_ptr
It's caused by the fact that new std::string[5]
is a dynamic array allocation, and so it has no (fixed/defined/calculable) size (if you don't save it before the allocation), infact
auto p = new std::string[5];
std::cout << sizeof(p);
Will print 8
that is the size of a pointer.
And so the debugger sees that ptr
as a pointer, and can't figure out that there are other 4 elements after the first one (and so can't figure out that is an array
, and not just a pointer
to a string
)
Seems like that this is a "C++ POV", and that the debugger can have more informations, and so should be able to figure out that it is an array of string
and not just a pointer to string
(thanks to @Konrad Rudolph)
Passing a std::unique_ptr to CListBox::GetSelItems
If you need access to the pointer to an object managed by a std::unique_ptr
without transferring ownership, you can call its get()
method. This is useful for interop with a C interface such as here (GetSelItems()
is really just wrapping a call to SendMessage
with the LB_GETSELITEMS
message).
That'd work, though in this case I'd probably use a std::vector<int>
instead. It provides the same properties as a std::unique_ptr
with respect to automatic cleanup, but also has other features that come in handy (specifically range adapters). It also feels more natural to use a container here, but that's a matter of personal preference.
The following implements the proposed changes:
void CSelectedBroHighlight::BuildSelectedArray() {
// empty current array
m_aryStrSelectedBro.RemoveAll();
// get selected count
auto const sel_item_count{ m_lbBrothers.GetSelCount() };
if(sel_item_count > 0) {
// get selected indices
std::vector<int> sel_items(sel_item_count);
m_lbBrothers.GetSelItems(sel_items.size(), sel_items.data());
// iterate over all selected item indices
for(auto const index : sel_items) {
CString strText;
m_lbBrothers.GetText(index, strText);
m_aryStrSelectedBro.Add(strText);
}
}
}
This provides the same automatic cleanup as an implementation based on std::unique_ptr
, but also enables use of a range-based for
loop further down.
Related Topics
Best Introduction to C++ Template Metaprogramming
C++11 Member Initializer List VS In-Class Initializer
Why Can a T* Be Passed in Register, But a Unique_Ptr<T> Cannot
What Is the Default Hash Function Used in C++ Std::Unordered_Map
Do Child Threads Exit When the Parent Thread Terminates
Do Built-In Types Have Move Semantics
How Do Take a Screenshot Correctly with Xlib
What Information Does Gcc Profile Guided Optimization (Pgo) Collect and Which Optimizations Use It
Scoped Using-Directive Within a Struct/Class Declaration
Is Sizeof(*Ptr) Undefined Behavior When Pointing to Invalid Memory
Std::Function and Std::Bind: What Are They, and When Should They Be Used
What Exactly Is the Difference Between "Pass by Reference" in C and in C++
Recursively Iterate Over All the Files in a Directory and Its Subdirectories in Qt
Self-Sufficient Header Files in C/C++
How to Make a Heterogeneous Boost::Map
How to Clear the Console in Both Windows and Linux Using C++