2010-03-09 18 views
6

Mi rendo conto che questa è una domanda di base, ma ho cercato online, sono stato a cplusplus.com, ho letto il mio libro e non riesco a cogliere il concetto di operatori sovraccaricati. Un esempio specifico da cplusplus.com è:Che cos'è un operatore sovraccarico in C++?

// vectors: overloading operators example 
#include <iostream> 
using namespace std; 

class CVector { 
    public: 
    int x,y; 
    CVector() {}; 
    CVector (int,int); 
    CVector operator + (CVector); 
}; 

CVector::CVector (int a, int b) { 
    x = a; 
    y = b; 
} 

CVector CVector::operator+ (CVector param) { 
    CVector temp; 
    temp.x = x + param.x; 
    temp.y = y + param.y; 
    return (temp); 
} 

int main() { 
    CVector a (3,1); 
    CVector b (1,2); 
    CVector c; 
    c = a + b; 
    cout << c.x << "," << c.y; 
    return 0; 
} 

Da http://www.cplusplus.com/doc/tutorial/classes2/ ma leggendo attraverso di essa io non sono ancora capire loro a tutti. Ho solo bisogno di un esempio di base del punto dell'operatore sovraccarico (che presumo sia il "CVector CVector :: operator + (parametro CVector)").

C'è anche questo esempio da wikipedia:

Time operator+(const Time& lhs, const Time& rhs) 
{ 
    Time temp = lhs; 
    temp.seconds += rhs.seconds; 
    if (temp.seconds >= 60) 
    { 
    temp.seconds -= 60; 
    temp.minutes++; 
    } 
    temp.minutes += rhs.minutes; 
    if (temp.minutes >= 60) 
    { 
    temp.minutes -= 60; 
    temp.hours++; 
    } 
    temp.hours += rhs.hours; 
    return temp; 
} 

Da "http://en.wikipedia.org/wiki/Operator_overloading"

L'assegnazione corrente su cui sto lavorando ho bisogno di sovraccaricare un ++ e - operatore.

Grazie in anticipo per le informazioni e mi dispiace per la domanda un po 'vaga, sfortunatamente non ne sono affatto sicuro.

risposta

14

L'overloading dell'operatore è la tecnica che C++ consente di definire come gli operatori nella lingua possono essere applicati a oggetti non incorporati.

In te esempio per il sovraccarico di operatore Time classe per il + dell'operatore:

Time operator+(const Time& lhs, const Time& rhs); 

Con questo sovraccarico, si possono ora eseguire operazioni di addizione sulla Time oggetti in modo 'naturale':

Time t1 = some_time_initializer; 
Time t2 = some_other_time_initializer; 

Time t3 = t1 + t2; // calls operator+(t1, t2) 

Il sovraccarico per un operatore è solo una funzione con il nome speciale "operatore" seguito dal simbolo di sovraccarico dell'operatore. La maggior parte degli operatori possono essere sovraccaricati - quelli che non possono sono:

. .* :: and ?: 

è possibile chiamare la funzione direttamente per nome, ma di solito non (il punto di overloading degli operatori è quello di essere in grado di utilizzare gli operatori normalmente).

La funzione di sovraccarico che viene chiamato è determinato dalla risoluzione di sovraccarico normale sugli argomenti per l'operatore - è così che il compilatore sa di chiamare il operator+() che utilizza il tipo di argomento Time dall'esempio precedente.

Un'ulteriore cosa da sapere quando si sovraccaricano gli operatori ++ e -- di incremento e decremento è che esistono due versioni di ciascuno: i moduli prefisso e postfisso. La versione postfissa di questi operatori richiede un parametro aggiuntivo int (che viene passato 0 e non ha altro scopo se non quello di distinguere tra i due tipi di operatore). standard C++ ha i seguenti esempi:

class X { 
public: 
    X& operator++();  //prefix ++a 
    X operator++(int); //postfix a++ 
}; 

