Pergunta

Existe um padrão canônico ou recomendado para implementar a sobrecarga do operador aritmético em classes semelhantes a números C ++?

A partir das perguntas frequentes do C ++, temos um operador de tarefas à prova de exceção que evita a maioria dos problemas:

class NumberImpl;

class Number {
   NumberImpl *Impl;

   ...
};

Number& Number::operator=(const Number &rhs)
{
   NumberImpl* tmp = new NumberImpl(*rhs.Impl);
   delete Impl;
   Impl = tmp;
   return *this;
}

Mas para outros operadores ( +, +=, etc ..) muito pouco conselho é dado além de fazê-los se comportar como os operadores em tipos internos.

Existe uma maneira padrão de defini -las? É isso que eu criei - existem armadilhas que não estou vendo?

// Member operator
Number& Number::operator+= (const Number &rhs)
{
    Impl->Value += rhs.Impl->Value; // Obviously this is more complicated
    return *this;
}

// Non-member non-friend addition operator
Number operator+(Number lhs, const Number &rhs)
{
     return lhs += rhs;
}
Foi útil?

Solução

No livro de Bjarne Stroustrup "A linguagem de programação C ++", no capítulo 11 (aquele dedicado ao sobrecarga do operador), ele passa por uma aula para um tipo de número complexo (Seção 11.3).

Uma coisa que percebo nessa seção é que ele implementa operações do tipo misto ... provavelmente é esperado para qualquer classe numérica.

Em geral, o que você tem parece bom.

Outras dicas

A grande coisa a considerar ao escrever qualquer operador é que os operadores de membros não sofrem conversões no parâmetro esquerdo:

struct example {
  example(int);
  example operator + (example);
};

void foo() {
  example e(3), f(6);
  e + 4; // okay: right operand is implicitly converted to example
  e + f; // okay: no conversions needed.
  6 + e; // BAD: no matching call.
}

Isso ocorre porque a conversão nunca se aplica a this para funções de membro, e isso se estende aos operadores. Se o operador fosse em vez disso example operator + (example, example) No espaço para nome global, ele compilaria (ou se fosse usado pass-por-consciar-ref).

Como resultado, operadores simétricos como + e - são geralmente implementados como não membros, enquanto os operadores de atribuição de compostos como += e -= são implementados como membros (eles também mudam de dados, o que significa que devem ser membros). E, como você deseja evitar a duplicação de código, os operadores simétricos podem ser implementados em termos de atribuição de composto (como no seu exemplo de código, embora a Convenção recomende tornar o temporário dentro da função).

A convenção é escrever operator+(const T&) e operator-(const T&) em termos de operator+=(const T&) e operator-=(const T&). Se fizer sentido adicionar e subtrair a/de tipos primitivos, você deve escrever um construtor que construa o objeto do tipo primitivo. Em seguida, os operadores sobrecarregados também funcionarão para tipos primitivos, porque o compilador chamará o construtor apropriado implicitamente.

Como você mencionou a si mesmo, você deve evitar dar privilégios de acesso a funções que não precisam. Mas em seu código acima para operator+(Number, const Number&) Eu pessoalmente faria as duas referências const parâmetros e usaria uma temperatura. Eu acho que não surpreende que o comentarista abaixo da sua pergunta tenha perdido isso; A menos que você tenha um bom motivo para não, evite surpresas e truques e seja o mais óbvio possível.

Se você deseja que seu código se integre com outros tipos numéricos, digamos std::complex, cuidado com as conversões cíclicas. Isto é, não forneça operator OtherNumeric() dentro Numeric E se OtherNumeric fornece um construtor que leva um Numeric parâmetro.

É tradicional escrever o operador x em termos do operador = x
Também é tradicional os parâmetros de todos os operadores padrão são const

// Member operator
// This was OK
Number& Number::operator+= (Number const& rhs) 
{
    Impl->Value += rhs.Impl->Value; // Obviously this is more complicated
    return *this;
}

// Non-member non-friend addition operator
Number operator+(Number const& lhs,Number const& rhs)
{
     // This I would set the lhs side to const.
     // Make a copy into result.
     // Then use += add the rhs
     Number result(lhs);
     return result += rhs;
}

Você menciona o operador de atribuição.
Mas você não mencionou o construtor de cópias. Como sua turma tem a propriedade de um ponteiro bruto, eu esperaria que você definisse isso também. O operador de atribuição é tradicionalmente escrito em termos do construtor de cópias.

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