Обеспечение правильной семантики перемещения - PullRequest
3 голосов
/ 27 января 2012

В настоящее время я пытаюсь выяснить, как правильно перемещать семантику с помощью объекта, который содержит указатель на выделенную память.У меня большая структура данных, которая содержит внутренний необработанный указатель на фактическое хранилище (по соображениям эффективности).Теперь я добавил конструктор перемещения и переместил operator=().В этих методах я std::move() указываю на новую структуру.Однако я не уверен, что делать с указателем из другой структуры.

Вот простой пример того, что я делаю:

class big_and_complicated {
   // lots of complicated code
};

class structure {
public:
   structure() :
      m_data( new big_and_complicated() )
   {}

   structure( structure && rhs ) :
      m_data( std::move( rhs.m_data ) )
   {
      // Maybe do something to rhs here?
   }

   ~structure()
   {
      delete m_data;
   }

private:
   big_and_complicated * m_data;
}

int main() {
  structure s1;
  structure s2( std::move( s1 ) );
  return 0;
}

Теперь из того, что я понимаю, послеstd::move( s1 ) до s2 единственное, что безопасно сделать на s1, - это вызвать его конструктор.Однако, насколько я могу судить, это привело бы к удалению указателя, содержащегося в s1 в деструкторе, что также делает s2 бесполезным.Поэтому я предполагаю, что мне нужно что-то сделать, чтобы сделать деструктор безопасным, когда указатель std::move().Насколько я могу видеть, что безопаснее всего сделать здесь, это установить его в 0 в перемещенном объекте, так как это позже превратило бы delete в неработоспособность.До сих пор верны ли эти рассуждения?Или std::move() на самом деле достаточно умен, чтобы обнулить указатель для меня, сделав его использование безопасным?Пока что я не вижу сбоев в реальном наборе тестов, но я не уверен, что на самом деле вызывается конструктор перемещения.

1 Ответ

13 голосов
/ 27 января 2012

«Перемещение» указателя ничем не отличается от копирования, и не устанавливает значение перемещенного из в нулевое значение (здесь «перемещение» заключено в кавычки, потому что std::move на самом деле ничего не перемещает, этопросто изменяет значение категории аргумента).Просто скопируйте указатель rhs ', затем установите его на nullptr:

struct structure
{
    structure()
      : m_data{new big_and_complicated{}}
    { }

    structure(structure&& rhs)
      : m_data{rhs.m_data}
    {
        rhs.m_data = nullptr;
    }

    structure& operator =(structure&& rhs)
    {
        if (this != &rhs)
        {
            delete m_data;
            m_data = rhs.m_data;
            rhs.m_data = nullptr;
        }
        return *this;
    }

    ~structure()
    {
        delete m_data;
    }

private:
    big_and_complicated* m_data;

    structure(structure const&) = delete;             // for exposition only
    structure& operator =(structure const&) = delete; // for exposition only
}

Еще лучше, используйте std::unique_ptr<big_and_complicated> вместо big_and_complicated*, и вам не нужно ничего определять самостоятельно:

#include <memory>

struct structure
{
    structure()
      : m_data{new big_and_complicated{}}
    { }

private:
    std::unique_ptr<big_and_complicated> m_data;
}

Наконец, если вы на самом деле не хотите, чтобы structure оставался не копируемым, вам лучше просто реализовать правильную семантику перемещения внутри big_and_complicated и иметь structure для непосредственного удержания объекта big_and_complicated.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...