Implementación básica de matriz entera

Aug 27 2020

Necesito escribir una CustomBasicMatriximplementación de clase para la universidad. Esto incluye operaciones básicas como suma inty CustomBasicMatrixresta, transposición e iteraciones/const iteraciones .

Me gustaría saber si existen convenciones de codificación o, lo que es más importante, posibilidades de fallas de fuga de memoria/segmentación .

Contexto:

  • La implementación debe incluirse en un espacio de nombres "sys".
  • El único archivo externo en uso es "Auxiliaries.h", que contiene: estructura "Dimensión", solo contiene 'ancho' y 'alto', con funciones auxiliares adicionales para imprimir una matriz.
  • En lugar de transponer una función cambiando las dimensiones, guardé un valor booleano que indica si la matriz está transpuesta, por lo tanto, cuando obtenemos el valor, usamos dicho valor booleano para determinar si obtenemos (i,j) o (j,i). Esto es para ahorrar memoria y sobrecarga.

¡Gracias por adelantado!

Matriz básica personalizada .h:


#include <ostream>
#include "Auxiliaries.h"

namespace sys{
    class CustomBasicMatrix{
    private:
        int rows;
        int cols;
        int** data;
        bool trans = false;
    public:
        explicit CustomBasicMatrix(Dimensions dim, int initValue = 0);
        CustomBasicMatrix(const CustomBasicMatrix& other);
        virtual ~CustomBasicMatrix();
        CustomBasicMatrix& operator=(const CustomBasicMatrix& other);


        static CustomBasicMatrix Identity(int dims);

        int height() const;
        int width() const;
        int size() const;

        CustomBasicMatrix transpose() const;



        CustomBasicMatrix operator-() const;
        CustomBasicMatrix& operator+=(int scalar);
        CustomBasicMatrix& operator+=(const CustomBasicMatrix& rhs);

        int &operator()(int row, int col);
        int &operator()(int row, int col) const;

        class iterator{
        private:
            CustomBasicMatrix* matrix;
            int row;
            int col;
        public:
            iterator(CustomBasicMatrix* matrix, int row=0, int col=0);
            virtual ~iterator() = default;
            iterator& operator=(const iterator& other);

            int& operator*();
            iterator& operator++();
            const iterator operator++(int);

            bool operator==(const iterator& other) const;
            bool operator!=(const iterator& other) const;
        };

        iterator begin();
        iterator end();


        class const_iterator{
        private:
            const CustomBasicMatrix* matrix;
            int row;
            int col;
        public:
            const_iterator(const CustomBasicMatrix *matrix, int row = 0, int col = 0);
            virtual ~const_iterator() = default;
            const_iterator& operator=(const const_iterator& other);

            const int& operator*() const;
            const_iterator operator++();
            const const_iterator operator++(int);

            bool operator==(const const_iterator& other) const;
            bool operator!=(const const_iterator& other) const;

        };

        const_iterator begin() const;
        const_iterator end() const;
    };

    bool any(const CustomBasicMatrix& mat);
    bool all(const CustomBasicMatrix& mat);

    CustomBasicMatrix operator+(const CustomBasicMatrix& lhs, const CustomBasicMatrix& rhs);
    CustomBasicMatrix operator+(const CustomBasicMatrix& lhs, int scalar);
    CustomBasicMatrix operator+(int scalar, const CustomBasicMatrix& matrix);
    CustomBasicMatrix operator-(const CustomBasicMatrix &lhs, const CustomBasicMatrix &rhs);

    CustomBasicMatrix operator<(const CustomBasicMatrix& lhs, int scalar);
    CustomBasicMatrix operator<=(const CustomBasicMatrix& lhs, int scalar);
    CustomBasicMatrix operator>(const CustomBasicMatrix& lhs, int scalar);
    CustomBasicMatrix operator>=(const CustomBasicMatrix& lhs, int scalar);
    CustomBasicMatrix operator!=(const CustomBasicMatrix& lhs, int scalar);
    CustomBasicMatrix operator==(const CustomBasicMatrix& lhs, int scalar);
    std::ostream &operator<<(std::ostream &os, const CustomBasicMatrix &matrix);
}

Matriz básica personalizada .cpp:


#include "CustomBasicMatrix.h"
#include "Auxiliaries.h"

#define CBM sys::CustomBasicMatrix

CBM::CustomBasicMatrix(sys::Dimensions dim, int initValue)
{
    this->rows = dim.getRow();
    this->cols = dim.getCol();
    this->data = new int*[this->rows];
    int i;
    try{
        for (i = 0; i < this->rows; ++i){
            this->data[i] = new int[this->cols];
        }
    } catch(const std::exception& e){
        for (int j = 0; j < i; ++j){
            delete[] this->data[j];
        }
        delete[] this->data;
        throw e;
    }

    for(int i=0; i< this->rows ; i++){
        for (int j = 0; j < this->cols; ++j){
            this->data[i][j] = initValue;
        }

    }
}

