Deallocation must be symmetric, at run-time, to allocation. You
must delete what you new,
delete[] what you new[], and free() what you malloc() (although you shouldn't be doing that in C++ normally). That applies to every allocation, not just "parent" allocations. There is no reference counting magic or anything along those lines happening by default: In C++, you don't pay for what you don't ask for, but you don't get it, either.
Also, you have to make sure that for each allocation, the deallocation happens
exactly once. This can be tricky business, especially if (as is very often desirable) you have multiple things pointing to the same allocation.
This is why, in C++, we try to make use of the 'Resource Acquisition Is Initialization' idiom: generally, wrap allocations into constructors and deallocations into destructors, so you can't forget the deallocation. Of course, the simplest approach there only covers the case where the object "owns" the allocated memory.
For completeness' sake, that approach looks like this:
class Foo { Bar* myAllocation; Foo() : myAllocation(new Bar()) {} Foo(const Foo& other) : myAllocation(new Bar(*(other.myAllocation))) {} Foo& operator=(const Foo& rhs) { Foo other(rhs); std::swap(myAllocation, other.myAllocation); return *this; } ~Foo() { delete myAllocation; }}
Now when a Foo is copy-constructed, the Bar is "cloned" (note that this does not respect polymorphism, because you requested a new Bar, not a new type-of-thing-pointed-at-by-other-dot-myAllocation - sadly, C++ constructors aren't virtual, so you have to hack that kind of thing if you need it), with the pointer set to point at a newly allocated Bar which is a copy of the old one. When a Foo is default-constructed, the Bar is allocated and default-constructed. When a Foo destructs, the Bar is properly deallocated. The only complicated case is the assignment operator: when one Foo is assigned to another, what happens is that we copy-construct a temporary Foo, and then swap the pointers around so that the temporary Foo takes ownership of the current object's "old" Bar, and the current object takes ownership of the copied Bar. Finally, at the end of the function, the temporary Foo falls out of scope, so its destructor is called - deallocating the old Bar. This "copy-and-swap idiom" implicitly guards against self-assignment and also helps provide exception safety.
Anyway, in more complicated situations, we don't reinvent the wheel, but use library components. The standard library isn't much help here, but Boost provides several excellent tools, including boost::shared_ptr.
But then, the real problem here wasn't about memory management, but about creating a multi-dimensional array ;) So I will tell you again to look at Boost (in particular boost::multi_array), and also read
this.