0

I'm trying to create a template to allocate dynamically an 2D matrix. Usually what I do is:

float **Allocate_matrix_float (int m, int n)
{
 float **v;  
 int   i;    
 if (m < 1 || n < 1) {
     printf ("** Invalid parameter **\n");
     return (NULL);
     }

  v = (float **) calloc (m, sizeof(float *));
  if (v == NULL) {
     printf ("** Unsufficient memory  **");
     return (NULL);
     }

  for ( i = 0; i < m; i++ ) {
      v[i] = (float*) calloc (n, sizeof(float));
      if (v[i] == NULL) {
         printf ("**  Unsufficient memory  **");
         return (NULL);
         }
      }
  return (v); 
}


float **free_matrix_float (int m, int n, float **v)
{
  int  i;  
  if (v == NULL) return (NULL);
  if (m < 1 || n < 1) { 
     printf ("** invalid parameter**\n");
     return (v);
     }
  for (i=0; i<m; i++) free (v[i]); 
  free (v);     
  return (NULL); 
}

However I'd like to create a template to allocate any type of 2D matrix. Can anyone help me?
The ideal would be something like:

template<typename T> 
T**Allocate_matrix(int n, int m)
...
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Well what have you tried so far? – NathanOliver May 16 '16 at 17:48
  • 2
    I would also suggest you use a `std::vector` instead of raw pointers and make you life easier. – NathanOliver May 16 '16 at 17:49
  • 1
    Please stop the hurting of my eyes. Use `new` and `delete` not `calloc` and `free`. – Jonathan Mee May 16 '16 at 17:51
  • 1
    Your `float** foo = Allocate_matrix_float(m, n)` should read: `vector> foo(m, vector(n))` – Jonathan Mee May 16 '16 at 17:54
  • What I've tried was to substitute all floats by T. Well, it compiles, but doesn't execute. – Goncalves L Augusto May 16 '16 at 17:57
  • 2
    Virtually all of your code vanishes if you take the above advice about using `std::vector`. You will also be mocked mercilessly if you persist in using `malloc` and `free` in C++ in place of `new` and `delete`. Consider what will happen if you `malloc` a matrix of objects rather than `new`ing it. If you need good performance, consider replacing the 2D vector with a 1D vector and performing the 2D indexing (`row * num_Columns + column`) yourself. Vector of vector and array of array have some nasty cache-resistant side effects – user4581301 May 16 '16 at 18:04
  • @user4581301 I agree with everything you say except array of array being cache unfriendly. A 2d array is laid out in memory just like a 1d array and is cache friendly. A double pointer(when naively allocated) is the same as a 2d vector. – NathanOliver May 16 '16 at 18:24
  • This is why my comment refers to "array of array", not 2D array. Clarification: My concern is with the `TYPE ** matrix = new TYPE*[m];` and the array-allocating `for` loop that often follows that is often called a 2D array. – user4581301 May 16 '16 at 19:13

2 Answers2

1

All of your allocation and deallocation code can be replaced by

std::vector<std::vector<float>> matrix(m, std::vector(n));

Seriously. It even deallocates itself when it goes out of scope, so you have almost no memory management issues.

#include <iostream>
#include <vector>

int main()
{
    size_t m;
    size_t n;

    std::cin >> m >> n;

    // floats initialized to 0.0
    std::vector<std::vector<float>> fltmatrix(m, std::vector<float>(n));
    // doubles initialized to 0.0
    std::vector<std::vector<double>> dblmatrix(m, std::vector<double>(n));
    // bools initialized to true
    std::vector<std::vector<bool>> boolmatrix(m, std::vector<bool>(n), true);
    // ints initialized to 42
    std::vector<std::vector<int>> intmatrix(m, std::vector<int>(n, 42));
} <-- all vectors are released here.

Practically no effort required.

However, because each vector is it's own independent entity and you have m+1 vectors, you have m+1 different places in memory that your program needs to look to return a value. This can have a really bad impact on your programs performance as small matrices, say a 3x3, can't take full advantage of the CPU's caching and this can be very, very noticeable when crunching large numbers of matrices. If you don't care, stop reading and go with the simple vector of vector approach.

If you do care, wrap a 1D vector in a class:

#include <iostream>
#include <vector>

