Domanda

Esiste un modello canonico o raccomandato per implementare il sovraccarico dell'operatore aritmetico in classi simili a numeri C ++?

Dalle Domande frequenti su C ++, abbiamo un operatore di assegnazione sicuro delle eccezioni che evita la maggior parte dei problemi:

class NumberImpl;

class Number {
   NumberImpl *Impl;

   ...
};

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

Ma per gli altri operatori (+, + =, ecc.) vengono forniti pochissimi consigli oltre a farli comportare come gli operatori sui tipi predefiniti.

Esiste un modo standard per definirli? Questo è quello che ho escogitato: ci sono insidie ??che non vedo?

// 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;
}
È stato utile?

Soluzione

Nel libro di Bjarne Stroustrup " Il linguaggio di programmazione C ++ " ;, nel capitolo 11 (quello dedicato al sovraccarico dell'operatore) passa attraverso una classe per un tipo di numero complesso (sezione 11.3).

Una cosa che noto da quella sezione è che implementa operazioni di tipo misto ... questo è probabilmente previsto per qualsiasi classe numerica.

In generale, quello che hai sembra buono.

Altri suggerimenti

La cosa importante da considerare quando si scrive un operatore è che gli operatori membri non subiscono conversioni sul parametro sinistro:

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

Questo perché la conversione non si applica mai a this per le funzioni membro e questo si estende agli operatori. Se invece l'operatore fosse esempio operatore + (esempio, esempio) nello spazio dei nomi globale, verrebbe compilato (o se fosse usato pass-by-const-ref).

Di conseguenza, gli operatori simmetrici come + e - sono generalmente implementati come non membri, mentre gli operatori di assegnazione composti come + = e - = sono implementati come membri (cambiano anche i dati, nel senso che dovrebbero essere membri). E, poiché si desidera evitare la duplicazione del codice, gli operatori simmetrici possono essere implementati in termini di assegnazioni composte (come nell'esempio di codice, sebbene la convenzione consiglia di rendere temporaneo all'interno della funzione).

La convenzione è di scrivere operator + (const T & amp;) e operator- (const T & amp;) in termini di operator + = (const T & amp;) e operator - = (const T & amp;) . Se ha senso aggiungere e sottrarre a / da tipi primitivi, è necessario scrivere un costruttore che costruisce l'oggetto dal tipo primitivo. Quindi gli operatori sovraccarichi lavoreranno anche per tipi primitivi, perché il compilatore chiamerà implicitamente il costruttore appropriato.

Come hai detto tu stesso, dovresti evitare di dare privilegi di accesso a funzioni che non ne hanno bisogno. Ma nel tuo codice sopra per operator + (Number, const Number & amp;) farei personalmente entrambi i parametri const riferimenti e userei un temp. Penso che non sorprenda che il commentatore sotto la tua domanda abbia mancato questo; a meno che tu non abbia una buona ragione per non farlo, evitare sorprese e trucchi ed essere il più ovvio possibile.

Se vuoi che il tuo codice si integri con altri tipi numerici, dì std :: complex , fai attenzione alle conversioni cicliche. Cioè, non fornire operatore OtherNumeric () in Numerico se OtherNumeric fornisce un costruttore che accetta un Numerico parametro.

È tradizionale scrivere l'operatore X in termini di operatore = X
È anche tradizionale che tutti i parametri per gli operatori standard siano 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;
}

Citi l'operatore di assegnazione.
Ma non hai menzionato il costruttore di copie. Poiché la tua classe è proprietaria di un puntatore RAW, mi aspetto che anche tu lo definisca. L'operatore di assegnazione viene quindi tradizionalmente scritto in termini di costruttore della copia.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top