class Y { }; 

Y& operator++(Y&);  //prefix ++b 
Y operator++(Y&, int); //postfix b++ 

Si dovrebbe anche essere consapevoli del fatto che gli operatori di overload non devono eseguire operazioni che sono simili alla costruita nel operatori - di essere più o meno normali funzioni che possono fare tutto ciò che volere. Ad esempio, l'interfaccia del flusso IO della libreria standard utilizza gli operatori di spostamento per l'output e l'input da/per gli stream, il che non è in realtà niente come lo spostamento dei bit. Tuttavia, se cerchi di essere troppo fantasioso con il sovraccarico dell'operatore, causerai molta confusione per le persone che cercano di seguire il tuo codice (forse anche tu quando guardi il tuo codice in un secondo momento).

Utilizzare con cura l'operatore.

+3

+1 per gli operatori che non è possibile sovraccaricare. – FrustratedWithFormsDesigner

+0

Grazie per l'assistenza ... purtroppo capisco meglio gli operatori sovraccaricati, la mia comprensione su come usarli non è ancora abbastanza vicino da permettermi di usarli. Ma almeno ora ho una migliore comprensione, grazie ancora! – Jeff

+0

+1 Risposta fantastica. In realtà non mi è mai venuto in mente che l'uso di '<<' and '>>' per lo streaming era in realtà dovuto all'overloading dell'operatore nella libreria standard. Ho sempre pensato che fosse solo una di quelle stranezze del C++ che questi operatori intendevano solo cose diverse a seconda del contesto. – LeopardSkinPillBoxHat

0

L'"operatore" in questo caso è il simbolo +.

L'idea qui è che un operatore fa qualcosa. Un operatore sovraccarico fa qualcosa di diverso.

Quindi, in questo caso, l'operatore '+', normalmente utilizzato per aggiungere due numeri, viene "sovraccaricato" per consentire l'aggiunta di vettori o tempo.

MODIFICA: l'aggiunta di due numeri interi è integrata in C++; il compilatore capisce automaticamente cosa si intende quando si fanno

int x, y = 2, z = 2; 
x = y + z; 

oggetti, d'altra parte, può essere qualsiasi cosa, in modo da utilizzare un '+' tra due oggetti non intrinsecamente alcun senso. Se hai qualcosa come

Apple apple1, apple2, apple3; 
apple3 = apple1 + apple2; 

Cosa significa quando aggiungi due oggetti Apple insieme? Nulla, finché non sovraccarichi l'operatore '+' e dici al compilatore che cosa intendi quando tu, aggiungi due oggetti Apple insieme.

+1

Sfortunatamente, questa spiegazione non ha molto senso per me ... il simbolo + sembra abbastanza inutile, questo programma (usando il programma Time come esempio) non funzionerebbe senza di esso? Perché è lì? Certo, capisco che fa "qualcosa di diverso" ... ma questo non spiega cosa sta facendo, perché lo sta facendo, o il punto di esso? – Jeff

+0

Sì, funzionerebbe senza di esso. Ma l'overloading dell'operatore può dare una sintassi più "naturale". Preferiresti scrivere b * b-4 * a * c o sottrarre (moltiplicare (b, b), moltiplicare (moltiplicare (4, a), c))? – dan04

4

Un operatore in C++ è solo una funzione con un nome speciale. Quindi invece di dire Add(int,int) dici operator +(int,int).

Ora come qualsiasi altra funzione, è possibile sovraccaricarlo per dire lavoro su altri tipi. (. es operator +(CVector, CVector)) Nel tuo esempio vettore, se si esegue l'overload operator + prendere CVector argomenti, si può quindi dire:

CVector a,b,res; 
res=a+b; 

Dal ++ e -- sono unario (si prendono solo un argomento), sovraccaricare li' d fare come:

type operator ++(type p) 
{ 
    type res; 
    res.value++; 

    return res; 
} 

