| [ 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. |
| [ 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. |
| [ 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. |
|
| | Home | Bookmarks | Universidades | Para Saber mais | Universidades | WEB Directory | Mapa do site | | |