E infatti restituisce un riferimento, e i riferimenti si inizializzano con degli oggetti (degli lvalue in effetti), non con dei puntatori; dato che this è un puntatore all'istanza corrente, per poter inizializzare con esso il riferimento restituito esso va dereferenziato.Originariamente inviato da serenasere24
DOMANDA 1
ho una funzione per fare l'overloading dell'operatore di assegnazione (il this-> l'ho messo solo per chiarezza):
Nel mio libro c'è un esempio simile, ho capito in generale a cosa serve fare l'overloading dell'operatore di assegnazione, quello che non ho capito è perché nella dichiarazione del metodo va scritto Copia& mentre il metodo stesso restituisce l'oggetto nell'area di memoria puntata da this (*this)?codice:Copia& Copia::operator=(Copia &sorgente) { this->value1 = new int; *this->value1 = *sorgente.value1; return *this; }
Non dovrebbe restituire un riferimento?
Mi spiego meglio: quando tu passi un oggetto ad una funzione tramite un reference, di fatto stai inizializzando il parametro reference con quell'oggetto; per fare ciò, apparentemente passi semplicemente l'oggetto, non un puntatore ad esso o altra roba. Lo stesso accade quando si usano i reference come valori restituiti.
No. L'operatore = in genere restituisce sempre l'oggetto di destinazione dell'assegnamento. Questo per consentire ad esempio cose del tipo:E poi un'altra cosa, perché questo metodo deve restituire qualcosa? Non può essere void?
Questo di fatto è uguale acodice:a=b=c;
ossia, imposta b a c, e a al risultato dell'operazione tra parentesi. Se l'operazione di assegnamento non restituisse un riferimento a b, non sarebbe possibile fare sì che anche a assuma quel valore (non in un unica istruzione).codice:a=(b=c);
Per inciso, implementare l'operatore = in quella maniera non è corretto: pensa a cosa succede se uno assegna un oggetto a sé stesso: in tal caso si avrà un memory leak nel tuo operatore =.
Teoricamente è impossibile. In pratica esiste un trucco (che coinvolge un const_cast su this) che consente di aggirare la limitazione (in alcuni rarissimi casi è lecito).DOMANDA 2
[...]
Quindi è virtualmente impossibile che un metodo possa involontariamente modificare l'oggetto chiamante se questo è const?
Se per oggetto chiamante intendi oggetto su cui è richiamato, no; ad un metodo const viene sempre passato un puntatore this che risulta const, per cui non può mai modificare i campi dell'oggetto.Ma soprattutto un metodo const può modificare un oggetto chiamante non const? (in effetti potrei provare ma visto che mi è venuto in mente ora lo chiedo XD )
Ha senso dichiarare come const il valore restituito da una funzione se e solo se si tratta di un reference o di un puntatore, in modo che il chiamante non possa modificare ciò a cui punta il reference o il puntatore in questione. In tutti gli altri casi non ha senso, dato che il valore restituito viene passato per copia e tra l'altro risulta essere un rvalue.DOMANDA 3
A quanto ho capito il valore restituito da una funzione può essere modificato durante la chiamata della stessa, e per questo motivo è consigliabile dichiarare come const il valore restituito:
Tuttavia mi sono accorta che se si prova a modificare il valore restituito da una funzione, il compilatore restituisce un errore, anche se questa non restituisce un const:codice:const Classe metodo(); //l'oggetto restituito è costante
Mi da un errore che ora sinceramente non ricordo, ma qualcosa tipo che l'espressione non è ammessa.codice:metodo1().proprieta1 = 0;
Perciò da un punto di vista pratico che utilità ha dichiarare come const il valore restituito da una funzione?
In quali casi può servire?
extern di rado si usa per le funzioni (o meglio, si usa soprattutto per questioni di name mangling, ma lasciamo stare), si usa piuttosto per le variabili nel modo in cui hai detto tu. Per le funzioni a questo scopo bastano i normali prototipi.DOMANDA 4
Non ho ben capito l'utilità pratica della parola extern. Forse ho frainteso il suo significato.
Ad esempio se dichiaro una variabile come extern significa che quella è solo una dichiarazione, mentre la definizione si trova in un altro file oppure più avanti nello stesso sorgente. Ho capito bene?
Se sì, perché mai un programmatore dovrebbe fare una cosa del genere? Sicuramente dal basso della mia ignoranza ed inesperienza non ci arrivo!
La necessità di dichiarare le variabili come extern e di mettere i prototipi delle funzioni sta nel fatto che in tale maniera è possibile metterne le dichiarazioni in altri sorgenti. Quando il progetto inizia a superare certe dimensioni, mettere tutto in un unico file sorgente diventa proibitivo (aumenta il casino a dismisura, e così pure i tempi di compilazione), per cui si tendono a separare le definizioni delle funzioni e le eventuali variabili globali tra vari file .cpp. I prototipi di tali funzioni e le dichiarazioni extern delle variabili globali di ciascun .cpp vengono messe nel relativo file header .h (o .hpp).
In questa maniera, quando serve utilizzare una funzione o una variabile dichiarata in un altro .cpp basta includere nel .cpp corrente l'include relativo all'altro .cpp, così da ottenere che il compilatore sappia dell'esistenza delle funzioni e delle variabili definite di là, in modo che possa effettuare i controlli sintattici e iniziare a mettere giù il codice delle chiamate a funzione, lasciando naturalmente al posto degli indirizzi "veri" delle funzioni degli altri .cpp dei segnaposto (questo perché non ha ancora sottomano il loro codice effettivo).
È poi compito del linker, dopo la compilazione di tutti i sorgenti in moduli oggetto, tirare le fila e collegare tutti questi segnaposto con gli indirizzi delle funzioni vere e proprie definite nei vari moduli oggetto per poi generare l'eseguibile finale.
In generale auto_ptr è pensato per una semantica di ownership stretta ma cedibile; in sostanza, un solo auto_ptr alla volta è responsabile della memoria puntata, anche se, costruendo un altro auto_ptr tramite costruttore di copia (cosa che avviene, ad esempio, se restituisci un auto_ptr da una funzione), sarà il nuovo auto_ptr ad averne la responsabilità.DOMANDA 5
Questa è l'ultima, giuro.
Non ho capito se un puntatore auto_ptr può essere l'unico ad avere un riferimento ad un'area specifica di memoria allocata dinamicamente.
Oppure è l'unico puntatore che determina l'esistenza di quell'area di memoria, e cioè l'unico che quando smette di esistere l'area di memoria a cui punta viene distrutta, a prescindere dall'esistenza di altri puntatori a quell'area?
Quindi posso creare un puntatore normale e farlo puntare alla stessa area di memoria del puntatore auto_ptr? E se invece creo due puntatori auto_ptr alla stessa area di memoria come determino qual'è quello responsabile di tale area?
In boost ci sono già diversi ottimi smart pointers, allo stato attuale delle cose io uso quelli di boost, visto che c'è più varietà rispetto all'auto_ptr e visto che saranno portati quasi in blocco nel nuovo standard.Infine, ho letto che auto_ptr sarà deprecato nel prossimo standard, ma allo stato attuale delle cose ci sono delle alternative? ho letto di shared_ptr e smart_prt ma anche altre varianti, non ho capito se sono presenti nella libreria boost (mi consigliate di utilizzarla?) oppure saranno aggiunti in futuro o ci sono già.