Dove type è qualsiasi tipo che ha un campo chiamato value. Hai un'idea.

+0

L'operatore non deve necessariamente essere un membro di uno dei tipi di operando o almeno un amico? –

+0

No, puoi definirli al di fuori della classe, se vuoi. Penso che l'unica eccezione potrebbe essere l'operatore =. – Maynza

+0

Corretto, è possibile definire operatori globali fuori classe. – Blindy

0

Un operatore sarebbe "+", "-" o "+ =". Eseguono metodi diversi su oggetti esistenti.Questo in effetti si riduce a una chiamata al metodo. Oltre al normale metodo di chiamata, questi sembrano molto più naturali per un utente umano. Scrivere "1 + 2" sembra più normale ed è più corto di "add (1,2)". Se sovraccarichi un operatore, cambi il metodo che esegue.

Nel primo esempio, il metodo dell'operatore "+" è sovraccarico, quindi è possibile utilizzarlo per l'aggiunta vettoriale.

Suggerisco di copiare il primo esempio in un editor e di giocarci un po '. Una volta compreso il codice, il mio suggerimento sarebbe di implementare la sottrazione e la moltiplicazione del vettore.

0

Un operatore sovraccarico è quando si utilizza un operatore per lavorare con tipi che C++ non supporta "nativamente" per quell'operatore.

Ad esempio, in genere è possibile utilizzare l'operatore "+" binario per aggiungere valori numerici (float, int, double, ecc.).È anche possibile aggiungere un tipo intero a un puntatore, ad esempio:

char foo[] = "A few words"; 
char *p = &(foo[3]);  // Points to "e" 
char *q = foo + 3;  // Also points to "e" 

Ma questo è tutto! Non puoi fare più nativamente con un operatore "+" binario.

Tuttavia, l'overloading degli operatori consente di fare le cose i progettisti di C++ non costruire nel linguaggio - come usare l'operatore + per concatenare le stringhe - per esempio:

std::string a("A short"), b(" string."); 
std::string c = a + b; // c is "A short string." 

Una volta che avvolgere la testa intorno a quella , gli esempi di Wikipedia avranno più senso.

0

Prima di iniziare, ci sono molti operatori là fuori! Ecco un elenco di tutti gli operatori C++: list.

Con ciò detto, l'overloading dell'operatore in C++ è un modo per far si che un determinato operatore si comporti in un modo particolare per un oggetto.

Ad esempio, se si utilizzano gli operatori di incremento/decremento (++ e -) su un oggetto, il compilatore non capirà cosa deve essere incrementato/decrementato nell'oggetto perché non è un tipo primitivo (int , char, float ...). È necessario definire il comportamento appropriato per il compilatore per capire cosa intendi. L'overloading dell'operatore dice fondamentalmente al compilatore che cosa deve essere realizzato quando gli operatori di incremento/decremento vengono utilizzati con l'oggetto.

Inoltre, è necessario prestare attenzione al fatto che esiste un incremento/decremento postfisso e un incremento/decremento del prefisso che diventa molto importante con la nozione di iterators e si noti che la sintassi per il sovraccarico di questi due tipi di operatori è diversa gli uni dagli altri Ecco come è possibile sovraccaricare questi operatori: Overloading the increment and decrement operators

0

Un altro utilizzo dell'overloading dell'operatore, AFAIK univoco per C++, è la capacità di sovraccaricare l'operatore di assegnazione. Se si dispone di:

class CVector 
{ 
    // ... 
    private: 
     size_t capacity; 
     size_t length; 
     double* data; 
}; 

void func() 
{ 
    CVector a, b; 
    // ... 
    a = b; 
} 

Poi a.data e b.data punterà alla stessa posizione, e se si modifica una, si interesserà b pure. Probabilmente non è quello che vuoi. Ma puoi scrivere:

