Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программная реализация библиотеки векторной, матричной и кватернионной алгебр.docx
Скачиваний:
2
Добавлен:
01.07.2025
Размер:
190 Кб
Скачать
☆
      1. Реализация с использованием библиотеки шаблонов

Использование библиотеки шаблонов С++ – ещё один вариант построения надёжного кода, позволяющего минимизировать затраты на разработку и избежать ошибок, связанных с реализацией системных функций управления памятью. В качестве недостатка такого подхода можно отметить несколько худшее быстродействие, так как код библиотеки шаблонов делается универсальным и, как правило, изобилует различными проверками и вызовами вспомогательных функций. Среди широко применяемых библиотек шаблонов можно отметить 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 – метод получения количества строк матрицы.

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