11.15. Реализация статической матрицы

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] << " ";

 }

 cout << endl;

}

template<class Matrix_T>

void initializeMatrix(Matrix_T& m) {

 int k = 0;

 for (int i=0; i < m.rows(); ++i) {

  for (int j=0; j < m.cols(); ++j) {

   m[i][j] = k++;

  }

 }

}

template<class Matrix_T>

void outputMatrix(Matrix_T& m) {

 for (int i=0; i < m.rows(); ++i) {

  cout << "Row " << i << " = ";

   outputRowOrColumn(m.row(i), m.cols());

 }

 for (int i=0; i < m.cols(); ++i) {

  cout << "Column " << i << " = ";

   outputRowOrColumn(m.col(i), m.rows());

 }

}

int main() {

 kmatrix<int, 2, 4> m;

 initializeMatrix(m); m *= 2;

 outputMatrix(m);

}

Программа примера 11.31 выдает следующий результат.

Row 0 = 0 2 4 6

Row 1 = 8 10 12 14

Column 0 = 0 8

Column 1 = 2 10

Column 2 = 4 12

Column 3 = 6 14

Обсуждение

Представленные в примерах 11.30 и 11.31 определение шаблона класса kmatrix и пример его использования очень напоминают шаблон класса matrix из рецепта 11.14. Единственным существенным отличием является то, что при объявлении экземпляра kmatrix приходится передавать размерности матрицы через параметры шаблона, например;

kmatrix<int 5, 6> m; // объявляет матрицу с пятью строками и шестью

                     // столбцами

В приложениях многих типов часто требуется, чтобы матрицы имели размерности, известные на этапе компиляции. Передача размера строк и столбцов через параметры шаблона позволяет компилятору легче применять такую оптимизацию, как развертка цикла, встраивание функций и ускорение индексации.

Как и рассмотренный ранее шаблон статического вектора (kvector), шаблон kmatrix особенно эффективен при небольших размерах матрицы.

Смотри также

Рецепты 11.14 и 11.16.