C++ перегрузка оператора = для объекта с shared_ptr

95
27 ноября 2020, 11:10

Создал класс, который реализует массив на shared_ptr. Хочу перегрузить операцию равно, внутри метода все работает - массив получает новый размер и новые указатели. Но когда происходит return, то в обїекте остается только размер, а shared_ptr становится empty. Вроде бы, мне нужно использовать shared_from_this, но мне не нужно возвращать указатели на мой массив, а объект. Как это исправить?

class DynamicArr: public std::enable_shared_from_this<DynamicArr<T>>
{
    private:
        int Size;
        std::shared_ptr<T[]> arr;
    public:
        DynamicArr()
        {
            arr.reset(new T[1]);
            arr[0] = 0;
        }
        DynamicArr(int Size)
        {
            this->Size = Size;
            arr.reset(new T[Size]);
            for (int i = 0; i < Size; arr[i++] = Size-i);
        }
       DynamicArr(DynamicArr<T> &obj)
       {
          this->Size = obj.Size;
          arr = std::move(obj.arr);
       }
       ~DynamicArr() {
          arr.reset(new T[1]);
       }
       DynamicArr<T> operator= (const DynamicArr<T> &Arr2)
    {
        if (this != &Arr2)
        {
            this->Size = Arr2.Size;
            this->arr = Arr2.arr;
            return *this;
        }
        else
            return *this;
    }
Answer 1

При таком объявлении класса реализовывать эти функции вообще не надо. Все конструкторы копирования и перемещения, операторы копирования и перемещения, а также деструктор будут правильно сгенерированы компилятором. Умные указатели в том числе для того и предназначены, чтобы в таком классе можно было пользоваться Правилом Ноля.

Не ясно, зачем вы написали конструктор копирования, который на самом деле разрушает (перемещает) исходный объект - из-за него ваш код и ведет себя странно.

Также, почему ваш оператор присваивания возвращает результат "по значению", вместо традиционного возвращения ссылки?

В данном случае можно было явно не писать и конструктор по умолчанию. Но раз вам зачем-то захотелось выделять там массив размера 1, то так тому и быть. Однако вы забыли там инициализировать Size.

Причин для наследования от std::enable_shared_from_this в вашем классе тоже не видно.

template <typename T>
class DynamicArr
{
private:
  int Size;
  std::shared_ptr<T[]> arr;
public:
  DynamicArr() : Size(1), arr(new T[1]())
    {}
  DynamicArr(int Size) : Size(Size), arr(new T[Size])
  {
    for (int i = 0; i < Size; arr[i++] = Size-i);
  }
};

Также arr[i++] = Size-i. До C++17 это выражение вызывает неопределенное поведение. После С++17 правая часть оператора присваивания упорядочена перед левой. То есть ваш код заполняет массив значениями { Size, Size - 1, ..., 1 }, а не { Size - 1, Size - 2, ..., 0 }. Это именно то, что вы хотели? Это, кстати, не согласуется с тем, что делает конструктор по умолчанию (arr[0] = 0;).

(Что интересно, в GCC поведение действительно меняется в зависимости от -std=c++17)

Answer 2

Как я и предполагал, у вас неправильно реализован конструктор копирования - вместо копирования он выполняет перемещение указателя на массив из другого объекта. Соответственно при возвращении нового объекта из operator = будет происходить перемещение указателя на массив из текущего объекта в возвращаемый. Для исправления необходимо реализовать нормальный конструктор копирования и ничего не возвращать из operator =

DynamicArr(DynamicArr<T> const & obj)
:   Size{obj.Size}
:   arr{obj.arr}
{
    return;
}
void operator =(DynamicArr<T> const & obj)
{
    if(this != ::std::addressof(Arr2))
    {
        this->Size = obj.Size;
        this->arr = obj.arr;
    }
    return;
}
Answer 3

Спасибо пользователю AR Hovseypan, теперь объект возвращается правильно.

DynamicArr<T>& operator= (const DynamicArr<T> &Arr2)
    {
        if (this != &Arr2)
        {
            this->Size = Arr2.Size;
            this->arr = Arr2.arr;
            return *this;
        }
        else
            return *this;
    }
READ ALSO
Определение вне класса для static const int

Определение вне класса для static const int

Насколько мне известно, для типа int инициализировать статические константные члены можно внутри класса, не вынося определение вне класса:

119
Как реализовать кнопку &ldquo;Удалить&rdquo; на Django?

Как реализовать кнопку “Удалить” на Django?

Я делаю простейшую тудушечкуНужна помощь в реализации удаления выполненных задач

90
Маппинг классов в C# по имени свойств?

Маппинг классов в C# по имени свойств?

Как на C# или через LINQ сделать такое:

113
Размещение remarks документации к методам

Размещение remarks документации к методам

Скажите, куда необходимо помещать блок <remarks>

81