CBM::CustomBasicMatrix(const CBM &other)
{
    this->rows = other.rows;
    this->cols = other.cols;
    this->trans = other.trans;

    this->data = new int*[this->rows];
    int i;
    try{
        for (i = 0; i < this->rows; ++i){
            this->data[i] = new int[this->cols];
        }
    } catch(const std::exception& e){
        for (int j = 0; j < i; ++j){
            delete[] this->data[j];
        }
        delete[] this->data;
        throw e;
    }

    for(int i=0; i< this->rows ; i++){
        for (int j = 0; j < this->cols; ++j){
            this->data[i][j] = other.data[i][j];
        }
    }
}

CBM::~CustomBasicMatrix()
{
    for (int i = 0; i < this->rows; ++i){
        delete[] this->data[i];
    }
    delete[] this->data;
}

CBM &CBM::operator=(const CBM &other)
{
    if(this == &other) return *this;

    for (int i = 0; i < this->rows; ++i){
        delete[] this->data[i];
    }
    delete[] this->data;

    this->rows = other.rows;
    this->cols = other.cols;
    this->trans = other.trans;

    this->data = new int*[this->rows];
    int i;
    try{
        for (i = 0; i < this->rows; ++i){
            this->data[i] = new int[this->cols];
        }
    } catch(const std::exception& e){
        for (int j = 0; j < i; ++j){
            delete[] this->data[j];
        }
        delete[] this->data;
        throw e;
    }

    for(int i=0; i< this->rows ; i++){
        for (int j = 0; j < this->cols; ++j){
            this->data[i][j] = other.data[i][j];
        }
    }

    return *this;
}

CBM CBM::Identity(int dims)
{
    Dimensions dim = Dimensions(dims, dims);
    CustomBasicMatrix ret(dim, 0);

    for (int i = 0; i < dims; ++i){
        ret.data[i][i] = 1;
    }

    return ret;
}

int CBM::height() const
{
    return this->trans ? this->cols : this->rows;
}

int CBM::width() const
{
    return this->trans ? this->rows : this->cols;
}

int CBM::size() const
{
    return this->rows * this->cols;
}

CBM CBM::transpose() const
{
    CustomBasicMatrix ret(*this);
    ret.trans = !ret.trans;
    return ret;
}

CBM& CBM::operator+=(int scalar)
{
    for (int i = 0; i < this->rows ; ++i){
        for (int j = 0; j < this->cols ; ++j){
            this->data[i][j] += scalar;
        }
    }
    return *this;
}

CBM &CBM::operator+=(const CBM &rhs)
{
    for (int i = 0; i < this->rows ; ++i){
        for (int j = 0; j < this->cols ; ++j){
            this->data[i][j] += rhs.data[i][j];
        }
    }
    return *this;
}

CBM CBM::operator-() const
{
    CustomBasicMatrix reg(*this);

    for (int i = 0; i < reg.rows ; ++i){
        for (int j = 0; j < reg.cols; ++j){
            reg.data[i][j] = -reg.data[i][j];
        }
    }

    return reg;
}

int &CBM::operator()(int row, int col)
{
    if(this->trans)
        return this->data[col][row];
    else
        return this->data[row][col];
}

int &CBM::operator()(int row, int col) const
{
    if(this->trans)
        return this->data[col][row];
    else
        return this->data[row][col];
}


CBM sys::operator+(const CBM &lhs, const CBM &rhs)
{
    CBM temp(lhs);
    return (temp += rhs);
}

CBM sys::operator+(const CBM &lhs, int scalar)
{
    CBM temp = lhs;
    return (temp += scalar);
}

CBM sys::operator-(const CBM &lhs, const CBM &rhs)
{
    CBM temp = lhs;
    return (temp += -rhs);
}


CBM sys::operator<(const CBM& lhs, int scalar)
{
    CBM res(lhs);
    for (int i = 0; i < res.height() ; ++i){
        for (int j = 0; j < res.width(); ++j){
            res(i,j) = res(i,j) < scalar;
        }
    }
    return res;
}

CBM sys::operator<=(const CBM& lhs, int scalar)
{
    CBM res1 = lhs == scalar;
    CBM res2 = lhs < scalar;
    return (res1 += res2);
}

CBM sys::operator>(const CBM& lhs, int scalar)
{
    CBM res(lhs);
    for (int i = 0; i < res.height() ; ++i){
        for (int j = 0; j < res.width(); ++j){
            res(i,j) = res(i,j) > scalar;
        }
    }

    return res;
}

CBM sys::operator>=(const CBM& lhs, int scalar)
{
    CBM res1 = lhs == scalar;
    CBM res2 = lhs > scalar;
    return res1 += res2;
}

