apolukhin / course-nimble_cpp

Tasks and helper libraries for nimble C++ course

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

оператор = в my_unique_ptr не является exception-safe

alexey-malov opened this issue · comments

my_unique_ptr& operator=(const my_unique_ptr& p) {
delete data_;
data_ = (p.data_ ? new int{*p.data_} : nullptr);
return *this;
}

если new выбросит исключение, то data_ будет указывать на удалённый адрес, и повторный delete в деструкторе приведёт к undefined behavior.
Кроме того, нет защиты от самоприсваивания - также будет UB при разыменовании удалённого указателя.
Лучше исправить на что-то такое:

    my_unique_ptr& operator=(const my_unique_ptr& p) {
        if (&p != this)
        {
            auto data = (p.data_ ? new int{*p.data_} : nullptr);
            delete data_;
            data_ = data;
        }
        return *this;
    }

Либо вместо этих двух

    my_unique_ptr& operator=(const my_unique_ptr& p) {
        delete data_;
        data_ = (p.data_ ? new int{*p.data_} : nullptr);
        return *this;
    }

    my_unique_ptr& operator=(my_unique_ptr&& p) {
        std::swap(data_, p.data_);
        return *this;
    }

сделать один

    my_unique_ptr& operator=(my_unique_ptr p) {
        std::swap(data_, p.data_);
        return *this;
    }

Такой "универсальный" оператор присваивания нельзя будет сделать noexcept в "оптимизированной" версии. А тут за счёт noexcept был в бенчмарке прирост раза в 1,5-2.

Можно "универсальный" оператор сделать noexcept, т.к. noexcept будет относиться только к содержимому функции между фигурными скобками и конструктор временного объекта под noexcept не попадёт. Но это немного непривычная конструкция...