CVector& CVector::operator=(const CVector& rhs) 
{ 
    delete[] data; 
    capacity = length = rhs.length; 
    data = new double[length]; 
    memcpy(data, rhs.data, length * sizeof(double)); 
    return (*this); 
} 

e ottenere una copia profonda.

+0

'delete data' richiama il comportamento non definito, dovrebbe essere' delete [] data'. – fredoverflow

2

Quello che hai trovato in quei riferimenti non sono dei brutti esempi di quando vuoi un sovraccarico dell'operatore (dando significato all'aggiunta del vettore, per esempio), ma sono un codice orribile quando si tratta di dettagli.

Ad esempio, questo è molto più realistico, mostrando delegando l'operatore di assegnamento composto e corretta marcatura di una funzione membro const:

class Vector2 
{ 
    double m_x, m_y; 
public: 
    Vector2(double x, double y) : m_x(x), m_y(y) {} 
    // Vector2(const Vector2& other) = default; 
    // Vector2& operator=(const Vector2& other) = default; 

    Vector2& operator+=(const Vector2& addend) { m_x += addend.m_x; m_y += addend.m_y; return *this; } 
    Vector2 operator+(const Vector2& addend) const { Vector2 sum(*this); return sum += addend; } 
}; 
-1

overloading degli operatori permette di dare un significato proprio per l'operatore. Ad esempio, considerare il seguente frammento di codice:

char * str1 = "String1"; char * str2 = "String2"; char str3 [20];

str3 = str1 + str2;

È possibile sovraccaricare l'operatore "+" per concatenare due stringhe. Non sembra più adatto ai programmatori?

+0

come è questo sovraccarico? Sembra proprio l'aggiunta (ad es. Int str1 = 2; int str2 = 3, int str 3 = str1 + str2;), come è un sovraccarico e l'altro no? – Jeff

+0

Se 'str1',' str2' e 'str3' dove' std :: string' che eseguirà l'overload 'std :: string operator + (std :: string const &, std :: string const &)'. Essendo 'char *' è un errore. Non c'è operatore che prenda due argomenti 'char *', e anche se ce n'era uno, non c'è operatore che assegni qualsiasi risultato del precedente operatore in un array (è possibile sovraccaricare l'operatore 'T + (char *, char *) 'con qualsiasi cosa' T' desideri, ma non puoi sovraccaricare l'operatore di assegnazione per le matrici –

2

Dai commenti sopra, non vedi il punto di questo sovraccarico dell'operatore?

L'overloading dell'operatore è semplicemente uno "zucchero sintattico" che nasconde una chiamata al metodo e rende il codice molto più chiaro in molti casi.

Considera una semplice classe Integer che include un int. Dovresti scrivere add e altri metodi aritmetici, possibilmente incrementare e decrementare, richiedendo una chiamata al metodo come my_int.add (5). ora rinominare il metodo add per operatore + consente my_int + 5, che è un codice più intuitivo e più chiaro e più pulito. Ma tutto ciò che sta facendo è nascondere una chiamata al tuo operatore + (rinominato add?) Metodo.

Le cose diventano un po 'più complesse, in quanto l'operatore + per i numeri è ben compreso da tutti al di sopra della 2a classe. Ma come nell'esempio di stringa sopra riportato, gli operatori dovrebbero essere applicati solo dove hanno un significato intuitivo. L'esempio di mele è un buon esempio di dove NON sovraccaricare gli operatori. Ma applicato per dire, una classe List, qualcosa come myList + anObject, dovrebbe essere intuitivamente intesa come "aggiungi un oggetto a myList", quindi l'uso dell'operatore +. E l'operatore '-' nel senso 'Rimozione dall'elenco'.

Come ho detto sopra, il punto di tutto questo è rendere il codice (si spera) più chiaro, come nell'esempio Elenco, quale preferiresti codice? (e quali trovi più facile da leggere?) myList.add (anObject) o myList + onObject? Ma sullo sfondo, un metodo (l'implementazione di operator + o add) viene chiamato in entrambi i modi. Si può quasi pensare al compilatore che riscrive il codice: my_int + 5 diventa my_int.operator + (5)