CBM sys::operator!=(const CBM& lhs, int scalar)
{
    CBM res1 = lhs > scalar;
    CBM res2 = lhs < scalar;
    return res1 += res2;
}

CBM sys::operator==(const CBM& lhs, int scalar)
{
    CBM res(lhs);
    for (int i = 0; i < res.height() ; ++i){
        for (int j = 0; j < res.width(); ++j){
            res(i,j) = res(i,j) == scalar;
        }
    }   return res;
}

CBM sys::operator+(int scalar, const CBM &matrix)
{
    return matrix + scalar;
}

CBM::iterator CBM::begin()
{
    return iterator (this);
}

CBM::iterator CBM::end()
{
    return iterator(this, this->rows, this->cols);
}

bool sys::any(const CBM &mat)
{
    for (CBM::const_iterator it = mat.begin() ; it != mat.end() ; it++){
        if((*it) != 0) return true;
    }
    return false;
}

bool sys::all(const CBM &mat)
{
    for (CBM::const_iterator it = mat.begin() ; it != mat.end() ; it++){
        if((*it) == 0) return false;
    }
    return true;
}

CBM::const_iterator CBM::begin() const
{

    return const_iterator(this);
}

CBM::const_iterator CBM::end() const
{

    return const_iterator(this, this->rows, this->cols);
}

std::ostream &sys::operator<<(std::ostream &os, const CBM &matrix)
{
    int *vals = new int[matrix.size()];

    for (int i = 0; i < matrix.height(); ++i){
        for (int j = 0; j < matrix.width(); ++j){
            vals[i * matrix.width() + j] = matrix(i,j);
        }
    }

    Dimensions dim(matrix.height(), matrix.width());
    std::string res = printMatrix(vals, dim);
    delete[] vals;

    return os << res;
}

/************************************/


CBM::iterator::iterator(CBM *matrix, int row, int col) : matrix(matrix), row(row), col(col){}


CBM::iterator &CBM::iterator::operator=(const iterator& other)
{
    if(this == &other) return *this;

    this->matrix = other.matrix;
    this->row = other.row;
    this->col = other.col;
    return *this;
}

int &CBM::iterator::operator*()
{
    return this->matrix->operator()(this->row, this->col);
}

CBM::iterator &CBM::iterator::operator++()
{
        this->col++;
        this->row += this->col / this->matrix->cols;
        this->col = this->col % this->matrix->cols;

    if(this->row == this->matrix->rows || this->col == this->matrix->cols){
        this->row = this->matrix->rows;
        this->col = this->matrix->cols;
    }


    return *this;
}

const CBM::iterator CBM::iterator::operator++(int)
{
    iterator i = (*this);
    ++(*this);
    return i;
}

bool CBM::iterator::operator==(const CBM::iterator &other) const
{
    bool matrixEquals = (this->matrix) == (other.matrix);
    bool colsEquals = this->col == other.col;
    bool rowsEquals = this->row == other.row;

    return matrixEquals && colsEquals && rowsEquals;
}

bool CBM::iterator::operator!=(const CBM::iterator &other) const
{
    return !this->operator==(other);
}



/************************************/



CBM::const_iterator::const_iterator(const CustomBasicMatrix *matrix, int row, int col) : matrix(matrix), row(row), col(col){}


CBM::const_iterator &CBM::const_iterator::operator=(const CBM::const_iterator &other)
{
    if(this == &other) return *this;

    this->matrix = other.matrix;
    this->row = other.row;
    this->col = other.col;
    return *this;

}

const int &CBM::const_iterator::operator*() const
{
    return this->matrix->operator()(this->row, this->col);
}

CBM::const_iterator CBM::const_iterator::operator++()
{

        this->col++;
        this->row += this->col / this->matrix->cols;
        this->col = this->col % this->matrix->cols;


    if(this->row == this->matrix->rows || this->col == this->matrix->cols){
        this->row = this->matrix->rows;
        this->col = this->matrix->cols;
    }

    return *this;
}

const CBM::const_iterator CBM::const_iterator::operator++(int)
{
    const_iterator i = (*this);
    ++(*this);
    return i;

}

bool CBM::const_iterator::operator==(const CBM::const_iterator &other) const
{
    bool matrixEquals = (this->matrix) == (other.matrix);
    bool colsEquals = this->col == other.col;
    bool rowsEquals = this->row == other.row;

    return matrixEquals && colsEquals && rowsEquals;
}

bool CBM::const_iterator::operator!=(const CBM::const_iterator &other) const
{
    return !this->operator==(other);
}

Editar: Se agregó el archivo de encabezado de Auxiliares. Se proporciona para que el archivo cpp no ​​esté disponible y puede ignorarlo en la revisión

Auxiliares.h:


