C ++ Matrix Class - Предложения - PullRequest
2 голосов
/ 12 апреля 2011

Я пытаюсь создать шаблонный класс Matrix в C ++.Ниже приведена его реализация.Я реализовал два оператора +, + = на данный момент, просто чтобы дать представление о том, как это будет выглядеть, и я подумал, что было бы лучше запросить обратную связь, прежде чем я продолжу.

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

Было бы очень полезно, если бы кто-то мог прокомментировать это и мог бы предложитьнесколько улучшений или предложений.

Спасибо.

template<class T>
class Matrix
{
    public:
    int r,c;
    vector< vector< T > >mat;

    Matrix() {}

    // Constructor to set the size of the matrix
    Matrix(int _r,int _c)
    {
        r=_r;c=_c;
        mat.resize(r);
        for(int i=0;i<r;i++)
            mat[i].resize(c);
    }
    // Constructor to build a matrix from a C 2d array
    // Pointer to the first element is passed (&arr[0][0])
    Matrix(T *arr,int _r,int _c)
    {
        r=_r;c=_c;
        mat.resize(r);
        for(int i=0;i<r;i++)
            for(int j=0;j<c;j++)
                mat[i].push_back(arr[i*c+j]);
    }
    template<typename U>
    Matrix<T>& operator +=(const Matrix<U>&M)
    {
        for(int i=0;i<r;i++)
            for(int j=0;j<c;j++)
                mat[i][j]+=static_cast<T>(M.mat[i][j]);
        return *this;
    }
    template<typename U>
    Matrix<T> operator +(const Matrix<U>&M)
    {
        Matrix<T>tmp=*this;
        return tmp+=M;
    }
};

template<typename T>
istream& operator >>(istream &in,Matrix<T>&M)
{
    in>>M.r>>M.c;
    Matrix<T>tmp(M.r,M.c);
    for(int i=0;i<M.r;i++)
        for(int j=0;j<M.c;j++)
            in>>tmp.mat[i][j];
    M=tmp;
    return in;
}
template<typename T>
ostream& operator <<(ostream &out,Matrix<T>M)
{
    for(int i=0;i<M.r;i++)
    {
        for(int j=0;j<M.c;j++)
            cout<<M.mat[i][j]<<" ";
        cout<<endl;
    }
    return out;
}

РЕДАКТИРОВАТЬ: Спасибо всем за предложения.

