ProgrammerGuy123
ProgrammerGuy123

Reputation: 357

Syntax for dynamically allocating a 2D array of smart pointers

I need to dynamically allocate a two dimensional array of smart pointers but the syntax for it is confusing me. I need this to be dynamic:

std::unique_ptr<someClass> myArray[size1][size2];

So from what I understand I create a pointer to a pointer to the type:

someClass** myArray; //actaully the type is std::unique_ptr<someClass> but I'll just keep it simple

Then to allocate it I do:

myArray* = new someClass*[size1];
for(int i = 0; i < size1; i++)
    myArray[i] = new someClass[size2];

But this doesn't use smart pointers which means I will have to manually delete it later, and I don't know how to make these pointers smart pointers;

The type is std::unique_ptr but then I need a pointer to a pointer to the type, so I tried:

std::unique_ptr<std::unique_ptr<std::unique_ptr<someClass>>> myArray;

But after this I'm lost on how I would allocate it. Could someone please help me out?

Upvotes: 5

Views: 9197

Answers (2)

A Prec
A Prec

Reputation: 195

As an illustrative example, below is the syntax that can be used in C++ modern for creating and filling a 2D int array (of size 3 by 5) using smart pointer (unique_ptr) and other features such as make_uniqueand move().

unique_ptr<unique_ptr<int[]>[]>     smartPtr2D;
unique_ptr<int[]>                   smartPtr1D;

int dataCounter = 0;

smartPtr2D = make_unique< unique_ptr<int[]>[] >(3);
for (int i = 0; i<3; i++)
{
    smartPtr1D = make_unique<int[]>(5);
    for (int j = 0; j<5; j++)
    {
        smartPtr1D[j] = dataCounter;
        dataCounter++;
    }
    smartPtr2D[i] = move(smartPtr1D);
}

Upvotes: 4

user2880576
user2880576

Reputation: 141

I'll show you how to solve your problem specifically and how to approach problems like this one in general.

In general, just like any problem that gets too complex, try to break it down. The tool for breaking down complex type declarations in C and C++ has long been the "typedef". Here is how you approach it for cascaded type definitions like the one you are having: take the innermost wrapping type, i.e. the unique_ptr wrapping your class, and make a typedef for the type you want to wrap. Then, proceed doing the same for the type which that type is wrapping, until you are at the outermost type.

This is tangentially related to your question only, but I want to mention it, because you might run into a similar issue like the one you are having now with templates later. Since C++11 you can more conveniently also define aliases for types involving template parameters with the "using" clause: http://en.cppreference.com/w/cpp/language/type_alias. Go check out that link if that is of interest to you in this context or becomes relevant in the future!

To your specific problem. The function "test_dynamic_2darray1" builds a two-dimensional 10x10 array of smart pointers. When you run this code, you should see 100 lines of the output from the destructor, just when the managed array goes out of scope.

size_t destructor_count = 0;
class MyClass {
    public:
    ~MyClass() {
        std::cout << "Destructor call #" << ++destructor_count << std::endl;
    }
};

typedef std::unique_ptr<MyClass[]> ManagedC;

void test_dynamic_2darray1() {
    size_t dimension1 = 10, dimension2 = 10;

    auto managed_array = std::unique_ptr<ManagedC[]>(new ManagedC[dimension1]);
    for (size_t i = 0; i < dimension1; ++i)
        managed_array[i] = ManagedC(new MyClass[dimension2]);
}

Compare that to this code, where the destructors of the dynamically allocated class instances will not be called and you see no output:

void test_dynamic_2darray2() {
    size_t dimension1 = 10, dimension2 = 10;

    auto simple_array = new MyClass*[dimension1];
    for (size_t i = 0; i < dimension1; ++i)
        simple_array[i] = new MyClass[dimension2];
}

I hope I have been able to answer your question! :) Let me know if you want me to elaborate on something! I also wrote a related blog post the other day that may interest you: http://frankriesecodingblog.blogspot.com/2015/01/performance-of-dynamic-multi.html. I am posting it here, because it shows different approaches to multi-dimensional dynamic arrays and looks into the performance of the often suggested method of using vectors of vectors.

Last but not least, let me mention your use of int to iterate over arrays. I hope this isn't turning into a pet peeve of mine, but I see this being done a lot. You should probably use size_t. Why? For example, on my 64 bit machine "int" is 32 bit, but addresses, represented by size_t, are 64 bit. This misuse of int has been the cause of many bugs, in particular for porting 32 bit applications to 64 bit machines. If you need a signed type, for uses such as offsets between array addresses, a better use would probably be ptrdiff_t.

Upvotes: 7

Related Questions