Реализация с использованием библиотеки шаблонов
Использование библиотеки шаблонов С++ – ещё один вариант построения надёжного кода, позволяющего минимизировать затраты на разработку и избежать ошибок, связанных с реализацией системных функций управления памятью. В качестве недостатка такого подхода можно отметить несколько худшее быстродействие, так как код библиотеки шаблонов делается универсальным и, как правило, изобилует различными проверками и вызовами вспомогательных функций. Среди широко применяемых библиотек шаблонов можно отметить 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 – метод получения количества строк матрицы. Премущества данного подхода к построению модуля библиотеки процедур векторной, матричной и кватернионной алгебр обусловлены высокой надёжностью и компактностью кода, отсутствием трудозатрат на разработку и отладку служебных функций. К уже упоминаемым недостаткам можно отнести меньшее быстродействие по сравнению с самостоятельной реализацией методов управления памятью. Литература 1. Амелькин Н.И. Кинематика и динамика твердого тела (кватернионное изложение). – М.: МФТИ (ГУ), 2000. – 64 с.
2. Бесекерский В. А., Попов Е.П. Теория Систем автоматического управления. – Изд. 4-е, перераб. и доп. – Спб.: Профессия, 2003. – 752 с. 3. Бобронников В. Т., Красильщиков М. Н., Козорез Д. А. и др. Статистическая динамика и оптимизация управления летательных аппаратов: учебное пособие. / Под общ. ред. М. Н. Красильщикова, В. В. Малышева. – Изд. 2-е, перераб. и доп. – М.: Альянс, 2013. – 468 с. 4. Бранец В. Н., Шмыглевский И. П. Применение кватернионов в задачах ориентации твердого тела. – М.: Наука, 1973. – 320 с. 5. Вержбицкий В.М. Основы численных методов: Учебник для вузов. – М.: Высшая школа, 2002. – 840 с. 6. Желтов С.Ю., Веремеенко К.К., Ким Н.В. и др. Современные информационные технологии в задачах навигации и наведения беспилотных маневренных летательных аппаратов. / Под ред. М.Н. Красильщикова, Г.Г. Себрякова. – М.: ФИЗМАТЛИТ, 2009. – 556 с. 7. Осипов Д.Л. – Delphi. Программирование для Windows, OS X, iOS и Android. – Спб.: БХВ-Петербург, 2014. – 464 с. 8. Рашевский П. К. Риманова геометрия и тензорный анализ. – М.: Наука, 1967. – 664 с. 9. Страуструп Б. Программирование: принципы и практика с использованием С++. – Второе издание. – М.: Вильямс, 2016. – 1328 с. 10. Умнов А. Е., Аналитическая геометрия и линейная алгебра: учебное пособие. – 3-е изд., испр. и доп. – М.: МФТИ, 2011. – 554 с. 11. Элджер Дж. С++: Библиотека программиста. – Спб.: Питер, 1999. – 320 с.
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|