As best as I'm able to determine, your code is correct with respect to what your comments expect is happening, prevents memory leaks, and is free of Undefined Behavior, which is all fine and good.
But consider, for a second, how much unnecessary boilerplate code you had to write to make this work. And then consider the following code using std::unique_ptr
, which is exactly identical to your code in terms of behavior, but removes the boilerplate that is responsible for deleting the memory, by using a class which handles that behavior automatically:
#include<memory>
int main()
{
auto x = std::make_unique<std::unique_ptr<int[]>[]>(20);
x[0] = std::make_unique<int[]>(5); //x[0] ==>[0,0,0,0,0]
x[1] = std::make_unique<int[]>(3); //x[1] ==>[0,0,0]
*x[0] = 10; //x[0][0] = 10
//This will no longer compile because, while technically correct, this kind of pointer arithmetic
//is inherently unsafe and prone to mistakes
//*(x+1)[0] = 20; //x[1][0] = 20
x[1][0] = 20;
cout << x[0][0] << endl;
cout << x[1][0] << endl;
//No more deletes needed; `std::unique_ptr` manages its own memory and deletes when needed
return 0;
}
Now consider the next code, which simplifies further by using a more robust idiom for dynamically-sized arrays, std::vector
:
#include<vector>
int main()
{
std::vector<std::vector<int>> x(20);
x[0].resize(5); //x[0] ==>[0,0,0,0,0]
x[1].resize(3); //x[1] ==>[0,0,0]
x[0][0] = 10;
x[1][0] = 20;
cout << x[0][0] << endl;
cout << x[1][0] << endl;
return 0;
}
This is a pretty clear case-study in why, for most purposes, you should prefer things like std::unique_ptr<T[]>
or std::vector<T>
for describing "Arrays of T
". And for 2d arrays, std::unique_ptr<std::unique_ptr<T[]>[]>
or std::vector<std::vector<T>>
, which describe the same semantics as your original code.