4

I need to get an input N from the user and generate a N*N matrix. How can I declare the matrix? Generally, the size of the array and matrix should be fixed at the declaration, right? What about vector<vector<int>> ? I never use this before so I need suggestion from veteran.

Steve Guidi
  • 19,700
  • 9
  • 74
  • 90
skydoor
  • 25,218
  • 52
  • 147
  • 201

3 Answers3

20

A vector<vector<int>> (or vector<vector<int> >, for older compilers) can work well, but it's not necessarily the most efficient way to do things1. Another that can work quite nicely is a wrapper around a single vector, that keeps track of the "shape" of the matrix being represented, and provides a function or overloaded operator to access the data:

template <class T>
class matrix { 
    int columns_;
    std::vector<T> data;
public:
    matrix(int columns, int rows) : columns_(columns), data(columns*rows) {}

    T &operator()(int column, int row) { return data[row*columns_+column]; }
};

Note that the C++ standard only allows operator[] to take a single operand, so you can't use it for this job, at least directly. In the example above, I've (obviously enough) used operator() instead, so subscripts look more like Fortran or BASIC than you're accustomed to in C++. If you're really set on using [] notation, you can do it anyway, though it's mildly tricky (you overload it in the matrix class to return a proxy, then have the proxy class also overload operator[] to return (a reference to) the correct element -- it's mildly ugly internally, but works perfectly well anyway).

Here's an example of how to implement the version using multiple overloads of operator[]. I wrote this (quite a while) before most compilers included std::vector, so it statically allocates an array instead of using a vector. It's also for the 3D case (so there are two levels of proxies involved), but with a bit of luck, the basic idea comes through anyway:

template<class T, int size>
class matrix3 {

    T data[size][size][size];

    friend class proxy;
    friend class proxy2;

    class proxy { 
        matrix3 &m_;
        int index1_, index2_;
    public:
        proxy(matrix3 &m, int i1, int i2) 
            : m_(m), index1_(i1), index2_(i2) 
        {}

        T &operator[](int index3) { 
            return m_.data[index1_][index2_][index3];
        }
    };

    class proxy2 { 
        matrix3 &m_;
        int index_;
    public:
        proxy2(matrix3 &m, int d) : m_(m), index_(d) { }

        proxy operator[](int index2) { 
            return proxy(m_, index_, index2);
        }
    };
public:
    proxy2 operator[](int index) {
        return proxy2(*this, index);
    }
};

Using this, you can address the matrix with the normal C++ syntax, such as:

matrix3<double, size> m;

for (int x=0; x<size; x++)
    for (int y = 0; y<size; y++)
        for (int z = 0; z<size; z++)
            m[x][y][z] = x*100 + y * 10 + z;

  1. An std::vector is normally implemented as a pointer to some dynamically allocated data, so something like a vector<vector<vector<int>>> will dereference two levels of pointers to get to each piece of data. This means more memory references, which tend to be fairly slow on most modern processors. Since each vector contains separately allocated data, it also leads to poor cache locality as a rule. It can also waste some space, since each vector stores both its allocated size and the size in use.
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Care to shed some light on the inefficiency part of vector of vectors? – Murali VP Feb 07 '10 at 05:35
  • 3
    @Murali:basically, you've got inefficiency in a couple of ways. First of all, even though all the sub-vectors (so to speak) are going to be the same size, each one stores its own length. Second, a vector is (at least normally) implemented using a pointer to dynamically allocated data, so with a vector of vectors, you need to go through two levels of pointers to get to the real data. Using a single vector involves multiplication instead, which was once a bad tradeoff, but with CPUs faster than memory, it's now almost always a win (extra CPU time vs. possibility of extra memory access). – Jerry Coffin Feb 07 '10 at 05:45
  • 2
    You could also use std::valarray since it already supports a variety of subset access mechanisms. – MSN Feb 07 '10 at 06:31
  • 3
    @MSN:You could -- `valarray` is something I've mentioned a few times in the past, but frankly that's a banner I've decided to quit waving, so to speak. Simple uses of it might make sense, but the minute you get into slice, gslice, slice_array, etc., it becomes completely opaque to at least 99% of the C++ community. Worse, it was really designed for vector processors; it's relatively cache unfriendly, so even if you know what it's doing, and a reader does as well, it'll often be quite an inefficient way of doing it anyway. – Jerry Coffin Feb 07 '10 at 16:26
  • but think about all the typing you would save! :) – MSN Feb 07 '10 at 18:34
3

Boost implements matrices (supporting mathematical operations) in its uBLAS library, and provides usage syntax like the following.

#include <boost/numeric/ublas/matrix.hpp>

int main(int argc, char* argv[])
{
    unsigned int N = atoi(argv[1]);
    boost::matrix<int> myMatrix(N, N);

    for (unsigned i = 0; i < myMatrix.size1 (); ++i)
        for (unsigned j = 0; j < myMatrix.size2 (); ++j)
            myMatrix(i, j) = 3 * i + j;

    return 0;
}
Steve Guidi
  • 19,700
  • 9
  • 74
  • 90
0

Sample Code:

template<class T>
class Array2D
{
public:
    Array2D(int a, int b)  
    {
        num1 = (T**)new int [a*sizeof(int*)];
        for(int i = 0; i < a; i++)
            num1[i] = new int [b*sizeof(int)];

        for (int i = 0; i < a; i++) {
            for (int j = 0; j < b; j++) {
                num1[i][j] = i*j;
            }
        }
    }
    class Array1D
    {
    public:
        Array1D(int* a):temp(a) {}
        T& operator[](int a)
        {
            return temp[a];
        }
        T* temp;
    };

    T** num1;
    Array1D operator[] (int a)
    {
        return Array1D(num1[a]);
    }
};


int _tmain(int argc, _TCHAR* argv[])
{
    Array2D<int> arr(20, 30);

    std::cout << arr[2][3];
    getchar();
    return 0;
}

    enter code here