template<class TYPE>
class Matrix
{
private:
    size_t rows, columns;
    std::vector<TYPE> matrix;
public:
    Matrix(size_t numrows, size_t numcols) :
            rows(numrows), columns(numcols), matrix(rows * columns)
    {
    }

    Matrix(size_t numrows, size_t numcols, TYPE init) :
            rows(numrows), columns(numcols), matrix(rows * columns, init)
    {
    }

    TYPE & operator()(size_t row, size_t column)
    {
        // check bounds here
        return matrix[row * columns + column];
    }

    TYPE operator()(size_t row, size_t column) const
    {
        // check bounds here
        return matrix[row * columns + column];
    }

    size_t getRows() const
    {
        return rows;
    }
    size_t getColumns() const
    {
        return columns;
    }
    friend std::ostream & operator<<(std::ostream & out, const Matrix & in)
    {
        for (int i = 0; i < in.getRows(); i++)
        {
            for (int j = 0; j < in.getColumns(); j++)
            {
                out << in(i, j) << ' ';
            }
            out << std::endl;
        }

        return out;
    }
};

int main()
{
    size_t m;
    size_t n;

    std::cin >> m >> n;

    // floats initialized to 0.0
    Matrix<float> fltmatrix(m, n);
    std::cout << fltmatrix << std::endl;
    // doubles initialized to 0.0
    Matrix<double> dblmatrix(m, n);
    std::cout << dblmatrix << std::endl;
    // bools initialized to true
    Matrix<bool>  boolmatrix(m, n, true);
    std::cout << boolmatrix << std::endl;
    // ints initialized to 42
    Matrix<int> intmatrix(m, n, 42);
    std::cout << intmatrix << std::endl;
}

More effort, but should be faster. Profile your program to see if Matrix is right for you.

operator<< is included as an output convenience and as an example of how to access Matrix's cells.

And if you just have to use an array... Things get a lot uglier. For one thing, you will have to be Rule of Three (and possibly Rule of Five) compliant and pick up a bunch of extra functions that, frankly, you're probably not going to get right the first few times.

I'm not even sure I can get it right if I just bang one out, and I have a perfectly good alternative, so I'm not going to. What you can gain is a matrix that does not spend time initializing the matrix before use. If that is an issue (profile, profile, profile!) I'd call that a new question. The current question uses calloc so it doesn't look like OP is concerned.

user4581301
  • 33,082
  • 7
  • 33
  • 54
0

As paddy mentions here using vector-of-vector is not practical, difficult to change and suffers from cache misses. As well as using bare pointers is impractical in terms of C++ which provides better tools like operator overloading. Taking paddy's implementation as basis your 2d matrix can be implemented in the following way:

template <class T>
class SimpleMatrix
{
public:
    SimpleMatrix( int rows, int cols, const T& initVal = T() )
        : m_data( rows * cols, initVal )
        , m_rows( rows )
        , m_cols( cols )
    {    
    }

    // Direct vector access and indexing
    operator const vector<T>& () const        { return m_data; }
    int Index( int row, int col ) const       { return row * m_cols + col; }

    // Get a single value
          T & Value( int row, int col )       { return m_data[Index(row,col)]; }
    const T & Value( int row, int col ) const { return m_data[Index(row,col)]; }

    // Proxy structure to allow [][] indexing
    struct Proxy
    {
    private:
        friend class SimpleMatrix<T>;
        SimpleMatrix<T>* m_matrix;
        int m_row;
        Proxy( SimpleMatrix<T>* m, int row ) : m_matrix(m), m_row(row) {}
    public:
              T & operator[] ( int col )       { return m_matrix->Value(m_row, col); }
        const T & operator[] ( int col ) const { return m_matrix->Value(m_row, col); }
    };

          Proxy operator[]( int row )        { return Proxy(this, row); }
    const Proxy operator[]( int row ) const  { return Proxy(const_cast<SimpleMatrix<T>*>(this), row); }

private:
    vector<T> m_data;
    int m_rows;
    int m_cols;
};

And use it in the following way:

SimpleMatrix<int> m(10, 2);
const SimpleMatrix<int>& cm = m;
m[1][1] = 1;
cout << cm[1][1];

This will also allow you to check the boundaries of the index.

Community
  • 1
  • 1
Teivaz
  • 5,462
  • 4
  • 37
  • 75