Tutti gli esempi forniti, come le classi Tempo e Vettore, hanno tutti definizioni intuitive per gli operatori. Aggiunta vettoriale ... di nuovo, più facile da codificare (e leggere) v1 = v2 + v3 rispetto a v1 = v2.add (v3). È qui che è tutta la cautela che è probabile che tu abbia letto riguardo al non esagerare con gli operatori nelle tue classi, perché per la maggior parte non hanno senso. Ma ovviamente non c'è nulla che ti impedisca di mettere un operatore & in una classe come Apple, non aspettarti che gli altri sappiano cosa fa senza vedere il codice!

'Sovraccarico' l'operatore significa semplicemente che si sta fornendo al compilatore un'altra definizione per quell'operatore, applicata alle istanze della classe. Un po 'come i metodi sovraccarico, lo stesso nome ... diversi parametri ...

Spero che questo aiuti ...

+0

Fai attenzione a quali definizioni possono essere considerate 'intuitive' Cosa significa 'a + b' per due vettori? Aggiunge ogni elemento abbinato insieme? Creerà un nuovo vettore aggiungendo il contenuto di 'b' in una copia di' a'? Sovraccarico solo un operatore quando ha un significato chiaro univoco. L'aggiunta di due numeri complessi è ben compresa e non ci possono essere equivoci –

+0

L'aggiunta del vettore è ben definita come per i numeri complessi ... l'aggiunta dovrebbe restituire un nuovo vettore tale che (in 2D) il nuovo vettore = (x1 + x2, y1 + y2) .... dove sono i vettori originali (x1, y1) e (x2, y2) ... Cos'altro potrebbe significare ??? Ho solo menzionato Vect ors perché un esempio nell'OP li usava ... –

+0

