Newsgroup: it.comp.lang.c++
Titolo: Altro dubbio generale:

Altro dubbio:
Sia già definita la classe DOverloader formata dal solo membro pubblico
"pubblica" di tipo int. Mettiamo questo codice:
void main() {
    DOverloader ogg1, ogg2, ogg3;
    ogg1.pubblica=100;
    ogg2.pubblica=200;
    ogg3=ogg1+ogg2;
}
DOverloader& operator+(DOverloader& addendo1, DOverloader& addendo2) {
    DOverloader somma;
    somma.pubblica=addendo1.pubblica+addendo2.pubblica;
    return somma;
}
Una volta eseguito, il valore di ogg3.pubblica è 300, ma il compilatore mi
da un warning nel return dell'operator+, dicendomi (giustamente):
warning C4172: returning address of local variable or temporary
Come lo evito?
Ciao by Stefano from Italy
On the net: 
s.pe...@usa.net

 wrote:
Devi restituire il risultato per valore
anzicche` restituire un riferimento.
Paolo
----------------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
----------------------------------------------------------

 }
Questa definizione non č corretta. Dovrebbe essere cosě:
DOverloader operator+(DOverloader& addendo) {
    DOverloader somma;
    somma.pubblica=pubblica+addendo.pubblica;
    return somma;
}
EMG


Dipende se l'operatore e' definito come membro della classe o no.
La prima definizione e' quella per un operatore "esterno" (a parte
il riferimento), la seconda e' per un membro di DOverloader.
In generale gli operatori "simmetrici" (+, -, ...), e' meglio
definirli _non_ membri, per evitare anomalie dovute al fatto
per per i paramentri di una funzione si applicano le conversioni
implicite, mentre per le classi di cui si chiama un metodo no.
Per esempio:
  class String {
  public:
    String( const char * );
    String operator + ( const String & rhs );
    ...
  };
  String s1, s2, s3;
  s3 = s1 + s2;     // ok
  s3 = s1 + "ciao"  // ok, "ciao" viene prima convertito in String
  s3 = "ciao" + s2; // ERROR: non ce' operator+(const char *, String)
Definendo il + come operatore esterno, le conversioni si applicano
anche al primo operando e non solo al secondo.
	Mauro


DOverloader DOverloader::operator + (DOverloader& addendo1, DOverloader&
addendo2) {
    DOverloader somma;
    somma.pubblica=addendo1.pubblica+addendo2.pubblica;
    return somma; // push in stack
}
Puoi fare anche
DOverloader& DOverloader::operator += (DOverloader& other )
{
    pubblica += other.pubblica;
    return *this;    // refers to my instance
}
Ciao
Alexei

}
Se fosse una funzione membro...
Ma la funzione originaria era invece eseguiva l'overload dell'operatore
globale.


 da un warning nel return dell'operator+, dicendomi (giustamente):
Un operator+ non deve restituire un reference. Nel tuo esempio restituisci un
reference a un oggetto che viene distrutto quando la funzione termina. Sia che
si tratti di una funzione membro, sia, come in questo caso, di una funzione
esterna, bisogna restituire un oggetto per valore e non per riferimento. cioe`:
DOverloader operator+(const DOverloader& addendo1, const DOverloader& addendo2)
{
    DOverloader somma;
    somma.pubblica = addendo1.pubblica + addendo2.pubblica;
    return somma;
}
oppure
DOverloader DOverloader::operator+(const DOverloader& addendo)
{
    DOverloader somma;
    somma.pubblica = pubblica + addendo.pubblica;
    return somma;
}
Meglio che gli argomenti siano const, e` piu` corretto e non hai warnings se
passi oggetti temporanei. 
Il reference si usa nelle funzioni membro di assegnamento, o simili, per poter
essere usati in "catena", come gli operatori dei tipi nativi:
DOverloader& DOverloader::operator+=(const DOverloader& addendo)
{
    pubblica += addendo.pubblica;
    return *this;
}
Saluti
Paolo

implicite, mentre per le classi di cui si chiama un metodo no.
Anche i parametri delle funzioni membro sono sottoposti a possibili
conversioni, e` solo il primo di questi parametri che non viene sottoposto
a tale possibilita` perche` si tratta di "this".
Sicuramente volevi dire questo, ma all'inizio non si capiva bene.


}
Un altro consiglio e` poi quello di cercare di definire le funzioni
in termini di altre funzioni (almeno quando le prestazioni non sono
assolutamente fondamentali) e quindi in questo caso
DOverloader& DOverloader::operator+=(const DOverloader& rsh) {
  *this = (*this) + rsh;
  return *this;
}
anche se probabilmente sarebbe meglio definire operator+ in termini
di operator+=...
In questo modo modifiche alla classe comportano un minor lavoro e si
preserva il significato degli operatori (e` cioe` piu` difficile che per
distrazione due operatori semanticamente correlati operino con
semantiche differenti).


 implicite, mentre per le classi di cui si chiama un metodo no.
Hai ragione. Tutto giusto. Non ci pensavo. Comunque rimane il fatto che
si deve restituire un valore e non un riferimento, giusto?
EMG

>Questa definizione non č corretta. Dovrebbe essere cosě:
Ma se volessi che fosse esterna alla classe?


anzicche` restituire un riferimento.
Ovvero? Se questa è la funzione originale:
DOverloader& operator+(DOverloader& addendo1, DOverloader& addendo2) {
    DOverloader somma;
    somma.pubblica=addendo1.pubblica+addendo2.pubblica;
    return somma;
}
Dovrei togliere "&" dalla prima riga? E si io invece volessi utilizzare per
forza il reference (magari perchè la classe è grande e non mi va di farla
mettere nello stack)?


si deve restituire un valore e non un riferimento, giusto?
Si dovrebbe restituire un oggetto costante, per evitare di scrivere
(a+b) = c; 
come suggerisce S. Meyers in Effective C++.


 }