У меня только один маленький вопрос, скажем, я хочу реализовать проверку ошибок (например: проверка границ, допустимых аргументов и т. Д.), Однако я хочу, чтобы пользователь предоставил возможность полностью отключить проверку ошибок,Есть ли хороший способ реализовать это?Что мне нужно, это что-то вроде примера: `ios_base :: sync_with_stdio (0) ;.Еще раз спасибо.

Ответы [ 9 ]

2 голосов
/ 12 апреля 2011

1. Использовать списки инициализаторов для конструктора, как, Matrix (...) : r(_r), c(_c)

2. operator + может быть просто { return this->Add(M) } (при условии, что имена типов T и U совместимы, и вы реализовали некоторый метод Add ()). 3. Если вы строите что-то из 2D-массива, используйте метод ниже:

template<typename T>
class Matrix {
public: // ...
  void Construct (T *arr)
  {
    mat.resize(r);
    for(int i=0;i<r;i++)
      for(int j=0;j<c;j++)
        mat[i].push_back(arr[i*c+j]);
  }
  template<size_t ROW, size_t COL>
  Matrix(T (&arr)[ROW][COL]) : r(ROW), c(COL)
  {
    Construct(arr);
  }
//...
};

Таким образом, вам не нужно передавать размер двумерного массива при вызове, и это уменьшит вероятность ошибок.

  1. Для лучшей читабельности вы можете использовать лучшие имена для учеников: либо this->r, либо t_r.
2 голосов
/ 12 апреля 2011

Пара баллов:

  • Используйте один std::vector<T> вместо std::vector<std::vector<T>>.Индексируйте его с помощью y * r + x - используйте и перегружайте оператор, чтобы сделать это проще (см. Следующий пункт).Это будет более эффективно использовать память и немного быстрее (и ваша инициализация будет намного проще: resize(r*c)).
  • Сделайте ваши элементы данных частными, чтобы защитить вашу матрицу от непреднамеренных изменений размера.Например, код пользователя может в настоящее время изменять размеры векторов, но оставить старые r и c.
  • Overload operator() для доступа к матрице (как const, так и non-const).Если вы действительно должны использовать синтаксис matrix[r][c] вместо matrix(r,c), рассмотрите возможность перегрузки operator[] и возврата итератора в правильную строку (векторные итераторы имеют произвольный доступ, поэтому они будут предоставлять operator[]).
  • Реализуйте operator+ как функцию, не являющуюся другом, не являющуюся членом. - Улучшает инкапсуляцию!
  • Используйте списки инициализации, как предлагали другие.
  • Позвольте конструктору, который в данный момент принимает T*, взятьитератор вместо.Таким образом, вы автоматически получаете поддержку указателей вместе со множеством других интересных вещей, таких как проверка диапазона для отладочных итераторов, автоматическое преобразование типов для совместимых типов значений и поддержка всех других итераторов произвольного доступа.Также рассмотрите возможность заполнения вашей матрицы построчно, чтобы вы могли использовать итераторы вперед.
2 голосов
/ 12 апреля 2011

Если вы хотите знать, как это делается правильно, посмотрите реализацию проекта Eigen:

https://bitbucket.org/eigen/eigen/src/49e00a2e570c/Eigen/src/Core/Matrix.h

https://bitbucket.org/eigen/eigen/src/49e00a2e570c/Eigen/src/Core/MatrixBase.h

Eigen - быстрый шаблонматричная библиотека для C ++.Я уверен, что там много полезного.

1 голос
/ 14 апреля 2015

Интересно, почему вы использовали очень дорогую функцию push_back в этой инициализации класса:

// Pointer to the first element is passed (&arr[0][0])
Matrix(T *arr,int _r,int _c)
{
    r=_r;c=_c;
    mat.resize(r);
    for(int i=0;i<r;i++)
        for(int j=0;j<c;j++)
            mat[i].push_back(arr[i*c+j]);
}

Поскольку у вас уже есть количество строк и столбцов, рассмотрите это:

// Pointer to the first element is passed (&arr[0][0])
Matrix(T *arr,int _r,int _c)
{
    r=_r;c=_c;
    mat.resize(r);
    for(int i=0;i<r;i++)
        mat[i].resize(c);
        for(int j=0;j<c;j++)
            mat[i][j]= arr[i*c+j];
}
1 голос
/ 12 апреля 2011

Вы тратите впустую время, сохраняя _r и _c, потому что векторы сохраняют свои размеры.

1 голос
/ 12 апреля 2011

Я бы не использовал вектор векторов для достижения этой цели. Таким образом, вы добавляете довольно много накладных расходов ко всем поискам без какой-либо реальной необходимости. Просто выделите одномерный или двумерный массив T и используйте его в своем классе. Это также позволило бы быстро копировать в другие экземпляры объекта без необходимости перебирать все поля, а также сбрасывать всю матрицу.

Также вы должны сделать своих членов постоянными, когда они не должны быть изменены позже (в вашем примере r и c). Для их инициализации просто используйте список конструкторов в посте Тони.

1 голос
/ 12 апреля 2011
  1. Возможно, вы захотите сделать члены r, c и mat приватными.
  2. Ваши операторы потоковой передачи не симметричны (operator<< не записывает r иc).
1 голос
/ 12 апреля 2011

Использовать списки инициализации при инициализации аргументов конструктора:

 Matrix(int _r,int _c)
    {
        r=_r;c=_c;

..

должно быть:

 Matrix(int _r,int _c) : r(_r), c(_c)
    {
        ....
0 голосов
/ 12 апреля 2011

Если скорость - это то, что вы имеете в виду, не реализовывайте матричную библиотеку самостоятельно, а используйте библиотеки Boost, я использовал их в некоторых проектах и ​​неплохо справляюсь как с 32-, так и с 64-битными архитектурами

...