Cuidados com a sobrecarga de operadores em C++

Neste texto vou tentar mostrar algumas armadilhas que o desenvolvedor pode encontrar quando cria seus próprios operadores. Meu primeiro exemplo é o operador de assinalamento (operator=). Considere a classe abaixo:

class A
{
  ...
  A& operator=(A&);
};

Em C/C++ temos várias maneiras de usar o operador de assinalamento (todas elas válidas):

A a, b, c;
...
a = b;
a = b = c;
(a = b) = c;
a = a;

A definição do operador de assinalamento da classe A, listada abaixo, serve muito bem para os casos acima:

A& A::operator=(A& r)
{
  // copia os membros de dados de r...

  // Retorna a referência a si mesmo.
  return *this;
}

O terceiro caso parece esquisito, mas é válido em C/C++. Essa construção pode ser evitada adicionado o modificador const na referência de retorno. Mas, ai você invalidaria o segundo caso também… No entanto essa é uma boa dica para operadores como +=. Você deverá evitar construções como:

(a += b) += c;

Adicionando const no tipo de retorno do operador:

const A& operator+=(A& r) { ... }

No entanto, no último caso (a = a), o operador realizará uma tarefa que não precisa ser feita. Realizar a “cópia” da referência r não precisa ser feita, neste caso, porque a referência aponta para o mesmo objeto!

O compilador, ao encontrar instruções de assinalamento para os mesmos objetos de tipos primitivos, não marcados como volatile, simplesmente ignorará a instrução. Mas, em C++, o compilador não pode fazer o mesmo para classes “customizadas” já que ele não tem como saber da sua intenção, como desenvolvedor… Para evitar esse trabalho adicional (ou, pelo menos, minimizá-lo), podemos reescrever o código do operador assim:

A& A::operator=(A& r)
{
  // Só copia se tivermos objetos diferentes!
  if (this != &r)
  {
    // copia os membros de dados de r...
  }

  // Retorna a referência a sí mesmo.
  return *this;
}

Notou que a comparação é feita entre o ponteiro para a referência e o ponteiro this? Isso é necessário para evitar o uso do operador ==, se este for definido para a classe A. Você pode achar isso estranho, já que a especificação de C++ diz, claramente, que não é possível obter um ponteiro para uma referência, mas para o objeto referenciado. Só que é exatamente isso que estou fazendo ai em cima.

Meu último alerta é: Se você criou o operador ==, crie também o operador !=.

Anúncios

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s