Cosě, perň, scrivendo
c = a + b;
alteri anche il valore di a, cosa che non si vuole, credo.
EMG

 
Esatto, altrimenti potresti scrivere
(a+b) = c;
Paolo

 mettere nello stack)?
Per restituire un reference bisogna avere un oggetto e cui fare
riferimento, e questo oggetto deve esistere anche dopo la fine della
funzione.  In questo caso somma e' una variabile locale, e quando
termina 'operator+', viene distrutto, e quindi il valore che viene
restituito e' l'indirizzo di un punto nello stack dove non c'e' niente.
Questo e' anche il motivo per cui e' preferibile usare ++obj invece
di obj++: ++obj puo' restituire un riferimento a se stesso, obj++
no, perche non c'e' piu' un oggetto come quello da restituire, e
quindi e' costretto a restituirlo per valore.
Se il costruttore e il distruttore di DOverloader non hanno effetti
collaterali, e' tutto inline (e quindi visibile al compilatore), e
sei fortunato, il compilatore potrebbe anche ottimizzare tutto.
Se hai oggetti pesanti e vuoi codice veloce evitendo gli oggetti temporanei,
puoi (oltre a rinunciare agli operatori infissi):
- definire classi ausiliarie che rappresentano il risultato delle operazioni;
- definire operator=(classe ausiliaria)
Le classi ausiliare si limitamo a memorizzare gli operandi (e implicitamente
l'operazione), l'operator= esegue le operazioni in maniera ottimizzata.
	Mauro



?????????????????????
operator+= non e` coinvolto in
c = a+b;
che sarebbe:
c.operator=(operator+(a, b));   // se operator+ e` globale!
o
c.operator=(a.operator+(b));   // se operator+ e` (erroneamente) un membro
Saluti,
Paolo

 ?????????????????????
Chiedo venia. Non mi ero accorto che era la definizione dell'operator+=
ed ero invece rimasto all'operator+.
EMG

}
Ma in questo modo, se faccio ogg1=ogg2+ogg3 il valore di ogg2 viene
modificato e copiato poi in ogg3. Giusto?


come suggerisce S. Meyers in Effective C++.
Non ho capito cosa accadrebbe in questo caso. Il valore di c verrebbe
assegnato all'oggetto restituito by reference dall'operator+ che c'è fra a e
b. Ma a me serve il contrario, che a c venga assegnata la somma.


}
Scusa un momento, questa funzione non dovrebbe restituire l'indirizzo di una
classe DOverloader? Perchè allora "return *this" (ritorna la classe puntata
da this) e non "return this" (ritorna il puntatore)?


 Non ho capito cosa accadrebbe in questo caso. Il valore di c verrebbe
La somma restituisce un oggetto, poi a quell'oggetto viene assegnato
il valore di c (perdendo cosi' quello della somma). Poi, finito
l'assegnamento, l'oggetto viene distrutto.
Tutto questo e' completamente inutile e privo di senso, infatti era
un esempio di cose da evitare.  Se l'oggetto restituito era const il
compilatore segnala un errore (non si puo' assegnare qualcosa a un
oggetto costante), altrimenti per il compilatore e' tutto ok, anche
se probabilmente il risultato non e' quello voluto.
	Mauro

modificato e copiato poi in ogg3. Giusto?
Quarda che quello prima era operator += e si applica solo
in espressiomi tipo A+=B.
Saluti,


da this) e non "return this" (ritorna il puntatore)?
Innanzi tutto non confondere le classi (che in C++ sono tipi) con le istanze di
classi (che sono "valori").
Quanto alla domanda, l'operatore restituisce un riferimento e non un puntatore,
ai puntatori si assegna un indirizzo, i reference invece vanno inizializzati con
istanze (nei reference il discorso degli indirizzi e` trasparente).
Ciao,


b. Ma a me serve il contrario, che a c venga assegnata la somma.
Lo so che ti serve il contrario (anche perche` l'istruzione in esame
(a+b) = c   non avrebbe molto senso), ma quando si scrive codice
e` bene evitare possibili situazioni strane e potenzialmente pericolose.


istanze (nei reference il discorso degli indirizzi e` trasparente).
Allora c'è qualcosa che non va: se una funzione ritorna int&, ritorna
l'indirizzo di memoria di un intero (credo di si)? Allora se "ciao" è un int*
(puntatore a intero), la funzione potrebbe concludersi con "return ciao"?
Ciao by Stefano from Italy
Online as 
s.pe...@usa.net

(puntatore a intero), la funzione potrebbe concludersi con "return ciao"?
No, dovrebbe essere "return *ciao" proprio perche` la gestione degli
indirizzi e` trasparente.
Paolo
------------------------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
  G: Per concludere, possiamo chiedervi di commentare
     l'attuale panorama informatico?
  I: /* l'attuale panorama informatico */
------------------------------------------------------------------