Pergunta

Atualmente, estou tentando descobrir como mover a semântica corretamente com um objeto que contém um ponteiro para a memória alocada.Eu tenho uma grande estrutura de dados, que contém um ponteiro bruto interno para o armazenamento real (por razões de eficiência).Agora adicionei um construtor de movimento e move operator=().Nestes métodos eu sou std::move()apontando o ponteiro para a nova estrutura.No entanto, não tenho certeza do que fazer com o ponteiro da outra estrutura.

Aqui está um exemplo simples do que estou fazendo:

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;
}

Agora, pelo que entendi, depois do std::move( s1 ) para s2 a única coisa que é segura para fazer s1 ist para chamar seu construtor.No entanto, até onde posso ver, isso levaria à exclusão do ponteiro contido em s1 no destruidor, renderizando s2 inútil também.Então, acho que preciso fazer algo para tornar o destruidor seguro quando std::move()o ponteiro.Tanto quanto posso ver, a coisa mais segura a fazer aqui é configurá-lo para 0 no objeto movido, pois isso transformaria o delete em um ambiente autônomo mais tarde.Este raciocínio está correto até agora?Ou é std::move() realmente inteligente o suficiente para anular o ponteiro para mim, tornando seu uso seguro?Até agora não estou vendo nenhuma falha no conjunto de testes real, mas não tive certeza de que o construtor de movimento foi realmente chamado.

Foi útil?

Solução

"Mover" um ponteiro não é diferente de copiar um e não não defina o valor movido como nulo ('moving' está entre aspas aqui porque std::move não move nada, apenas muda o categoria de valor do argumento).Basta copiar rhs'ponteiro e então configure-o para 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
}

Melhor ainda, use std::unique_ptr<big_and_complicated> em vez de big_and_complicated* e você não precisa definir nada disso sozinho:

#include <memory>

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

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

Por último, a menos que você realmente queira structure para permanecer não copiável, é melhor apenas implementar a semântica de movimento adequada dentro de big_and_complicated e tendo structure segure um big_and_complicated objeto diretamente.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top