Передача значений, исключения / утверждения и дизайн класса.Критика / Вопросы - PullRequest
0 голосов
/ 04 января 2019

Я строю матричный класс шаблона для использования в моем будущем коде c ++.У меня есть несколько вопросов, касающихся передачи значений для перегруженных операторов, исключений и утверждений и общего дизайна класса.

  1. Правильно ли я передаю значения?Это эффективно?Что я могу сделать иначе, чтобы сделать это лучше?

  2. Эта библиотека построена с учетом будущего дизайна приложения (терминал или графический интерфейс), где пользователь может определять свои собственные матрицы и выполнять вычисления.Было бы лучше использовать исключения вместо утверждений в этом случае?

  3. Я искал правило 5 для c ++, где говорится, что:

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

    Могу ли я обойтись без реализации этого правила, просто не имея ни одного из этих трех? ** Что было быСтандартный способ сделать этот класс более функциональным?

В моей программе определены вычитание, умножение и деление (скалярные) с такой же / сходной структурой, как и у предоставленных определений операторов сложения, поэтомуздесь не весь этот код необходим.

Принимается любой жесткий совет или критика в отношении общего дизайна!

#ifndef MACMATRICES_H
#define MACMATRICES_H

#include <vector>
#include <iomanip>
#include <iostream>
#include <exception>
#include "../../DMF-Terminal.h"


namespace DMF
{
    template <typename T>
    class matrix
    {
    public:

    // Constructors 
    matrix();
    matrix(int p_rows, int p_columns);

    // Operators
    std::vector<T>& operator[] (size_t i) { return m[i]; }
    matrix<T> operator+(const matrix<T>& rhs);
    matrix<T> operator+(const T& rhs);
    matrix<T>& operator+=(const matrix<T>& rhs);
    matrix<T>& operator+=(const T& rhs);


    // Class Methods 
    void print() const;
    matrix<T> inverse();
    T determinant();

    // Observers 
    bool isSquare() const;
    int rowSize() const { return m_rows; } 
    int colSize() const { return m_cols; } 

private:

    int m_rows, m_cols;
    std::vector< std::vector<T> > m;
};

/ * Конструкторs ----------------------------------------------------------------------------------- * /

template <typename T>
matrix<T>::matrix(){}

template <typename T>
matrix<T>::matrix(int p_rows, int p_cols) :
    m(p_rows, std::vector<T>(p_cols)), m_rows(p_rows), m_cols(p_cols) {}

/ * Дополнение -------------------------------------------------------------------------------------- * /

template <typename T>
matrix<T> matrix<T>::operator+(const matrix<T>& rhs)
{
    try
    {
        if((this->rowSize() == rhs.rowSize()) && (this->colSize() == rhs.colSize()))
        {
            matrix<T> sum (this->rowSize(), this->colSize()); 
            for(int i = 0; i < this->rowSize() ; ++i)
            {
                for(int j = 0; j < this->colSize(); ++j)
                    sum.m[i][j] = this->m[i][j] + rhs.m[i][j];
            }
            return sum; 
        }
        else throw std::runtime_error("Cannot add matrices, invalid row/column sizes."); 
    }
    catch (std::exception &e)
    {
        std::cout << "Error: " << e.what(); DMF::wait();
    }
}

template <typename T>
matrix<T> matrix<T>::operator+(const T& rhs)
{
    matrix<T> sum (this->rowSize(), this->colSize()); 
    for(int i = 0; i < this->rowSize() ; ++i)
    {
        for(int j = 0; j < this->colSize(); ++j)
            sum.m[i][j] = this->m[i][j] + rhs;
    }
    return sum; 
}

template <typename T>
matrix<T>& matrix<T>::operator+=(const matrix<T>& rhs)
{
    try
    {
        if((this->rowSize() == rhs.rowSize()) && (this->colSize() == rhs.colSize()))
        {
            for(int i = 0; i < this->rowSize() ; ++i)
            {
                for(int j = 0; j < this->colSize(); ++j)
                    this->m[i][j] += rhs.m[i][j];
            }
            return *this; 
        }
        else throw std::runtime_error("Cannot add matrices, invalid row/column sizes."); 
    }
    catch (std::exception &e)
    {
        std::cout << "Error: " << e.what(); DMF::wait();
    }
}

template <typename T>
matrix<T>& matrix<T>::operator+=(const T& rhs)
{
    matrix<T> sum (this->rowSize(), this->colSize()); 
    for(int i = 0; i < this->rowSize() ; ++i)
    {
        for(int j = 0; j < this->colSize(); ++j)
            this->m[i][j] += rhs;
    }
    return *this; 
}
}
#endif /* MACMATRICES_H */

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

1 Ответ

0 голосов
/ 04 января 2019

Правильно ли я передаю значения? Это эффективно? Что я могу сделать иначе, чтобы сделать его лучше?

Вы передаете свою матрицу по (const) ссылке, поэтому вы избегаете копирования, так что все в порядке.

У вас есть некоторая "опечатка" в качестве переменной, используемой как matrix<T> sum в operator+=.

Вы дублируете некоторую информацию, std::vector знает ее размер.

линеаризация std::vector<std::vector<T>> в std::vector<T>> была бы более дружественной к кешу, но для нее требовался бы прокси-класс (с другим operator[]) для обработки operator[], или вы могли бы вместо этого использовать аксессор как operator()(int, int) или operator[](std::pair<int, int>).

Эта библиотека построена с учетом будущего дизайна приложения (терминал или графический интерфейс), где пользователь может определять свои собственные матрицы и выполнять вычисления. Будет ли лучше использовать исключения вместо утверждений в этом случае?

Исключением является сообщение об ошибке пользователю, но в настоящее время вы перехватываете ее напрямую, чтобы игнорировать ошибку в каком-либо журнале ... Таким образом, вместо выдачи вы можете напрямую зарегистрировать ошибку в данный момент.

Существует несколько способов решения этой проблемы:

  • Иметь размер матрицы в аргументе шаблона и использовать систему типов для проверки этих ошибок при компиляции. требуется знать размер при компиляции.

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

  • Если вы считаете, что пользователь не сможет игнорировать / восстановить после ошибки, тогда возможен assert / terminate / UB.

Я посмотрел правило 5 для c ++, где говорится, что: «Поскольку наличие определяемого пользователем деструктора, оператора копирования или копирования-присваивания предотвращает неявное определение конструктора перемещения и назначения перемещения оператор, любой класс, для которого семантика перемещения желательна, должен объявить все пять специальных функций-членов. " Могу ли я обойтись без применения этого правила, просто не имея ни одного из этих трех? Каков был бы стандартный способ сделать этот класс более функциональным?

Правило 3 имеет варианты:

  • Правило 5 также включает конструктор перемещения и назначение перемещения.
  • Правило 0, где все члены уже дружественны к RAII, поэтому реализация по умолчанию в порядке.

использование std::vector позволяет использовать правило 0 :-) Так что ты в порядке.

...