Семантика перемещения: почему деструктор вызывается в перемещенном экземпляре, и это проблема? - PullRequest
2 голосов
/ 29 марта 2019

Я догоняю современный C ++, практикую семантику перемещения.

Я сделал очень простой тестовый пример:

  • создать экземпляр
  • переместить-построить новый экземпляр

Я заметил, что когда мои экземпляры уничтожаются, оба деструктора называются:

  • один из экземпляров, созданных при перемещении, где данные являются действительными указатель
  • тот из исходного экземпляра, где указатель данных был удален и установите значение nullptr при перемещении

Мой код, удаляющий nullptr, причиняет мне неудобство, вот вопросы:

  • заключается в том, что (удаление nullptr) даже допустимой операции (т. Е. Приводит ли это к UB; в итоге вылетает мое приложение)?
  • или мое определение оператора move-constructor / move-assignement неправильно?
  • Я нашел этот похожий вопрос , но он все еще неясен, в частности, если удаление nullptr является проблемой. Должен ли я избежать этого, проверив указатель в деструкторе? Если так, то кажется, что использование семантики перемещения вызывает систематическое неправильное поведение.

Мой вывод для теста (код ниже):

Test 5
        new 0x7512b0
        move_new 0x7512b0
        delete[] 0x7512b0
        delete[] 0

Вывод delete [] 0 - это то, что размалывает мои механизмы.

Вот главное:

#include <iostream>
#include "test5.h"
int main()
{
    std::cout << "Test 5" << std::endl;

    test5 rule5;
    test5 rule5move = std::move(rule5);
    // rule5 = std::move(rule5move);

    return 0;
}

и вот test5.h:

#ifndef TEST5_H
#define TEST5_H

class test5
{
public:
    test5(): data(new float[10]){
        std::cout << "\tnew " << data << std::endl;
        for (int i = 0; i < 10; i++)
            data[i] = float(i);
    }

    ~test5(){
        std::cout << "\tdelete[] " << data << std::endl;
        delete[] data;
    }

    // copy constructor
    test5(const test5& t) : data(new float[10]){
        std::cout << "\tcopy " << data << std::endl;
        std::copy(t.data, t.data + 10, data);
    }

    // copy operator
    test5& operator=(const test5& t){
        std::cout << "\tassign " << data << std::endl;
        std::copy(t.data, t.data + 10, data);
        return *this;
    }

    // move constructor
    test5(test5&& t): data(new float[10]){
        delete[] data;
        data = t.data;
        std::cout << "\tmove_new " << data << std::endl;
        t.data = nullptr;
    }
    // move operator
    test5& operator=(test5&& t){
        delete[] data;
        data = t.data;
        std::cout << "\tmove_assign " << data << std::endl;
        t.data = nullptr;
        return *this;
    }

private:
    float* data;
};

#endif // TEST5_H

1 Ответ

2 голосов
/ 29 марта 2019

это (удаление nullptr) даже допустимой операции (т. Е. Приводит ли это к UB; приведет ли это в конечном итоге к краху моего приложения)?

Удаление nullptr запрещено. Это действительно. Согласно онлайн-ссылке CPP :

Если выражение имеет нулевое значение указателя, деструкторы не вызываются и функция освобождения не вызывается.

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

Если вы догоняете современный C ++ (как вы упомянули), вам следует использовать умные указатели .

...