Pergunta

Muitas vezes eu adicionar um método Empty ao meu C ++ objetos para limpar o estado interno usando código semelhante ao seguinte.

class Foo
{
private:
    int n_;
    std::string str_;
public:
    Foo() : n_(1234), str_("Hello, world!")
    {
    }

    void Empty()
    {
        *this = Foo();
    }
};

Este parece ser melhor do que duplicar o código no construtor, mas eu me perguntava se *this = Foo() é uma abordagem comum quando se quer limpar um objeto? Há algum problema com este espera para me morder na parte traseira? Existem outras maneiras melhores para alcançar esse tipo de coisa?

Foi útil?

Solução

Eu deixaria o construtor chamar a minha função em vez disso:

class Foo
{
private:
    int n_;
    std::string str_;
public:
    Foo()
    {
        Reset();
    }

    void Reset()
    {
        n_ = 1234;
        str_ = "Hello, world!";
    }
};

Sim, você está inicializando desnecessariamente a cadeia como uma cadeia vazia em primeiro lugar, em seguida, fazendo um trabalho, mas isso é muito mais clara.

Outras dicas

problemas em potencial? Como você sabe que * este é realmente um Foo?

O que você está fazendo com esse método vazio, é essencialmente o mesmo que atribuir manualmente um objeto recém-construído a uma variável (a coisa que a função vazio faz).

Pessoalmente, eu remover o método vazio, e substituir todos os usos desse método com o seguinte:

// let's say, that you have variables foo and pfoo - they are properly initialized.
Foo foo, *pfoo;

// replace line "foo.Empty()" with:
foo = Foo();

// replace line "pfoo->Empty()" with:
delete pfoo;
pfoo = new Foo();
// or
*pfoo = Foo();

Eu realmente não vejo nenhuma vantagem em ter este método vazio. Ela esconde o que realmente está acontecendo com o objeto na bruxa que é chamado, o nome não é a melhor escolha, quer.

Se o chamador quer realmente um objeto limpo -. Ele não terá nenhum problema construir o objeto próprio

Além disso, considere fazer objetos imutáveis, i. , quando construídos, eles não podem ser alterados. Esta lata em uma série de cenários de salvar-se de efeitos colaterais imprevistos.

Há algo ainda mais comum do que o que você sugeriu. usando swap.

Basicamente você faz algo parecido com isto:

T().swap(*this);

uma vez que muitos contentores normais (todos os contêineres STL?) Tem um método troca constante de tempo, este é um agradável e maneira simples de limpar um recipiente e certifique-se de armazenamento é liberado.

Da mesma forma, é uma boa maneira de "encolher para caber" um recipiente bem, mas usando o construtor de cópia em vez do construtor padrão.

considerar o uso de colocação new:

void Empty() {
    this->~Foo();
    new (this) Foo();
}

Seu invoca código operator = que pode levar a todos os tipos de efeitos colaterais.

Editar em resposta aos comentários. - Este código é definitivamente bem definido, o padrão permite explicitamente. Vou postar o parágrafo mais tarde se eu encontrar o tempo. Sobre delete - é claro. O que eu quis dizer foi ~Foo(), este foi um descuido. E sim, Rob é certo também; destruindo o objeto é realmente necessário aqui para chamar destrutor da string.

Esta pode ser uma fonte potencial de vazamento de memória se você tem uma memória alocada dinamicamente no construtor.

Aqui está como eu fazer isso:

class Foo {
private:
    int n_;
    std::string str_;
public:
    Foo() : n_(1234), str_("Hello, world!")
    {
    }

    void Empty()
    {
        Foo f;
        swap(f);
    }

    void swap(Foo & other) {
        std::swap(n_, other.n_);
        swap(str_, other.str_);
    }
};

void swap(Foo & one, Foo & other) {
    one.swap(other);
}

Coloque a função swap no mesmo namespace como a classe Foo. Argumento de pesquisa dependente acha quando os usuários fazem uma chamada para swap para trocar dois Foo. Você pode implementar operator= usando sua função de troca também.

Este parece ser melhor do que duplicar o código no construtor, mas eu me perguntava se * this = Foo () é uma abordagem comum quando se quer limpar um objeto?

Apagar um objeto que não é comum uma coisa a fazer:., Mais comumente, seja um objeto (talvez até mesmo um objeto imutável) é instanciado e contém dados reais, ou não é instanciado

O tipo mais comum de coisa que são Redefinir seria recipientes ... mas, você não estaria escrevendo suas próprias classes de contêiner agora, seria você.

Existem problemas com esta espera para me morder na parte traseira?

Sim:

  • Se isto não é realmente um Foo mas em vez disso é uma DerivedFoo
  • Se o operador de atribuição de Foo não existe, ou se é de buggy (por exemplo, se ele não está definida e o operador por defeito não é bom, por exemplo, por causa de um membro de dados é um ponteiro nu).

Existem outras maneiras melhores para alcançar esse tipo de coisa?

Sim, talvez uma função livre seria melhor (evitaria ambos os problemas acima):

template<class T> void reconstruct(T* p)
{
    p->~T();
    new (p) T();
}

Sim, esta não é eficiente em termos de desempenho (criando outro objeto foo em vez de trabalhar no lugar) e ele vai morder você se você alocar memória no construtor com um vazamento de memória desagradável.

Fazendo-o mais seguro em termos de memória estaria chamando o this-> apagar e este = new Foo () -. Mas será lenta

Se você quer ser super criar um campo estático objeto vazio e memcpy-lo em Reset.

Se você quer ser apenas rápido atribuir as propriedades, um por um.

Se você quiser manter o estilo razoável, sem chamada duplicação reposição de ctor como Ates Goral sugerido, mas você vai perder a construção mais rápida com os parâmetros padrão.

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