Pregunta

¿Existe un patrón canónico o recomendado para implementar la sobrecarga de operadores aritméticos en clases de tipo C ++?

Desde las Preguntas frecuentes de C ++, tenemos un operador de asignación de excepciones que evita la mayoría de los 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;
}

Pero para otros operadores (+, + =, etc.) se da muy poco consejo aparte de hacer que se comporten como los operadores en los tipos integrados.

¿Hay una manera estándar de definir esto? Esto es lo que he encontrado: ¿hay trampas que no estoy viendo?

// 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;
}
¿Fue útil?

Solución

En el libro de Bjarne Stroustrup " The C ++ Programming Language " ;, en el capítulo 11 (el dedicado a la sobrecarga de operadores) pasa a través de una clase para un tipo de número complejo (sección 11.3).

Una cosa que sí noté en esa sección es que implementa operaciones de tipo mixto ... probablemente se espera para cualquier clase numérica.

En general, lo que tienes se ve bien.

Otros consejos

Lo importante a tener en cuenta al escribir cualquier operador es que los operadores miembros no experimentan conversiones en el parámetro de la izquierda:

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.
}

Esto se debe a que la conversión nunca se aplica a this para las funciones miembro, y esto se extiende a los operadores. Si, en cambio, el operador fuera operador de ejemplo + (ejemplo, ejemplo) en el espacio de nombres global, se compilaría (o si se usara el paso por const. Ref).

Como resultado, los operadores simétricos como + y - generalmente se implementan como no miembros, mientras que los operadores de asignación compuesta como + = y - = se implementan como miembros (también cambian los datos, lo que significa que deberían ser miembros). Y, como desea evitar la duplicación de código, los operadores simétricos se pueden implementar en términos de asignaciones compuestas (como en su ejemplo de código, aunque la convención recomienda hacer lo temporal dentro de la función).

La convención es escribir operator + (const T & amp;) y operator- (const T & amp;) en términos de operator + = (const T & amp;) y operator - = (const T & amp;) . Si tiene sentido sumar y restar a / de los tipos primitivos, debe escribir un constructor que construya el objeto a partir del tipo primitivo. Luego, los operadores sobrecargados también trabajarán para tipos primitivos, porque el compilador llamará implícitamente al constructor apropiado.

Como usted mismo mencionó, debe evitar otorgar privilegios de acceso a las funciones que no lo necesitan. Pero en su código anterior para operator + (Number, const Number & amp;) , personalmente haría ambos parámetros constantes de referencias y usaría una temperatura. Creo que no es sorprendente que el comentarista que se encuentra debajo de su pregunta se haya perdido esto; a menos que tenga una buena razón para no hacerlo, evite sorpresas y trucos y sea lo más obvio posible.

Si desea que su código se integre con otros tipos numéricos, diga std :: complex , tenga cuidado con las conversiones cíclicas. Es decir, no suministre el operador OtroNumérico () en Numérico si OtroNumérico suministra un constructor que toma un Numérico parámetro.

Es tradicional escribir el operador X en términos del operador = X
También es tradicional que todos los parámetros a los operadores estándar son 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;
}

Usted menciona operador de asignación.
Pero no mencionaste el constructor de copia. Como su clase posee un puntero RAW, espero que también lo defina. El operador de Asignación se escribe tradicionalmente en términos del constructor de copia.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top