- •Программная реализация формальных средств анализа и моделирования интегрированных систем ла
- •Примеры реализации пмо на Delphi
- •Реализация на основе записей (record)
- •Использование классов и объектных интерфейсов
- •Реализация с самостоятельным управлениям памятью
- •Реализация с использованием библиотеки шаблонов
- •Литература
Реализация с использованием библиотеки шаблонов
Использование библиотеки шаблонов С++ – ещё один вариант построения надёжного кода, позволяющего минимизировать затраты на разработку и избежать ошибок, связанных с реализацией системных функций управления памятью. В качестве недостатка такого подхода можно отметить несколько худшее быстродействие, так как код библиотеки шаблонов делается универсальным и, как правило, изобилует различными проверками и вызовами вспомогательных функций. Среди широко применяемых библиотек шаблонов можно отметить STL, Boost, Qt. Первая из них входит в стандарт С++, являясь частью языка, поэтому пример реализации приводится именно на её основе.
Все библиотеки шаблонов предоставляют готовые реализации программных контейнеров с различной организацией хранения данных в памяти и доступа к ним – вектор, список, очередь, ассоциативный массив и т.д., причём тип данных может быть любым (за счёт представления таких контейнеров в виде типизированных шаблонов классов).
Классы TVector и TMatrix модуля библиотеки векторной, матричной и кватернионной алгебр целесообразно построить на основе шаблона std::vector, объявленного в стандартном заголовочном файле vector. Объявление и реализация класса TQuaternion будут полностью аналогичны описанным в разделе 3.2.1 и здесь не рассматриваются.
Для расширения функциональности класса std::vector алгебраическими методами можно применить один из двух классических подходов. Первый из них – наследование, он предполагает объявление пользовательских классов как наследников от класса-шаблона с указанным типом параметра. Для вектора типом параметра может быть double или long double (в зависимости от платформы и необходимой точности вычислений), а для матрицы можно использовать вложенную параметризацию шаблона – в качестве типа использовать ранее объявленный параметризованный шаблон вектора (см. листинг 11, типы BaseVector и BaseMatrix соответственно). Это обеспечит «двумерность» массива для хранения содержимого матрицы.
Второй подход основан на агрегировании самостоятельными пользовательскими классами вектора и матрицы поля типа std::vector. Данный подход требует более обширного применения методов-декораторов (транслирующих вызовы методам агрегированного объекта и возвращающих назад результаты их работы) в пользовательских классах, при этом не давая никаких преимуществ. Подход же, связанный с наследованием, позволяет минимизировать декорирование, по существу обеспечивая лишь расширение уже имеющегося функционала класса std::vector алгебраическими операциями. В случае с матрицей преимущество не так очевидно, так как для защиты объектов матриц от некорректного использования (например, изменение длин отдельных строк) приходится использовать защищённое наследование, скрывающее все публичные методы класса std::vector. Это требует применения декорирования, аналогичного схеме с агрегированием.
Учитывая приведенные соображения, рассмотрим пример объявления классов TVector и TMatrix с их наследованием от класса-шаблона std::vector.
Листинг 11
#include <vector>
namespace StdLinearAlgebra {
// Базовые типы-шаблоны
typedef std::vector<double> BaseVector;
typedef std::vector< BaseVector > BaseMatrix;
// Опережающая декларация
class TMatrix;
// Класс векторов
class TVector : public BaseVector {
public:
// Конструктор по умолчанию
TVector() : BaseVector() {}
// Конструктор с заданным кол-вом элементов
TVector(int n) : BaseVector(n) {}
// Конструктор копий
TVector(const TVector& rvalue) : BaseVector(rvalue) {}
// Функция получения индекса последнего элемента
inline int high() const { return size() - 1; }
// Оператор - унарный минус
TVector operator - () const;
// Оператор вычитания векторов
TVector operator - (const TVector& arg) const;
// Оператор сложения векторов
TVector operator + (const TVector& arg) const;
// Оператор умножения вектора на число
TVector operator * (double arg) const;
// Оператор скалярного умножения векторов
double operator * (const TVector& arg) const;
// Оператор умножения вектора на матрицу
TVector operator * (const TMatrix& arg) const;
// Оператор умножения вектора на кватернион
//TQuaternion operator * (const TQuaternion& arg) const;
// Оператор векторного умножения векторов
TVector operator ^ (const TVector& arg) const;
// Дружественная функция - оператор умножения числа на вектор
friend TVector operator * (double lvalue, const TVector& rvalue);
// Функция получения модуля вектора
double length() const;
// Функция нормирования вектора
TVector& norm();
// Поворот вектора вокруг заданной оси на заданный угол при помощи формулы Родрига
//TVector rotateByRodrigFormula(double phi, const TVector& axis) const;
// Поворот вектора вокруг заданной оси на заданный угол при помощи кватерниона
//TVector rotate(double phi, const TVector& axis) const;
// Поворот вектора при помощи заданного кватерниона
//TVector rotateByQuaternion(const TQuaternion& L) const;
};
// Класс матриц
class TMatrix : protected BaseMatrix {
public:
// Конструктор по умолчанию
TMatrix() : BaseMatrix() {}
// Конструктор с заданным кол-вом элементов
TMatrix(int n, int m) : BaseMatrix() { this->resize(n, m); }
// Конструктор копий
TMatrix(const TMatrix& arg) : BaseMatrix(arg) {}
// Функция получения количества строк
inline int rowCount() const { return this->size(); }
// Функция получения кол-ва столбцов
inline int colCount() const {return (this->size() > 0) ? (*this)[0].size() : 0;}
// Функция получения индекса последней строки
inline int rowHigh() const { return rowCount() - 1; }
// Функция получения индекса последнего столбца
inline int colHigh() const { return colCount() - 1; }
// Оператор доступа к элементам матрицы
inline double& operator()(int i, int j) { return (*this)[i][j]; }
// Оператор константного доступа к элементам матрицы
inline const double& operator()(int i, int j) const { return (*this)[i][j]; }
// Константный оператор доступа к строке матрицы как к вектору
inline const TVector& operator() (int i) const { return (TVector&)(*this)[i]; }
// Изменение размера
void resize(int n, int m);
// Оператор - унарный минус
TMatrix operator - () const;
// Оператор вычитания матриц
TMatrix operator - (const TMatrix& arg) const;
// Оператор сложения матриц
TMatrix operator + (const TMatrix& arg) const;
// Оператор умножения матрицы на число
TMatrix operator * (double arg) const;
// Оператор умножения матриц
TMatrix operator * (const TMatrix& arg) const;
// Оператор умножения матрицы на вектор
TVector operator * (const TVector& arg) const;
// Дружественная функция - оператор умножения числа на матрицу
friend TMatrix operator * (double lvalue, const TMatrix& rvalue);
// Оператор обращения матриц (метод Гаусса)
TMatrix operator ! () const throw(int);
// Функция вычисления детерминанта
double det() const;
// Функция транспонирования
TMatrix t() const;
// Функция формирования единичной матрицы
static TMatrix E(int n);
// Функция перестановки строк
TMatrix& swapRows(int i, int j);
}; }
Чтобы избежать совпадения наименований, объявление вспомогательных типов BaseVector, BaseMatrix и классов TVector, TMatrix делается в пространстве имён StdLinearAlgebra.
Следует также обратить внимание на уже упомянутый приём – защищённое наследование класса TMatrix от BaseMatrix, предотвращающее прямой доступ извне к публичным методам и свойствам класса std::vector как предка TMatrix. Главным образом этот приём нацелен на оператор BaseMatrix::operator [] (int), который в противном случае позволял бы получить безконтрольный доступ к строкам матрицы как к отдельным объектам, менять размер и даже удалять их. Нужно отметить, что допустимо отдельно осуществить перегрузку этого оператора в защищённой или закрытой секции, чтобы запретить доступ к нему.
Реализация алгебраических методов для классов векторов, матриц и кватернионов полностью аналогична реализации, описанной в предыдущем разделе. Все служебные функции, предназначенные для работы с памятью, реализованы в классе std::vector, необходимая декорация этих методов и операторов выполняется непосредственно в заголовочном файле в виде встроенных (inline) методов, как например inline int TMatrix::rowCount() const – метод получения количества строк матрицы.
Премущества данного подхода к построению модуля библиотеки процедур векторной, матричной и кватернионной алгебр обусловлены высокой надёжностью и компактностью кода, отсутствием трудозатрат на разработку и отладку служебных функций. К уже упоминаемым недостаткам можно отнести меньшее быстродействие по сравнению с самостоятельной реализацией методов управления памятью.
