Д. Стефенс - C++. Сборник рецептов
Пример 11.28. matrix.hpp
#ifndef MATRIX_HPP
#define MATRIX_HPP
#include "stride_iter.hpp" // см. рецепт 11.12
#include <valarray>
#include <numeric>
#include <algorithm>
template<class Value_T>
class matrix {
public:
// открытые имена, вводимые typedef
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<value_type*> col_type;
typedef const value_type* const_row_type;
typedef stride_iter<const value_type*> const_col_type;
// конструкторы
matrix() : nrows(0), ncols(0), m() {}
matrix(int r, int c) : nrows(r), ncols(c), m(r * с) {}
matrix(const self& x) : m(x.m), nrows(x.nrows), ncols(x.ncols) {}
template<typename T>
explicit matrix(const valarray<T>& x)
: m(x.size() + 1), nrows(x.size()), ncols(1) {
for (int i=0; i<x.size(); ++i) m[i] = x[i];
}
// позволить конструирование из матриц других типов
template<typename T> explicit matrix(const matrix<T>& x)
: m(x.size() + 1), nrows(x.nrows), ncols(x.ncols) {
copy(x.begin(), x.end(), m.begin());
}
// открытые функции
int rows() const { return nrows; }
int cols() const { return ncols; }
int size() const { return nrows * ncols; }
// доступ к элементам
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(); }
// операторы
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 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<Value_T> m;
int nrows;
int ncols;
};
#endif
Пример 11.29 показывает, как можно использовать шаблонный класс matrix.
Пример 11.29. Применение шаблона matrix
#include "matrix.hpp"
#include <iostream>
using namespace std;
int main() {
matrix<int> 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;
}
Программа примера 11.29 выдает следующий результат.
(2,0)
(0,2)
ОбсуждениеПроект шаблона матрицы, представленный в примере 11.28, в значительной степени инспирирован шаблоном матрицы Бьерна Страуструпа (Bjarne Stroustrup) из его книги «The C++ Programming Language», 3-е издание (издательство «Addison Wesley»). Реализация Страуструпа отличается тем, что его итератор использует класс slice и указатель на valarray для индексации. Реализованная в примере 11.27 матрица использует вместо них итератор с шагом из рецепта 11.12, что делает итераторы более компактными и при некоторых реализациях более эффективными.
Шаблонный класс matrix позволяет индексировать элемент i-й строки и j-го столбца, используя операцию двойной индексации. Например:
matrix<int> m(100,100);
cout << "the element at row 24 and column 42 is " << m[24][42] << endl;
Шаблонный класс matrix также имеет функции-члены begin и end, т.е. его легко можно использовать в различных алгоритмах STL.
Пример 11.28 содержит строку, которая, возможно, вызывает у вас некоторое удивление. Имеется в виду следующее объявление.
mutable valarray<Value_T> m;
Объявление поля-члена m со спецификатором mutable вынужденно. В противном случае я не мог бы обеспечить итераторы со спецификатором const, потому что нельзя создать итератор для const valarray.
Смотри такжеРецепты 11.15 и 11.16.
11.15. Реализация статической матрицы
Требуется эффективно реализовать матрицу, когда ее размерность (т.е. количество строк и столбцов) постоянна и известна на этапе компиляции.
РешениеКогда размерность матрицы известна на этапе компиляции, компилятор может легко оптимизировать реализацию, в которой количество строк и столбцов задается в виде параметров шаблона, как показано в примере 11.30.
Пример 11.30. kmatrix.hpp
#ifndef KMATRIX_HPP
#define KMATRIX_HPP
#include "kvector.hpp"
#include "kstride_iter.hpp"
template<class Value_T, int Rows_N, int Cols_N>
class kmatrix {
public:
// открытые имена, вводимые typedef
typedef Value_T value_type;
typedef kmatrix self;
typedef Value_T* iterator;
typedef const Value_T* const_iterator;
typedef kstride_iter<Value_T*, 1> row_type;
typedef kstride_iter<Value_T*, Cols_N> col_type;
typedef kstride_iter<const Value_T*, 1> const_row_type;
typedef kstride_iter<const Value T*, Cols_N> const_col_type;
// открытые константы
static const int nRows = Rows_N;
static const int nCols = Cols_N;
// конструкторы
kmatrix() { m = Value_T(); }
kmatrix(const self& x) { m = x.m; }
explicit kmatrix(Value_T& x) { m = x.m; }
// открытые функции
static int rows() { return Rows_N; }
static int cols() { return Cols_N; }
row_type row(int n) { return row_type(begin() * (n * Cols_N)); }
col_type col(int n) { return col_type(begin() + n); }
const_row_type row(int n) const {
return const_row_type(begin() + (n * Cols_N));
}
const_col_type col(int n) const {
return const_col_type(begin() + n);
}
iterator begin() { return m.begin(); }
iterator end() { return m.begin() + size(); }
const_iterator begin() const { return m; }
const_iterator end() const { return m + size(); }
static int size() { return Rows_N * Cols_N; }
// операторы
row_type operator[](int n) { return row(n); }
const_row_type operator[](int n) const { return row(n); }
// операции присваивания
self& operator=(const self& x) { m = x.m; return *this; }
self& operator=(value_type x) { m = x; return *this; }
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-() { return self(-m); }
// друзья
friend self operator+(self x, const self& у) { return x += y; }
friend self operator-(self x, const self& y) { return x -= y; }
friend self operator+(self x, value_type y) { return x += y; }
friend self operator-(self x, value type y) { return x -= y; }
friend self operator*(self x, value_type y) { return x *= y; }
friend self operator/(self x, value_type y) { return x /= y; }
friend bool operator==(const self& x, const self& y) { return x == y; }
friend bool operator!=(const self& x, const self& y) { return x.m != y.m; }
private:
kvector<Value_T, (Rows_N + 1) * Cols_N> m;
};
#endif
В примере 11.31 приведена программа, демонстрирующая применение шаблонного класса kmatrix.
Пример 11.31. Применение kmatrix
#include "kmatrix.hpp"
#include <iostream>
using namespace std;
template<class Iter_T>
void outputRowOrColumn(Iter_T iter, int n) {
for (int i=0; i < n; ++i) {
cout << iter[i] << " ";