Оператор перемещения уничтожает исходный указатель

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

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

a = Data("New data");
  1. Data(const char* cdata) вызывается конструктор.
  2. Вызывается Data operator=(Data&& data) оператор перемещения.

РЕДАКТИРОВАТЬ: я знаю следующее:

  • std::shared_ptr‹›
  • std::make_shared‹›

но я пытаюсь сделать это без этих новых функций. Я полностью осознаю.

Мой код C++:

class Data 
{
private:
    char* local_data;
    int _size = 0;
    int length(const char* c)
    {
        int i = 0;
        while(c[++i] != '\0');
        return i;
    }
public:
    Data() {
        local_data = new char[_size];
    }
    
    Data(const char* cdata){
        _size = length(cdata);
        local_data = new char[_size];
        memcpy(local_data, cdata, _size);
    }
    
    int size() { return _size; }
    char* data() { return local_data; }
    const char* data() const { return local_data; }
    
    Data& operator=(const Data& data){}
    Data& operator=(Data&& data){
        
        if(this == &data)
            return *this;
        
        _size = std::move(data.size());
        local_data = std::move(data.data());
        return *this;
    }
};

int main(){
    
    Data a("Some data");
    auto dptr = a.data(); // Gives a pointer to the original location
    a = Data("New data"); // Must modify both a and dptr
    assert(dptr == a.data()); // Should pass successfully, else fail
    return 0;
}

person Community    schedule 28.12.2020    source источник
comment
_size всегда равно 0, поэтому вы всегда выделяете 0 байтов и копируете 0 байтов.   -  person mch    schedule 28.12.2020
comment
Часть _size здесь не имеет значения. Атм. Что важно, так это move operator.   -  person    schedule 28.12.2020
comment
Необходимо изменить как a, так и dptr — этого не произойдет с семантикой перемещения. dptr — это просто const char*. Ваш оператор присваивания перемещения буквально заменяет указатель, хранящийся как local_data, другим значением. Я даже не собираюсь пытаться понять, для чего вы хотите это сделать, но один способ (весьма сомнительный) сделать это — заставить data() вернуть const char *& (т. е. ссылку на элемент, а не копию его Имейте в виду, это ужасная идея и рецепт для висящей ссылки.   -  person WhozCraig    schedule 28.12.2020
comment
@WhozCraig, спасибо. Я не хочу оборванных указателей или утечек памяти в этом отношении.   -  person    schedule 28.12.2020
comment
Часть _size здесь не имеет значения. поэтому не включайте ее для M(inimal)CVE.   -  person Jarod42    schedule 28.12.2020
comment
Не редактируйте вопрос, чтобы сделать существующие ответы недействительными.   -  person Jarod42    schedule 28.12.2020
comment
Висячая ссылка, возможно, хуже (и в лучшем случае не лучше). Решение Джарода с использованием shared_ptr является правильным подходом. Неправильное подсчет ссылок и правильное поддержание двойного (или более) владения. Будь это я, я бы взял его и не оглядывался назад.   -  person WhozCraig    schedule 28.12.2020
comment
Тем не менее, если бы я сделал это таким образом, я бы здесь не спрашивал. Как я уже писал, я полностью осведомлен о новых функциях. Но, опять же, без тех. Я пытаюсь обойтись без них.   -  person    schedule 28.12.2020
comment
Что еще вы хотите? Как я уже сказал, отсюда вы туда не попадете. Не обошлось и без ужасно хрупкой архитектуры, как я пытался (видимо, тщетно) довести до конца в своем первом комментарии.   -  person WhozCraig    schedule 28.12.2020


Ответы (2)


Есть несколько проблем с вашим class Data. Во-первых, он никогда не вызывает delete[] local_data, поэтому произойдет утечка памяти. Добавьте деструктор:

~Data() {
    delete[] local_data;
}

Затем убедитесь, что ваш оператор присваивания перемещения работает правильно. Не используйте std::move(data.data()), это фактически не устанавливает указатель в объекте, который перемещается из в nullptr. Кроме того, убедитесь, что вы правильно очистили this->local_data. Самый простой способ сделать это:

Data& operator=(Data&& data){
    std::swap(local_data, data.local_data);
    std::swap(_size, data._size);
    return *this;
}

Он меняет местами указатели, поэтому утечки памяти нет. После перемещения уничтожение data обеспечит удаление старого this->local_data.

Ваш оператор присваивания копирования пуст, вы должны исправить это, и если у вас есть оператор присваивания перемещения, вы, вероятно, также захотите реализовать конструктор перемещения. Ну наконец то:

assert(dptr == a.data()); // Should pass successfully, else fail

Это не правильно; dptr не должно совпадать с a.data(), так как dptr является копией указателя на данные, взятые до назначения перемещения. Значение dptr не изменится, если вы явно не измените dptr.

person G. Sliepen    schedule 28.12.2020

Кажется, вы хотите std::shared_ptr<std::string>:

class Data 
{
private:
    std::shared_ptr<std::string> local_data = std::make_shared<std::string>();
public:
    Data() = default;
    
    Data(const char* cdata) : local_data(std::make_shared<std::string>(cdata)) {}
    
    int size() const { return local_data->size(); }
    char* data() { return local_data->data(); }
    const char* data() const { return local_data->c_str(); }
    
    Data operator=(const Data& rhs) {
        if (this == &data)
            return *this;
        
        *local_data = *rhs.local_data;
        return *this;
    }
};

int main(){
    
    Data a("Some data");
    auto dptr = a.data(); // Gives a pointer to the original location
    a = Data("New data"); // Must modify both a and dptr
    assert(dptr == a.data()); // Should pass successfully
}
person Jarod42    schedule 28.12.2020
comment
Привет, Джарод42, спасибо. Я не хочу использовать это здесь, std::shared_ptr. Но я в полном сознании. Позвольте мне более внимательно посмотреть на то, что вы делаете. - person ; 28.12.2020
comment
Без, std::make_shared. Как это можно сделать обычным способом? - person ; 28.12.2020
comment
Обычный способ - использовать инструменты, предоставляемые std :) Я даже думаю, что std::unique_ptr достаточно (и его проще переписать, если вы не можете использовать std). - person Jarod42; 28.12.2020
comment
Джарод, но без этого. ? - person ; 28.12.2020
comment
Если вы не можете использовать стандартный материал, напишите свою собственную (упрощенную) версию, тогда вы можете использовать их вместо перезапуска с нуля для каждого класса. Если вы не знаете, как написать эквивалент std::string/std::unique_ptr/std::vector, выполните поиск, а затем задайте конкретный вопрос об этом. - person Jarod42; 28.12.2020
comment
МОЙ БОГ! Чувак, что я написал в своем коде? Данные — это класс. Он содержит текстовую информацию. Я просто хочу сделать это традиционным способом, используя указатель. Если вы внимательно посмотрите на семантику моего оператора перемещения, вы увидите, что я использую: std::move - person ; 28.12.2020
comment
Традиционный способ обработки текста - std::string.... Перемещение указателя - это просто... копирование. - person Jarod42; 28.12.2020