Реализация собственного класса Matrix

Создаю свой класс Matrix(использовать контейнеры STL нельзя, все ресурсы управляются вручную). Начал реализовать конструктор и оператор перемещения, но начинают появляться сомнения, что ушел в какую то не ту сторону. Можно узнать как можно сделать данные конструкторы и операторы наиболее эффективными с точки зрения памяти и времени выполнения?

Сам класс Matrix:

template <typename T>
    class Matrix
    {
    private:
        T *data = nullptr;
        size_t rows; 
        size_t cols; 

Конструктор и оператор:

Matrix(Matrix &&other) noexcept : rows((other.rows)), cols((other.cols)), data(other.data)
        {
            other.data = nullptr;
            other.rows = 0;
            other.cols = 0;
        }

        Matrix &operator=(Matrix<T> &&other) noexcept
        {

            if (&other == this)
            {
                return *this;
            }

            if (rows != other.rows || cols != other.cols)
            {
                std::cout << "Ошибка присваивания матриц разного размеры" << std::endl;
                exit(-1);
            }

            clean();

            std::swap(data, other.data);
            std::swap(rows, other.rows);
            std::swap(cols, other.cols);

            other.data = nullptr;
            other.rows = 0;
            other.cols = 0;

            return *this;
        }

Функция очистки:

void clean()
{
    if (nullptr != data)
    {
        rows = 0;
        cols = 0;
        delete[] data;
    }
}

Ответы (1 шт):

Автор решения: HolyBlackCat

Премещающий конструктор сделан нормально, но непонятно зачем там двойные скобки.

Еще есть более выпендрежный способ записать то же самое: : rows(std::exchange(other.rows, 0)). Тогда отдельное зануление в теле конструктора не нужно.


В перемещающем присваивании проверка на совпадение размеров смотрится очень странно, я бы убрал. swap тоже смотрится странно; раз вы все равно зануляете поле, можно обойтись rows = other.rows; other.rows = 0;.

Есть трюк, позволяющий в 99% случаев писать присваивание не думая, называется copy & swap idiom. Пишется так:

Matrix &operator=(Matrix<T> other) noexcept
{
    std::swap(data, other.data);
    std::swap(rows, other.rows);
    std::swap(cols, other.cols);
    return *this;
}

Прелесть в том, то он может одновременно работать как копирующий (если есть копирующий конструктор) и как перемещающий (если есть перемещающий конструктор). При такой записи проверка на самоприсваивание больше не нужна, и бросок исключения из копирующего/перемещающего конструктора не оставляет класс в неправильном состоянии.


В clear() забыли занулить указатель.


В низкоуровневых классах а-ля "матрица" не стоит печатать ничего в консоль и/или закрывать программу. Об ошибке лучше сообщать броском исключения, или возвращаемым значением функции (для operator= последнее не подойдет), или чем-то таким.

→ Ссылка