#include <iostream>
#include <string>

#include <cmath>

namespace sys {

    typedef int units_t;
    
    class Dimensions {
        int row, col;
    public:
        Dimensions( int row_t,  int col_t);
        std::string toString() const;
        bool operator==(const Dimensions& other) const;
        bool operator!=(const Dimensions& other) const;
        int getRow() const ;
        int getCol() const ;
    };
    
    std::string printMatrix(const int* matrix,const Dimensions& dim);

    template<class ITERATOR_T>
    std::ostream& printMatrix(std::ostream& os,ITERATOR_T begin,
                                ITERATOR_T end, unsigned int width){
        unsigned int row_counter=0;
        for (ITERATOR_T it= begin; it !=end; ++it) {
            if(row_counter==width){
                row_counter=0;
                os<< std::endl;
            }
            os <<*it<<" ";
            row_counter++;
        }
        os<< std::endl;
        return os;
    }   
}

Respuestas

5 ALX23z Aug 27 2020 at 03:02
  1. En lugar de transponer una función cambiando las dimensiones, guardé un valor booleano que indica si la matriz está transpuesta, por lo tanto, cuando obtenemos el valor, usamos dicho valor booleano para determinar si obtenemos (i,j) o (j,i). Esto es para ahorrar memoria y sobrecarga.

Desafortunadamente, esto solo aumenta los gastos generales. De esta manera, tiene una rama innecesaria cada vez que uno simplemente quiere acceder a un elemento de la matriz que ralentiza el código y, además, seguramente dará como resultado un acceso deficiente a la memoria que ralentizará aún más el código.

Además, la implementación de operaciones como +=ignora si las matrices están transpuestas. Y la única forma de transponer una matriz es llamar a transpose()which hace una copia de todos modos.

  1. int** data;

Esto no es bueno. Realiza una asignación para cada elemento de columna de la matriz, lo que contribuye a la fragmentación de la memoria. Es preferible almacenar toda la matriz de datos como una pieza contigua de datos, es decir, int* data;averiguar dónde comienzan y terminan las columnas mediante el tamaño de row. O mejor envuélvalo en un puntero inteligente sin sobrecarga existente std::unique_ptr<int[]> data;y, aparte, no necesitará escribir todo try/catchpara la desasignación.

  1. ¿Por qué no hay un constructor predeterminado? Al menos habilitarlo. Además, aquí no hay absolutamente ninguna necesidad de hacer que el destructor sea virtual; no es necesario para esta clase. Haga que los destructores sean virtuales para las clases que tienen métodos polimórficos.

  2. int &operator()(int row, int col); int &operator()(int row, int col) const;

Creo que querías que la constversión regresara into const int&no int&.

  1. La clase debe tener un constructor de movimiento y un operador de asignación de movimiento. Esto es crucial para propósitos de memoria.

  2. ¿Qué hace iteratoren la matriz? ¿Sobre qué itera y en qué orden? No está claro en el encabezado. Además, iteratordebería estar iterando sobre filas en lugar de elementos de la matriz.

CustomBasicMatrix operator<(const CustomBasicMatrix& lhs, int scalar);
CustomBasicMatrix operator<=(const CustomBasicMatrix& lhs, int scalar);
CustomBasicMatrix operator>(const CustomBasicMatrix& lhs, int scalar);
CustomBasicMatrix operator>=(const CustomBasicMatrix& lhs, int scalar);
CustomBasicMatrix operator!=(const CustomBasicMatrix& lhs, int scalar);
CustomBasicMatrix operator==(const CustomBasicMatrix& lhs, int scalar);

¿Es así realmente como quieres usar la matriz? me parece raro Podría entender si quisieras algo como esto para una imagen... opencv lo usa cv::Mattanto para matrices como para imágenes, pero no me parece una buena opción de diseño para envolver todo a través de una clase.

  1. Todas las implementaciones de funciones están en el archivo cpp. No es bueno. Las funciones no pueden estar en línea si su definición está oculta. Debe mover todas las definiciones de funciones pequeñas al encabezado.

  2. Creo que las operaciones principales de una matriz de rango dinámico deberían ser la multiplicación por vector y calcular su inversa. Ambos faltan. Sin embargo, no es de extrañar teniendo en cuenta que escribió una intmatriz de tipos en lugar de tipos más adecuados para una matriz como floaty double.

  3. Estoy seguro de que podría escribir algunas funciones simples como reserveo resizey simplemente usarlas en lugar de copiar y pegar el procedimiento de asignación. Además, la asignación de copias eliminará innecesariamente todos los datos incluso si las dimensiones coinciden.

  4. Como parte del diseño general ahora en C ++, los visores son bastante populares y recomendados: clases que no poseen memoria (no eliminan ni asignan). Una clase MatrixViewer sería muy eficiente para terminar las cosas.