C++ FAQ Celebrating Twenty-One Years of the C++ FAQ!!!
(Click here for a personal note from Marshall Cline.)
Section 16:
16.1 Does delete p delete the pointer p, or the pointed-to-data *p?
16.2 Is it safe to delete the same pointer twice?
16.3 Can I free() pointers allocated with new? Can I delete pointers allocated with malloc()?
16.4 Benefits of new over malloc()?
16.5 Can I use realloc() on pointers allocated via new?
16.6 Checking for NULL after p = new Fred()?
16.7 How can I convince my (older) compiler to automatically check new to see if it returns NULL?
16.8 Checking for NULL before delete p?
16.9 What are the two steps that happen when I say delete p?
16.10 Does p = new Fred() leak memory if the ctor throws an exception?
16.11 How do I allocate / unallocate an array of things?
16.12 What if I forget the [] when deleteing an array allocated via new T[n]?
16.13 Can I drop the [] when deleteing an array of some built-in type (char, int, etc)?
16.14 After p = new Fred[n], how does the compiler know there are n objects to be destructed during delete[] p?
16.15 Is it legal (and moral) for a member function to say delete this?
16.16 How do I allocate multidimensional arrays using new?
16.17 How to simplify the Matrix code from the previous FAQ?
16.18 How to make the Matrix class generic?
16.19 What's another way to build a Matrix template?
16.20 Does C++ have arrays whose length can be specified at run-time?
16.21 Allocating all objects via new, not local/global/static?
16.22 How do I do simple reference counting?
16.23 How do I provide reference counting with copy-on-write semantics?
16.24 How do I provide reference counting with copy-on-write semantics for a hierarchy of classes?
16.25 Preventing people from subverting the reference counting mechanism?
16.26 Can I use a garbage collector in C++?
16.27 What are the two kinds of garbage collectors for C++?
16.28 Where can I get more info on garbage collectors for C++?
[16.19] What's another way to build a Matrix template?

Use the standard vector template, and make a vector of vector.

The following uses a std::vector<std::vector<T> > (note the space between the two > symbols).

#include <vector>

template<typename T>  // See section on templates for more
class Matrix {
public:
  Matrix(unsigned nrows, unsigned ncols);
  // Throws a BadSize object if either size is zero
  class BadSize { };

  // No need for any of The Big Three!

  // Access methods to get the (i,j) element:
  T&       operator() (unsigned i, unsigned j);         subscript operators often come in pairs
  T const& operator() (unsigned i, unsigned j) const;   subscript operators often come in pairs
  // These throw a BoundsViolation object if i or j is too big
  class BoundsViolation { };

  unsigned nrows() const;  // #rows in this matrix
  unsigned ncols() const;  // #columns in this matrix

private:
  std::vector<std::vector<T> > data_;
};

template<typename T>
inline unsigned Matrix<T>::nrows() const
{ return data_.size(); }

template<typename T>
inline unsigned Matrix<T>::ncols() const
{ return data_[0].size(); }

template<typename T>
inline T& Matrix<T>::operator() (unsigned row, unsigned col)
{
  if (row >= nrows() || col >= ncols()) throw BoundsViolation();
  return data_[row][col];
}

template<typename T>
inline T const& Matrix<T>::operator() (unsigned row, unsigned col) const
{
  if (row >= nrows() || col >= ncols()) throw BoundsViolation();
  return data_[row][col];
}

template<typename T>
Matrix<T>::Matrix(unsigned nrows, unsigned ncols)
  : data_ (nrows)
{
  if (nrows == 0 || ncols == 0)
    throw BadSize();
  for (unsigned i = 0; i < nrows; ++i)
    data_[i].resize(ncols);
}
Note how much simpler this is than the previous: there is no explicit new in the constructor, and there is no need for any of The Big Three (destructor, copy constructor or assignment operator). Simply put, your code is a lot less likely to have memory leaks if you use std::vector than if you use explicit new T[n] and delete[] p.

Note also that std::vector doesn't force you to allocate numerous chunks of memory. If you prefer to allocate only one chunk of memory for the entire matrix, as was done in the previous, just change the type of data_ to std::vector<T> and add member variables nrows_ and ncols_. You'll figure out the rest: initialize data_ using data_(nrows * ncols), change operator()() to return data_[row*ncols_ + col];, etc.