Создание функции присваивания перемещения, получение освобождаемого указателя не было выделено

Я пытаюсь создать функцию назначения перемещения, но все время получаю сообщение «освобождаемый указатель не был выделен»

const MyString& MyString::operator=(MyString&& move){
    cout<< "Move Assignment" << endl;
    if(this != &move){
        delete[] str;
        len = move.len;
        for(int i = 0; i<len;i++){
            str[i] = move.str[i];
        }
        move.str=nullptr;
        move.len = 0;
        return *this;
    }

    return *this;
}

a.out (37068,0x1000b45c0) malloc: * ошибка для объекта 0x1001025a0: освобождаемый указатель не был выделен a.out (37068,0x1000b45c0) malloc: * установить точку останова в malloc_error_break для отладки


person Dhruv Patel    schedule 10.06.2019    source источник
comment
Как вы распределяете ул?   -  person    schedule 11.06.2019
comment
Создайте минимальный воспроизводимый пример. Есть несколько серьезных проблем с вашей функцией, но чтобы дать исчерпывающий ответ, нам нужно больше узнать о том, как реализован MyString.   -  person alter igel    schedule 11.06.2019
comment
На самом деле это не движется. Вы копируете данные (куда-то, чего не существует ...). На самом деле вам просто нужно установить новый указатель.   -  person Aconcagua    schedule 11.06.2019
comment
Если вы переходите по значению и используете Идиому копирования и обмена самостоятельное присвоение становится невозможным, и вы можете использовать один и тот же оператор для перемещения и обычного назначения.   -  person user4581301    schedule 11.06.2019


Ответы (2)


Этот:

delete[] str;

удаляет str. Но потом:

str[i] = move.str[i];

str удален. Так что это неопределенное поведение.

Во всяком случае, это не то, как делать ход. Весь смысл переезда состоит в том, чтобы избежать копирования строки. Если предположить, что str - это char*, тогда правильная реализация будет следующей (общее имя аргумента rhs, что означает «правая сторона»):

MyString& MyString::operator=(MyString&& rhs) noexcept
{
    std::swap(len, rhs.len);
    std::swap(str, rhs.str);
    return *this;
}

Опять же: это правильная реализация, только если str - просто указатель. Обратите внимание, что реализация ничего не удаляет. Удаление произойдет в деструкторе rhs. Спецификатор noexcept не требуется, но поскольку эта реализация никогда не может генерировать исключение, его отметка noexcept позволяет компилятору выполнить еще несколько оптимизаций.

person Nikos C.    schedule 10.06.2019
comment
Спасибо. Похоже, это сработало. И у меня была еще одна проблема с моей программой, но вы также ответили на нее, исправив мое назначение хода. Спасибо! - person Dhruv Patel; 11.06.2019

Дополнение к Никосу С. ответ:

Обмен - не единственное решение, но оно довольно элегантное: вы сохраняете память целевой строки для повторного использования в исходной строке. Пока все в порядке, вы можете захотеть начать заново с пустой строкой после перемещения. Опять же, вы не должны удалять память, она отлично подходит для повторного использования. Итак, вы просто установите длину равной 0.

Однако тогда вам нужно будет отдельно запомнить, сколько символов еще уместилось в памяти. Но это все равно полезно. Подумайте, хотите ли вы перераспределять память строки каждый раз, когда вы добавляете один символ?

Скорее всего, нет. Таким образом, вы должны добавить дополнительную память (например, удвоить емкость, если у вас закончится память). Все вместе:

class MyString
{
    size_t length;
    size_t capacity;
    char* data;
public:
    MyString& operator=(MyString&& other)
    {
        if(&other != this)
        {
            std::swap(data, other.data);         // as in Nikos' answer
            std::swap(capacity, other.capacity);
            length = other.length;
            other.length = 0;                    // restart with empty string
                                                 // still you have quite a bit of
                                                 // memory already reserved
        }
        return *this;
    }
};

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

person Aconcagua    schedule 10.06.2019
comment
Единственное, что вам разрешено делать с перемещенными объектами, - это либо назначать им что-то новое, либо уничтожать их. Таким образом, установка длины на 0 на самом деле не требуется. Тут даже больно, потому что пришлось заново вводить охрану самостоятельного назначения. - person Nikos C.; 11.06.2019
comment
@NikosC. На самом деле, это в точности то же самое, что делает std::string (по крайней мере, реализация GCC) - за исключением того, что первый байт не установлен в 0. Так что, если вы правы, в GCC есть ошибка ... Любая ссылка на стандарт? - person Aconcagua; 11.06.2019
comment
Оказывается, каждый тип должен определять, какое состояние перемещено. Информацию о типах библиотек см. В этом ответе. Допустимый, но неуказанный параметр состояния также является хорошим выбором для настраиваемых типов. У строковой реализации в libstdc ++ могут быть другие причины для этого. Может, это из-за оптимизации коротких строк, не знаю. В любом случае это не ошибка. Просто в данном случае это не нужно. - person Nikos C.; 11.06.2019
comment
Однако в одном отношении вы правы: эта реализация нарушает принцип не платить за то, что вам не нужно - если вы хотите повторно использовать объект, тогда clear это явно ... - person Aconcagua; 11.06.2019