FAQ Lite
Operadores de Atribuição

[ 12.1 ] O que é auto atribuição?
[ 12.2 ] Porque eu devo me preocupar com auto atribuição?
[ 12.3 ] Ok. Já entendi. Eu vou manejar a auto atribuição. Como eu faço isso?

[ 12.1 ] O que é auto atribuição?

Auto atribuição é que ocorre quando alguém atribui um objeto ao próprio objeto. Por exemplo:
    #include "Fred.hpp"    // Declares class Fred
    
    void userCode(Fred& x)
    {
      x = x;   // Self-assignment
    } 
Obviamente ninguém vai fazer uma atribuição como a auto atribuição exemplificada acima, mas já  que mais de um pointer, ou referência, podem apontar para o mesmo objeto (aliasing), é possivel que se tenha auto atribuição, sem que se perceba.
Topo
[ 12.2 ] Porque eu devo me preocupar com auto atribuição?

Se você não se preocupar com auto atribuição, você vai expor seus usuários a bugs sutis, que tem sintomas também sutis e freqüentemente desastrosos. Por exemplo, a classe seguinte vai causar um desastre completo devido a auto atribuição:
    class Wilma { };
    
    class Fred {
    public:
      Fred()                : p_(new Wilma())      { }
      Fred(const Fred& f)   : p_(new Wilma(*f.p_)) { }
     ~Fred()                { delete p_; }
      Fred& operator= (const Fred& f)
        {
          // Bad code: Doesn't handle self-assignment!
          delete p_;                // Line #1
          p_ = new Wilma(*f.p_);    // Line #2
          return *this;
        }
    private:
      Wilma* p_;
    }; 
Se alguem atribui o objeto Fred a ele mesmo, a linha #1 exclui tanto this->p quanto f.p_, já que *this e f são o mesmo objeto. Mas a linha #2 usa *f.p_, que a essa altura não é mais um objeto válido. Isso vai causar um desastre importante.

O ponto básico é que você, autor da classe Fred, é responsável por garantir que auto atribuições do objeto Fred sejam inócuas. Não assuma que os usuários não farão auto atribuição com seus objetos. A falha será sua se os seus objetos falharem devido a auto atribuição.

Ressalva: O código Fred::operator= (const Fred&) acima tem ainda um segundo problema. Se uma exceção  ocorre enquanto se processa new Vilma(*f.p_), isto é, uma exceção do tipo out-of-memory, ou uma exceção na cópia do construtor de Vilma, this-P_ se tornará um pointer errático. Ele vai apontar para um endereço de memória que a essa altura não é mais válido. Isso pode ser resolvido alocando-se novos objetos antes de se excluir velhos objetos.

Topo
[ 12.3 ] Ok. Já entendi. Eu vou manejar a auto atribuição. Como eu faço isso?

Você deve se preocupar com auto atribuição todas as vezes que cria uma classe. Isso não significa que você precisa adicionar codificação extra à todas as suas classes. A medida em que seus objetos elegantemente manejem auto atribuição, é irrelevante se você  adiciona código extra ou não.

Se você precisar adicionar código extra ao seu operador de atribuição, aqui está uma técnica simples e efetiva.

    Fred& Fred::operator= (const Fred& f)
    {
      if (this == &f) return *this;   // Gracefully handle self assignment
    
      // Put the normal assignment duties here...
    
      return *this;
    } 
O teste explícito não é sempre necessário. Por exemplo, se você quer corrigir o operador de atribuição do tópico anterior desse FAQ, para que maneje tratamento de exceções de new e/ou exceções de cópia do construtor de Vilma, você deve produzir o seguinte código. Note que esse código tem o grato efeito secundário de automaticamente manejar auto atribuição.
    Fred& Fred::operator= (const Fred& f)
    {
      // This code gracefully (albeit implicitly) handles self assignment
      Wilma* tmp = new Wilma(*f.p_);   // It would be OK if an exception got thrown here
      delete p_;
      p_ = tmp;
      return *this;
    } 
Alguns programadores hão de querer adicionar

if (this == &f) return *this;

para fazer o controle da auto atribuição mais eficiente. Essa é geralmente uma má opção. Se a auto atribuição ocorre uma vez em cada mil vezes, o if vai desperdiçar ciclos de máquina em 99,9% dos casos.

Topo Anterior Próximo Índice
C++ FAQ Lite
Copyright © 1991-98 by Marshall Cline Ph.D., cline@parashift.com
Tradução: Dagoberto Haele Arnaut

| Home | Bookmarks | Universidades | Para Saber mais | Universidades | WEB Directory | Mapa do site |