Costruttore di copia

di il
6 risposte

Costruttore di copia

Ho scritto questo semplice programmino per capire il meccanismo del costruttore di copia

class Prova;
void funz(Prova p) {}

int main(int argc, char* argv[]) {
	Prova p1;
	Prova p3 = p1;
	funz(p1);
	Prova p5 = Prova();
	funz(Prova());
	return 0;
}
ma ci sono alcuni aspetti che non sono riuscito a chiarire.
Col debug ho notato che:
1) eseguendo l'istruzione
Prova p1;
viene giustamente chiamato il costruttore normale, e non quello di copia
2) eseguendo
Prova p3 = p1;
viene giustamente chiamato il costruttore di copia, e non quello normale
3) eseguendo
funz(p1);
viene giustamente chiamato il costruttore di copia, e non quello normale
4) eseguendo
Prova p5 = Prova();
viene giustamente chiamato il costruttore normale, ma non capisco perché non venga chiamato anche quello di copia
5) eseguendo
funz(Prova());
viene giustamente chiamato il costruttore normale, ma non capisco perché non venga chiamato anche quello di copia

Negli ultimi due casi il costruttore normale non dovrebbe costruire un oggetto temporaneo che poi il costruttore di copia copierà rispettivamente in p5 e nell'argomento della funzione p?

Grazie

6 Risposte

  • Re: Costruttore di copia

    Mostra anche il codice della classe Prova
  • Re: Costruttore di copia

    
    class Prova {
    public:
    	Prova();
    	~Prova();
    	Prova(const Prova& prova);
    	Prova& operator=(const Prova& prova);
    	Prova operator+(Prova z);
    	int get();
    private:
    	int* p;
    };
    
    Prova::Prova() {
    	p = new int(8);
    }
    
    Prova::~Prova() {
    	delete p;
    }
    
    Prova::Prova(const Prova& prova) {
    	p = new int;
    	*p = *(prova.p);
    }
    
    Prova& Prova:: operator=(const Prova& prova) {
    	if(this != &prova) {
    		*p = *(prova.p);
    	}
    	return *this;
    }
    
    Prova Prova::operator+(Prova z) {
    	Prova temp;
    	*(temp.p) = *p + *(z.p);
    	return temp;
    }
    
    int Prova::get() {
    	return *p;
    }
    
    
  • Re: Costruttore di copia

    Autodidatta ha scritto:


    Ho scritto questo semplice programmino per capire il meccanismo del costruttore di copia
    
    class Prova;
    void funz(Prova p) {}
    
    int main(int argc, char* argv[]) {
    	Prova p1;
    	Prova p3 = p1;
    	funz(p1);
    	Prova p5 = Prova();
    	funz(Prova());
    	return 0;
    }
    
    ma ci sono alcuni aspetti che non sono riuscito a chiarire.
    Col debug ho notato che:
    1) eseguendo l'istruzione
    Prova p1;
    viene giustamente chiamato il costruttore normale, e non quello di copia
    2) eseguendo
    Prova p3 = p1;
    viene giustamente chiamato il costruttore di copia, e non quello normale
    3) eseguendo
    funz(p1);
    viene giustamente chiamato il costruttore di copia, e non quello normale
    4) eseguendo
    Prova p5 = Prova();
    viene giustamente chiamato il costruttore normale, ma non capisco perché non venga chiamato anche quello di copia
    5) eseguendo
    funz(Prova());
    viene giustamente chiamato il costruttore normale, ma non capisco perché non venga chiamato anche quello di copia

    Negli ultimi due casi il costruttore normale non dovrebbe costruire un oggetto temporaneo che poi il costruttore di copia copierà rispettivamente in p5 e nell'argomento della funzione p?

    Grazie
    calma calma, vedendo il codice della tua classe:
    1) esatto, viene chiamato il costruttore di default
    2) errore! viene chiamato l'overloading dell'operatore = (che esisterebbe anche se tu non l'avessi definito). l'istanza p1 gli viene passata per riferimento (notare il prototipo che hai usato: Prova& operator=(const Prova& prova)), quindi non è chiamato nessun costruttore poiché gli passi proprio l'indirizzo in memoria in cui è memorizzato p1.
    3) esatto, perché gli passi un oggetto per copia.
    4) qui c'è un accenno importante da fare. l'istruzione Prova() restituisce un oggetto temporaneo senza nome. in c++11, per questo tipo di oggetti (che vengono chiamati rvalue references) c'è una notazione speciale ovvero il doppio reference (Prova&&), questo perché nel c++11 è stato introdotto il concetto dello spostamento: nel c++ "vecchio" avresti avuto temporaneamente l'oggetto senza nome creato da Prova(), che veniva copiato nell'oggetto con nome p5. nel c++11 invece l'oggetto senza nome viene spostato direttamente dentro p5, quindi non viene richiamato il costruttore copia poichè non si tratta di una copia, bensì di uno spostamento.
    5) stesso discorso per il punto precedente.
  • Re: Costruttore di copia

  • Re: Costruttore di copia

    Autodidatta ha scritto:



    1) Infatti, siamo d'accordo.
    2) No, qui non siamo d'accordo, deve venire chiamato il costruttore di copia perché l'oggetto p3 non esiste, deve essere costruito, e infatti è quello che accade (provato con le cout e col gdb).
    3) Siamo di nuovo d'accordo.

    I punti 4) e 5) sono il nocciolo della questione. Stavo guardando il funzionamento del costruttore di copia e dell'operatore di assegnamento proprio perché ho iniziato a studiare il C++11, e volevo capire gli r-value reference e il concetto di movibilità. Questo programmino di cui sopra è compilato non in C+11, proprio per capire il motivo che ha portato all'introduzione del concetto di riferimento rvalue. Con il move constructor del C++11 l'oggetto temporaneo senza nome viene copiato in p5 "sfruttando" la memoria già allocata dell'oggetto senza nome, cioè senza costruire un oggetto temporaneo nel costruttore di copia, che non verrebbe invocato ma al suo posto verrebbe chiamato proprio il move constructor. La cosa che non mi torna è che qui il move constructor non c'è, perché non sono in C++11!
    ti ringrazio per la giusta correzione, in effetti ho appena testato ed è come dici tu
    andando a logica pensavo evocasse l'operatore =.

    per il resto non saprei che dirti se non di controllare effettivamente che l'opzione c++11 sia disabilitata...

    edit:
    da quanto ne so, i costruttori restituiscono un lvalue reference dell'oggetto appena costruito, quindi per il caso 5 il costruttore copia è superfluo perché alla funzione stai effettivamente passando un reference.

    per il caso 4 invece non so che dirti, forse in qualche modo oscuro l'assegnazione di un lvalue normale con un lvalue reference avviene senza chiamare nulla di conosciuto....
  • Re: Costruttore di copia

    Nessuno ne sa niente?
Devi accedere o registrarti per scrivere nel forum
6 risposte