2013-06-11 13 views
15

Perché il programma C++ di sotto emette "ACCA"? Perché lo operator int() è chiamato due volte?Sovraccarico dell'operatore C++

#include "stdafx.h" 
#include <iostream> 

using namespace std; 

class Base { 
public: 
    Base(int m_var=1):i(m_var){ 
     cout<<"A"; 
    } 
    Base(Base& Base){ 
     cout<<"B"; 
     i=Base.i; 
    } 
    operator int() { 
     cout<<"C"; 
     return i; 
    } 
private: 
    int i; 
}; 

int main() 
{ 
    Base obj; 
    obj = obj+obj; 
    return 0; 
} 
+0

FYI; Questo è un costrutto creato per creare ACCA, come esercizio per il lettore. –

risposta

30

In primo luogo, questa linea:

Base obj; 

predefinite costrutti oggetto obj scegliendo il costruttore che accetta un numero intero con valore di default 1. Questo è responsabile per il primo A che viene stampato sullo standard output.

Poi, questa espressione:

obj + obj 

Richiede la raccolta di un sovraccarico di vitale di operator +. In questo caso, dal momento che obj ha una conversione definita dall'utente in int, viene selezionato il valore predefinito operator + e entrambi gli argomenti vengono convertiti in int. Questo è responsabile per i due C s stampati sullo standard output.

Quindi, il compito di obj in:

obj = obj + obj 

ha bisogno di invocare il implicitamente generato operator = per Base. Il implicitamente generato operator = ha firma:

Base& operator = (Base const&); 

Ciò significa che l'espressione sul lato destro del segno uguale, che è di tipo int, deve essere convertito in un Base oggetto temporanea dalla quale obj è assegnato (il riferimento il parametro implicitamente generato operator = è associato a questo temporaneo).

Ma la creazione di questo temporaneo da un int a sua volta richiede invocando la costruzione conversione di Base che accetta un int nuovo, che è responsabile per il secondo A essere stampato sullo standard output.

9

operator int() è chiamato due volte perché non si è sovraccaricato operator+. Il compilatore non sa come aggiungere un Base a Base, quindi vengono convertiti in int (dato che gli hai insegnato come farlo), che sa come fare. Il codice seguente stampa ADA:

#include <iostream> 

using namespace std; 

class Base { 
public: 
    Base(int m_var=1):i(m_var){ 
     cout<<"A"; 
    } 
    Base(Base& Base){ 
     cout<<"B"; 
     i=Base.i; 
    } 
    operator int() { 
     cout<<"C"; 
     return i; 
    } 
    int operator+(Base& Base) 
    { 
     cout<<"D"; 
     return i+Base.i; 
    } 
private: 
    int i; 
}; 

int main() 
{ 
    Base obj; 
    obj = obj+obj; 
    return 0; 
} 
1

Si rimanda a obj due volte nell'espressione obj+obj e ogni tale riferimento deve essere convertito in un numero intero.

