Implementing a Dynamically Sized Matrix

Problem

You need to store and represent Matricies of numbers where the dimensions (number of rows and columns) are not known at compile time.

Solution

Example 11-28 provides a general purpose and efficient implementation of a dynamically sized matrix class using the stride iterator from Recipe 11.12 and a valarray.

Example 11-28. matrix.hpp

#ifndef MATRIX_HPP
#define MATRIX_HPP

#include "stride_iter.hpp" // see Recipe 11.12

#include 
#include 
#include 

template
class matrix
{
public:
 // public typedefs
 typedef Value_T value_type;
 typedef matrix self;
 typedef value_type* iterator;
 typedef const value_type* const_iterator;
 typedef Value_T* row_type;
 typedef stride_iter col_type;
 typedef const value_type* const_row_type;
 typedef stride_iter const_col_type;

 // constructors
 matrix( ) : nrows(0), ncols(0), m( ) { }
 matrix(int r, int c) : nrows(r), ncols(c), m(r * c) { }
 matrix(const self& x) : m(x.m), nrows(x.nrows), ncols(x.ncols) { }

 template
 explicit matrix(const valarray& x) 
 : m(x.size( ) + 1), nrows(x.size( )), ncols(1) 
 {
 for (int i=0; i
 explicit matrix(const matrix& x) 
 : m(x.size( ) + 1), nrows(x.nrows), ncols(x.ncols) 
 {
 copy(x.begin( ), x.end( ), m.begin( ));
 }

 // public functions
 int rows( ) const { return nrows; }
 int cols( ) const { return ncols; }
 int size( ) const { return nrows * ncols; }

 // element access
 row_type row_begin(int n) { return &m[n * cols( )]; }
 row_type row_end(int n) { return row_begin( ) + cols( ); }
 col_type col_begin(int n) { return col_type(&m[n], cols( )); }
 col_type col_end(int n) { return col_begin(n) + cols( ); }
 const_row_type row_begin(int n) const { return &m[n * cols( )]; }
 const_row_type row_end(int n) const { return row_begin( ) + cols( ); }
 const_col_type col_begin(int n) const { return col_type(&m[n], cols( )); }
 const_col_type col_end(int n) const { return col_begin( ) + cols( ); }
 iterator begin( ) { return &m[0]; }
 iterator end( ) { return begin( ) + size( ); }
 const_iterator begin( ) const { return &m[0]; }
 const_iterator end( ) const { return begin( ) + size( ); }

 // operators
 self& operator=(const self& x) { 
 m = x.m; nrows = x.nrows; ncols = x.ncols; return *this; 
 }
 self& operator=(value_type x) { m = x; return *this; }
 row_type operator[](int n) { return row_begin(n); }
 const_row_type operator[](int n) const { return row_begin(n); }
 self& operator+=(const self& x) { m += x.m; return *this; }
 self& operator-=(const self& x) { m -= x.m; return *this; }
 self& operator+=(value_type x) { m += x; return *this; }
 self& operator-=(value_type x) { m -= x; return *this; }
 self& operator*=(value_type x) { m *= x; return *this; }
 self& operator/=(value_type x) { m /= x; return *this; }
 self& operator%=(value_type x) { m %= x; return *this; }
 self operator-( ) { return -m; }
 self operator+( ) { return +m; }
 self operator!( ) { return !m; }
 self operator~( ) { return ~m; }

 // friend operators
 friend self operator+(const self& x, const self& y) { return self(x) += y; }
 friend self operator-(const self& x, const self& y) { return self(x) -= y; }
 friend self operator+(const self& x, value_type y) { return self(x) += y; }
 friend self operator-(const self& x, value_type y) { return self(x) -= y; }
 friend self operator*(const self& x, value_type y) { return self(x) *= y; }
 friend self operator/(const self& x, value_type y) { return self(x) /= y; }
 friend self operator%(const self& x, value_type y) { return self(x) %= y; }
private:
 mutable valarray m;
 int nrows;
 int ncols;
};

#endif

Example 11-29 shows how you might use the matrix template class.

Example 11-29. Using the matrix template

#include "matrix.hpp"

#include 

using namespace std;

int main( ) {
 matrix m(2,2);
 m = 0;
 m[0][0] = 1;
 m[1][1] = 1;
 m *= 2;
 cout << "(" << m[0][0] << "," << m[0][1] << ")" << endl;
 cout << "(" << m[1][0] << "," << m[1][1] << ")" << endl;
}

The program in Example 11-29 produces the following output:

(2,0)
(0,2)

 

Discussion

The design of the matrix template in Example 11-28 is heavily inspired by Bjarne Stroustrup's matrix template from The C++ Programming Language, Third Edition (Addison Wesley). Stroustrup's implementation differs in that its iterator uses slice and a pointer to the valarray for indexing. The matrix implementation in Example 11-27 uses instead the stride iterator from Recipe 11.12, making the iterators more compact and, on some implementations, more efficient.

The matrix template class allows indexing of the element ith row and jth column using a double subscripting operation. For example:

matrix m(100,100);
cout << "the element at row 24 and column 42 is " << m[24][42] << endl;

The matrix template class also provides begin and end member functions, which means that it can be used easily with the various STL algorithms.

There is a line of code in Example 11-28 that might have caused you to raise your eyebrows. That is the declaration:

mutable valarray m;

The declaration of the member field m as being mutable was a necessary evil. If it wasn't for this line I would not have been able to provide const iterators, because you can't create an iterator to a const valarray.

See Also

Recipe 11.15 and Recipe 11.16





C++ Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2006
Pages: 241
Simiral book on Amazon

Flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net