How to Create Unique_Ptr That Holds an Allocated Array

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 todelete` 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_ptrs, 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],
array.size()
; in C++11, you can be clearer, and pass
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



Leave a reply



Submit