1

Annoyance over C++'s requirement to pass a dimension in a 2-d array got me working on a templated Matrix class. I've been coding in C# for a bit, so I'm sure I'm a little rusty here.

Issue is, I get a heap exception as soon as I hit the destructor, which is trying to delete the 2-d array.

Any help gratefully accepted!

template <typename T>
class Matrix {
public:
    Matrix(int m, int n) : nRows(m), nCols(n) { 
        pMatrix = new T * [nRows]; 
        for (int i = 0; i < nCols; i++) {
            pMatrix[i] = new T[nCols];
        }
    }
    ~Matrix() { 
        if (pMatrix != NULL) { 
            for (int i = 0; i < nRows; i++) { delete[] pMatrix[i]; }
            delete[] pMatrix;
        }
    }
    T ** GetMatrix() const { return pMatrix; }
    T * Row(int i) const { return pMatrix[i]; }
    inline T Cell(int row, int col) const { return pMatrix[row][col]; }
    inline int GetNRows() const { return nRows; }
    inline int GetNCols() const { return nCols; }
private:
    int nRows, nCols;
    T ** pMatrix;
};
Dana
  • 347
  • 2
  • 9
  • 1
    Why do you need to return a `T**` and a `T*`? To me that seems like exposing inner implementation details. And you could simplify the code by using a single `std::vector` and playing with the indices for access. – juanchopanza Dec 02 '12 at 08:39
  • Completely concur with Juan. You can make this *significantly* simpler by using an `std::vector<>` and a simple `i*cols+j` to get the `[i][j]` element. The only thing you would be missing is the ability to perform double-bracket dereferencing, and even that you can conquer with a creative subclass that returns a row-iterator. – WhozCraig Dec 02 '12 at 08:59
  • Agreed, and see answer for an even more elegant solution similar to these. – Dana Dec 04 '12 at 03:19

2 Answers2

3

This is the bug:

for (int i = 0; i < nCols; i++) {
        pMatrix[i] = new T[nCols];
}

The loop should be until nRows, not nCols.

Other than that, let me tell you about something I did when I got tired of allocating 2-d arrays. I had to do a 3-d array. I used a map, that mapped from a coordinate - a struct holding x, y, z to the type I wanted.

I worked fast, and no need to allocate or deallocate. Assigning to a coordinate was simply done by

mymap[Coord(x, y, z)] = whatever...

Of course I needed to define the Coord struct and overload the < operator, but I found that way more comvenient than trying to allocate and deallocate a 3-d array.

Of course you will need to check if this scheme is fast enough for you. I used it to draw cells within a big cube using OpenGL and had no complaints at all.

Israel Unterman
  • 11,748
  • 2
  • 22
  • 31
1

Concerning the bug, @CodeChords_man explained it right. I have notes on implementation. I recommend to look through this wonderful FAQ post.

You should not use dynamic memory allocation unless you are 100% sure that

  1. You really need it
  2. You know how to implement it

I don't know of the first, and how the performance is crucial for you. But as for the second, you at least violated the rule of three. You class is very unsafe to use. If you copy it, the memory buffer will then be double-deleted.

You should not afraid to used STL containers, they are fast and optimized. At least the std::vector, it is as fast as the raw pointer in many scenarios. You can rewrite you class using std::vector as follows:

template <typename T>
class Matrix {
public:
  typedef std::vector<T> MatrixRow;
  typedef std::vector<MatrixRow> MatrixBody;

  Matrix(int m, int n) : nRows(m), nCols(n), _body(m, MatrixRow(n)) {}

  const MatrixBody& GetMatrix() const { return _body; }
  const MatrixRow& GetRow(int i) const { return _body[i]; }

  inline T Cell(int row, int col) const { return _body[row][col]; }
  inline int GetNRows() const { return nRows; }
  inline int GetNCols() const { return nCols; }
private:
  int nRows, nCols;
  MatrixBody _body;
};

Since this class is not using dynamic memory allocation, it is safe to copy and assign. You also don't need to explicitly store nRows and nCols in this case; you can use _body.size() and _body[0].size() instead.

Concerning underlying vector of vectors, it is dereferenced using the same [i][j] construction. It is easily iterated with begin() and end(). And if you absolutely need to use the raw pointer in some routine, you can always access it with &row[0].

The only possible difficulty is that you cannot easily convert MatrixBody to T**. But think it twice, maybe you don't really need to use T** at all.

Community
  • 1
  • 1
Mikhail
  • 18,155
  • 5
  • 56
  • 129
  • Couldn't agree with you more, on all counts! I've fixed the bug; I'll go after using STL now. I originally come from a deeply embedded environment where STL can't be used (size). This is a good tip, and I'll try it and let you know how it works. – Dana Dec 02 '12 at 21:02
  • Glad for you! Consider upvoting or even accepting my answer then :) – Mikhail Dec 02 '12 at 21:05
  • I'm getting greedy now - I'm going to go figure out how to overload the []s so I don't have to use the awkward "GetMatrix" syntax :) – Dana Dec 02 '12 at 21:24
  • You can use `MatrixBody` as a matrix itself. In this case you can go without implementing any additional class. You won't be able to construct it with `(m, n)` in this case, however. Anyway, implementing `operator[]()` is easy, straightforward and appropriate in this case, so go for it. Remember, you only need to replace `GetRow` with it. After you get `vector`, the second `[]` will work automatically. – Mikhail Dec 02 '12 at 21:40
  • I was just coming around to the question of why bother with the Matrix class. Nice construction is what I got to, too. Thanks for the tip on the operator[] - my search had just turned up the need for a proxy to operate the second [] on, but you're right, the vector fills that need! I'd upvote you three times if I could. – Dana Dec 02 '12 at 21:45
  • Thanks again - final class follows. #include template class Matrix { public: typedef std::vector MatrixRow; typedef std::vector MatrixBody; Matrix(int m, int n) : nRows(m), nCols(n), _body(m, MatrixRow(n)) {} inline int GetNRows() const { return nRows; } inline int GetNCols() const { return nCols; } inline MatrixRow & operator[](int index) { return _body[index]; } inline MatrixBody & Get() { return _body; } private: int nRows, nCols; MatrixBody _body; }; – Dana Dec 06 '12 at 01:59
  • I have tried everything I can think of to format the above, but without success. At least the class is there, if you'd like to see it. – Dana Dec 06 '12 at 02:00
  • You don't have to explicitly use `inline` - methods, defined within the class declaration, are implicitly marked inline. I recommend to add const-flavors of `Get()` and `operator[]()`. I also suggest to rename `Get()` to more meaningful `GetBody()`. – Mikhail Dec 06 '12 at 07:03