Non è impossibile (anche se è un'idea orribile) che una tale conversione possa essere "stateful" - cioè, potrebbe di design restituire un valore diverso ogni volta che viene chiamato. Dopo tutto, il valore rappresentato da obj potrebbe cambiare (potrebbe essere un contatore o qualcosa del genere). Quindi il compilatore deve valutarlo nuovamente per ogni riferimento.

+0

Dato che 'operator int()' non è marcato 'const', la firma dice al compilatore in modo abbastanza diretto che dovrebbe aspettarsi che la chiamata modifichi lo stato dell'oggetto. Tuttavia, anche per un operatore const, la seconda chiamata non dovrebbe essere ottimizzata via esattamente perché potrebbe esserci qualcosa come "cout' in esso (e qui, in effetti lo è). – celtschk

+0

E possiamo pensare a una funzione che ha un effetto collaterale (come la scrittura sullo standard output) come il ritorno di una nuova versione dell'universo ogni volta che viene chiamata, che è un altro modo di interpretare "restituire un valore diverso ogni volta che è chiamato". –

1

È una chiamata una volta per ogni operando, non importa se si tratta della stessa istanza.

obj = obj+obj; 

"lancia" il primo operando su un int e quindi il secondo.

È necessario sovraccaricare l'operatore + se si desidera evitare questa trasmissione implicita.

1

Chiama il numero operator int() due volte perché è necessario convertire entrambi obj in int prima che possa aggiungerli.

5

Quando si costruisce l'oggetto, si ottiene la prima "A":

Base obj; 

Quando si specifica di aggiungere obj+obj, il compilatore ha bisogno di capire un modo per utilizzare + su obj. Dal momento che non è stato l'override un operator+ per Base, la conversione in int() viene chiamato per ogni lato dell'equazione:

obj+obj 

Questo stampa "CC".

Poi, si assegna a obj, che è di tipo Base, in modo che il costruttore che può accettare un int (i + i dall'operatore int()) è gestito, che stampa "A":

obj = obj+obj; // Assignment prints "A" 
5
obj = obj+obj; 
     ^^^--------obj converted to int here 
      ^^^----obj converted to int here 
^^^^^------------Base(int) ctor and default operator= called here 

Sovraccarico operatori del cast non è generalmente una buona idea a meno che non si capisce i costi e sa per certo che i benefici nel caso specifico li superano.

1

Il primo viene da un

Base obj;

I due Cs provengono da convertire in obj obj+obj a int come non è stata eccessivamente il operator +.

L'ultima A viene dalla conversione obj = del a obj.

2

come fa il programma C++ a "ACCA"?

La prima lettera mostrata è 'A'. Questo output è correlato a questa riga:

Base obj; 

... in cui si sta creando una nuova istanza di Base.

La riga successiva è un po 'complicato:

obj = obj+obj; 

Normalmente, questo è tradotto in obj.operator+(obj), ma non hanno operator + sovraccaricato in classe Base, quindi questo la traduzione non è valido. La restante possibilità è che l'operatore + sia in realtà l'operatore di aggiunta numerica.

E sì, questo è possibile, dal momento che è stato fornito un cast a int. Quindi è possibile convertire ogni termine dell'equazione in un int ... e pertanto operator int viene chiamato due volte. Il numero effettivo di volte che viene chiamato operator int dipende dalle ottimizzazioni attivate. Ad esempio, il compilatore potrebbe rendersi conto che entrambi i termini sono gli stessi e quindi creare un nuovo temporaneo una volta che il operator int è stato chiamato la prima volta. In tal caso, vedresti CA invece di CC.

Infine, viene eseguita l'espressione di assegnazione obj.operator=(temp). E qui la parola chiave è temp. Affinché lo defaultoperator= funzioni, poiché non è sovraccarico, è necessario un oggetto Base alla destra. In realtà è possibile averlo, dal momento che Base utilizza uno int per creare nuove istanze. Okay, allora il risultato di obj + obj era un int (diciamo che è chiamato 'x') il numero e il compilatore crea un oggetto temporaneo di classe Base che è costruito con il numero x, come è stato eseguito il seguente riga:

Base temp(x); 

Questo è il modo in cui la lettera finale vista è una "A". Ancora una volta, molti compilatori possono evitare la creazione di temporaries in alcuni casi, quindi è possibile non vedere una "A" alla fine.

noti che questa linea:

obj = obj + obj 

è così scomponibile in:

int x = ((int) obj) + ((int) obj); 
Base temp(x); 
obj = temp; 

L'istruzione finale ha il risultato come se la memoria in cui obj siede sarà occupato con il contenuto delle temp (questo è il ruolo del costruttore di copie predefinito, che esegue operator= per ogni membro della classe, rivedere la "regola dei tre").

L'overloading dell'operatore comporta un sacco di problemi che potrebbero non essere previsti se non si dispone di una conoscenza più o meno approfondita della lingua, come si può vedere. Prendi in considerazione anche che linguaggi come Java impediscono completamente il suo utilizzo, mentre C# lo consente da una prospettiva controllata.