简体   繁体   中英

Deleting templated C++ 2-dimensional array

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;
};

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM