It is a common practice in modern C++ applications to use smart-pointers for managing dynamically allocated objects. Nowadays, smart pointers are not only recommended but are considered essential for safer memory management. An std::unique_ptr is a lightweight smart-pointer that is often used to exclusively own and manage a dynamically allocated object, as follows:
struct MyClass { };
void foo() {
//ptr is std::unique_ptr<MyClass>
auto ptr = std::unique_ptr<MyClass>(new MyClass());
//...
//MyClass instance is disposed of when foo returns
}
A unique_ptr disposes of the controlled object by calling delete
. But, what if we want the unique_ptr to hold a dynamically allocated array of objects? In C++, a dynamically allocated array of objects must be disposed of by calling delete[]
. That's where comes a lesser-known feature of unique_ptr that it can be used to control the lifecycle of a dynamic array also:
//!!Undefined Behavior!!
auto pArr = std::unique_ptr<MyClass>(new MyClass[10]); //!Wrong
//Use a std::unique_ptr<T[]> for arrays instead.
auto pArr = std::unique_ptr<MyClass[]>(new MyClass[10]); //Correct
The unique_ptr also has an overloaded operator [] to access the array elements by index:
//Can use std::make_unique<T[]> to create std::unique_ptr<T[]>
auto ints = std::make_unique<int[]>(10);
for(int i=0; i < 10; i++)
ints[i] = i; //Access array element at index
At this point, you might ask why to bother about managing a dynamic array through unique_ptr when we have std::vector? In my opinion, we are better-off using std::vector in most cases. But there are a few circumstances where dynamic array through unique_ptr might be necessary or a better choice when the vector's dynamic expansion is not needed. Precisely, where exclusive ownership by unique_ptr is needed because an std::vector can be copied, or where the memory usage is a more pressing concern because an std::vector has a small memory overhead, or when we are dealing with a library that requires passing a pointer. Let's look at another example and a question on this subject in the next section.
We have a struct
Blob that has some data members. The ArrayBlobPtr is a unique_ptr type that can hold an array of unique_ptr<Blob
> instances. We have omitted ("_____
") the type of ArrayBlobPtr in its alias declaration:
struct Blob {
Blob(/*Data*/) {
//...
}
//...
};
using ArrayBlobPtr = _____; //Type omitted
Here is a conceptual illustration of ArrayBlobPtr:
A function createBlobs creates a specified number of Blob objects and fills them in a provided ArrayBlobPtr, as shown:
void createBlobs(ArrayBlobPtr& blobPtrs, int n) {
//Create Blobs
for(int i=0; i < n; ++i) {
blobPtrs[i] = std::make_unique<Blob>(/*Data*/);
}
//...
}
The function createBlobs is called as shown below:
//Create array of 10 unique_ptr<Blob>
auto blobPtrs = ArrayBlobPtr(new std::unique_ptr<Blob>[10]);
createBlobs(blobPtrs, 10);
What should be the correct type of ArrayBlobPtr in the alias declaration above ("_____
")? Select below (check Explanations
for details):