Riflettendo, vedo il tuo pensiero su un vettore nel senso della struttura dei dati, in cui gli esempi (e come l'ho usato) erano nel senso matematico. Tuttavia, la ragione per cui la struttura dati è chiamata Vector è legata alle sue radici matematiche, quindi le operazioni come V1 + V2 dovrebbero essere definite allo stesso modo, vale a dire che è valida solo se gli elementi hanno un operatore + definito su di esse. Quindi V1 + V2 = un nuovo Vecor V3 tale che V3_i = V1_i + V2_i, con 0 <= i <| Vettore |. (tutto il vettore dovrebbe essere della stessa lunghezza). Questo equivale davvero a un vettore N-dimensionale, dove la fisica ecc. Usa il 3D. –

0

La risposta accettata da Michael Burr è abbastanza buona per spiegare la tecnica, ma dai commenti sembra che oltre al "come" sei interessato al "perché". I motivi principali per fornire un sovraccarico dell'operatore per un determinato tipo migliorano la leggibilità e forniscono un'interfaccia richiesta.

Se si dispone di un tipo per il quale v'è un unico comunemente inteso nel senso di un operatore nel dominio del problema, quindi a condizione che come un sovraccarico operatore rende il codice più leggibile:

std::complex<double> a(1,2), b(3,4), c(5, 6); 
std::complex<double> d = a + b + c;   // compare to d = a.add(b).add(c); 
std::complex<double> e = (a + d) + (b + c); // e = a.add(d).add(b.add(c)); 

Se il tipo di ha una determinata proprietà che sarà espressa naturalmente con un operatore, puoi sovraccaricare quel particolare operatore per il tuo tipo. Si consideri, ad esempio, che si desidera confrontare i propri oggetti per l'uguaglianza. Fornire operator== (e operator!=) può darti un modo semplice e leggibile di farlo.Questo ha il vantaggio di adempiere un'interfaccia comune che può essere utilizzato con algoritmi che dipendono uguaglianza:

struct type { 
    type(int x) : value(x) {} 
    int value; 
}; 
bool operator==(type const & lhs, type const & rhs) 
    { return lhs.value == rhs.value; } 
bool operator!=(type const & lhs, type const & rhs) 
    { return !lhs == rhs; } 

std::vector<type> getObjects(); // creates and fills a vector 
int main() { 
    std::vector<type> objects = getObjects(); 
    type t(5); 
    std::find(objects.begin(), objects.end(), t); 
} 

noti che quando viene implementato l'algoritmo find, dipende == essendo definita. L'implementazione di find funzionerà con i tipi primitivi e con qualsiasi tipo definito dall'utente con un operatore di uguaglianza definito. C'è un'interfaccia singola comune che ha senso. Confrontalo con la versione Java, dove il confronto dei tipi di oggetto deve essere eseguito tramite la funzione membro .equals, mentre il confronto dei tipi primitivi può essere fatto con ==. Consentendo di sovraccaricare gli operatori, è possibile lavorare con i tipi definiti dall'utente nello stesso modo in cui è possibile con i tipi primitivi.

Lo stesso vale per l'ordinazione. Se esiste un ordine (parziale) ben definito nel dominio della classe, fornire operator< è un modo semplice per implementare quell'ordine. Codice sarà leggibile, e il tipo sarà utilizzabile in tutte le situazioni in cui è richiesto un ordine parziale, come all'interno di contenitori associativi:

bool operator<(type const & lhs, type const & rhs) 
{ 
    return lhs < rhs; 
} 
std::map<type, int> m; // m will use the natural `operator<` order 

Un errore comune quando l'overloading degli operatori è stato introdotto nel linguaggio è quello del 'golden hammer "Una volta che hai un martello d'oro, tutto sembra un chiodo e il sovraccarico dell'operatore è stato abusato.

È importante notare che il motivo del sovraccarico in primo luogo sta migliorando la leggibilità. La leggibilità è migliorata solo se quando un programmatore guarda il codice, le intenzioni di ogni operazione sono chiare a prima vista, senza dover leggere le definizioni. Quando vedi che due numeri complessi vengono aggiunti come a + b, sai cosa sta facendo il codice. Se la definizione dell'operatore non è naturale (si decide di implementarla aggiungendo solo la parte reale di essa), il codice diventerà più difficile da leggere rispetto a se fosse stata fornita una funzione (membro). Se il significato dell'operazione non è ben definito per il tipo lo stesso accade:

MyVector a, b; 
MyVector c = a + b; 

Che cosa è c? È un vettore in cui ogni elemento i è la somma dei rispettivi elementi da a e b oppure è un vettore creato concatenando gli elementi di a prima degli elementi di b. Per capire il codice, si avrebbe bisogno di andare alla definizione dell'operazione, e questo significa che il sovraccarico l'operatore è meno leggibile che fornire una funzione:

MyVector c = append(a, b); 

L'insieme di operatori che possono essere sovraccaricato non è limitato agli operatori aritmetici e relazionali. È possibile sovraccaricare operator[] per indicizzare in un tipo o operator() per creare un callable oggetto che può essere usato come una funzione (questi sono chiamati funtori) o che semplificherà l'utilizzo della classe:

class vector { 
public: 
    int operator[](int); 
}; 
vector v; 
std::cout << v[0] << std::endl; 

class matrix { 
public: 
    int operator()(int row, int column); 
     // operator[] cannot be overloaded with more than 1 argument 
}; 
matrix m; 
std::cout << m(3,4) << std::endl; 

Ci sono altri usi dell'overloading dell'operatore. In particolare, lo operator, può essere sovraccaricato in modi davvero fantasiosi per scopi di metaprogrammazione, ma probabilmente è molto più complesso di quello che ti interessa davvero ora.