Значение R и семантика перемещения с уникальным указателем: ошибка для объекта 0x7ffee5c7b670: освобождаемый указатель не был выделен

#c #unique-ptr #delete-operator #rvalue-reference #template-classes

#c #уникальный-ptr #удалить-оператор #rvalue-ссылка #шаблоны-классы #c #оператор удаления #классы шаблонов

Вопрос:

Я пытаюсь понять, как уникальные указатели перемещают семантику. Теперь я создал фиктивный пример ниже, чтобы показать, в чем проблема. Мой вопрос в том, почему этот код выдает ошибку «освобождаемый указатель не был выделен»?:

 #include <iostream>
#include <memory>

using namespace std;

template <class T>
class Object
{
public:
    T *values = nullptr;
    int size;
    Object(int size) : size(size)
    {
        values = new T[size];
    }

    ~Object()
    {
        delete[] this->values;
    }

    void myFunc(T *amp;amp;new_values)
    {
        cout << "myFunc called!" << endl;
        delete[] this->values;
        values = new_values;
    }

    void print()
    {
        for (int i = 0; i < size; i  )
            cout << this->values[i] << " ";
        cout << endl;
    }
};

int main()
{
    auto my_object = new Object<int>(4);
    std::unique_ptr<Object<int>> my_other_object(new Object<int>(4));

    int values[4] = {1, 2, 3, 4};
    int my_other_values[4] = {10, 20, 30, 40};

    /* This works all fine! */
    my_object->myFunc(std::move(values));
    my_object->print();

    /* This next bit throws pointer being freed was not allocated */
    my_other_object->myFunc(std::move(my_other_values));
    my_other_object->print();
}
  

Комментарии:

1. Вы не можете переместить массив, выделенный в стеке, в свой «уникальный ptr», потому что удалить[] этот массив — это UB, он никогда не был обновлен. Фактически это похоже на выполнение: int x = 4; { auto p = std::unique_ptr<int>(amp;x); } , что тоже не является законным.

2. Ах, теперь в этом так много смысла. Спасибо, сэр!

Ответ №1:

Это не имеет никакого отношения к std::unique_ptr . Причина, по которой вы получаете ошибку «освобождаемый указатель не был выделен», заключается в том, что показанный код пытается сделать delete то, чего не было newed .

     delete[] this->values;
    values = new_values;
  

Это правильно delete старое values , но затем просто слепо устанавливает values , чтобы указать на то, что никогда не было new отредактировано (те new_values , которые передаются здесь, нигде не new редактируются).

Следовательно, позже деструктор этого объекта попытается сделать delete то, что сейчас находится в values , но он никогда не был new отредактирован (он просто указывает на некоторый статический массив), и вы получите ошибку времени выполнения.