Short square matrix class in C++ using an array I'm a novice at C++ in comparison with other languages, so as an exercise I wrote a square Matrix class that doesn't use STL.

#ifndef MATRIX_H
#define MATRIX_H
#include &lt;initializer_list&gt;
#include &lt;algorithm&gt;


class Matrix {
public:
  Matrix(std::size_t sz) : elements{new int[sz * sz]}, size{sz} {}
  Matrix(std::size_t sz, std::initializer_list&lt;int&gt; elem) : Matrix(sz) {
    std::copy(elem.begin(), elem.end(), elements);
  }
  ~Matrix() { delete[] elements; }

  /** Get the matrix element at row i and column j. */
  int get(std::size_t i, std::size_t j) const {
    return elements[convert(i, j)];
  }

  /** Set the value of the matrix at row i and column j and return the old value. */
  int set(std::size_t i, std::size_t j, int new_value) {
    std::size_t idx = convert(i, j);
    int old_value = elements[idx];
    elements[idx] = new_value;
    return old_value;
  }

  std::size_t width() const {
    return size;
  }


private:
  int *elements;
  std::size_t size;

  inline std::size_t convert(std::size_t i, std::size_t j) const {
    if (i &gt;= size || j &gt;= size) {
      throw std::exception("Matrix indices out of bounds");
    }
    std::size_t idx = i * size + j;
    return idx;
  }
};

#endif


Critiques are welcome!  In particular, I'd be happy to get advice on the following:


Is this an appropriate usage of exception handling?  Is there a better option than just tossing up a std::exception if the matrix indices are out of bounds?
Do the constructors and destructors make sense?
Any general comments on writing in idiomatic C++ style.
(1) The exception is a good way of handling such errors, but it may be a good idea to provide a non-throwing, less safe accessors. For example, std::vector::at() will throw an std::out_of_range if requested index is not valid, but std::vector::operator[] will silently accept all values. That's because, if individual elements are accessed very frequently (some complicated math algorithm), all those ifs may impact the performance. So, as it goes for the exceptions, two suggestions:


throw std::out_of_range instead of plain std::exception to indicate what kind of error just happened,
do not throw in every accessor (you only have one, which leads to point 4, see below).


(2) Yes, they do. But, the basic question is: do you really need to be able to specify size of a matrix at runtime? Perhaps it would be better to use compile-time constant size:

template &lt;size_t Size&gt;
class Matrix
{
private:
  int elements[Size*Size];
  //...
};


Then, you would use it this way:

Matrix&lt;4&gt; mat4;


In this version, no destructor would be necessary and no memory allocation required. Because of that, this version also can be faster.

(3) "Idiomatic C++ style" has become a very fragile term recently (IMHO). But, you could probably provide support for accessing elements of your matrix using operator[] (see point 4).

(4) It would be nice if users of your Matrix could write simply:

Matrix&lt;4&gt; mat; // Or: Matrix mat {4}, in dynamically-allocated version
mat[3][3] = 1; // set elements in row 3, column 3 (note, that indices are from 0 to 3)


Or, equivalently, in simpler version:

mat[15] = 1;


(*) Simple version only requires the presence of operator[]:

template &lt;size_t Size&gt;
class Matrix
{
    int&amp; operator[](size_t index)
    {
        return elements[index];
    }

    const int&amp; operator[](size_t index) const
    {
        return elements[index];
    }
};


Note, that two version are provided, so you can do this:

Matrix&lt;4&gt; mat;
mat[5] = 1;


but not this:

const Matrix&lt;4&gt; constMat;
constMat[2] = 3; // compilation error: cannot assign to const reference


(**) The more elegant, but also more difficult version (nested [][] syntax), requires, that operator[] of the matrix must return an object (or an array) that also supports [] notation.

The easier way to achieve this would be to to change the T* elements to T** elements (or, in templated version, elements[Size*Size] to elements[Size][Size]). Note, that in this case you do not need the convert() function, since you can use the indices explicitly.

You could also declare simple Vector struct (I will limit myself here to templated version):

template &lt;size_t Size&gt;
struct Vector
{
private:
  int elements[Size];

public:
  int&amp; operator[](size_t index)
  {
    return elements[index];
  }

  const int&amp; operator[](size_t index) const
  {
    return elements[index];
  }
};


And then implement your Matrix using Vector:

template &lt;size_t Size&gt;
class Matrix
{
private:
  Vector&lt;Size&gt; columns[Size];

public:
  Vector&lt;Size&gt;&amp; operator[](size_t index)
  {
    return columns[index];
  }

  const Vector&lt;Size&gt;&amp; operator[](size_t index) const
  {
    return columns[index];
  }
};


(1.1) Now, after we covered the topic of operator[], we can return to the exceptions once more. To aid those unsafe operator[]s, we can add more safe, explicit at() function:

template &lt;size_t Size&gt;
class Matrix
{
private:
  Vector&lt;Size&gt; columns[Size];

public:
  Vector&lt;Size&gt;&amp; at(size_t index)
  {
    if (index &gt;= Size) throw std::out_of_range("Invalid index!");
    return columns[index];
  }

  const Vector&lt;Size&gt;&amp; at(size_t index) const
  {
    if (index &gt;= Size) throw std::out_of_range("Invalid index!");
    return columns[index];
  }
};