PDA

View Full Version : [C++]Strano comportamento costruttore di copia ridefinito


silvertears
04-12-2005, 11:08
Salve a tutti, vi posto quà di seguito il codice di un programmino d'esempio per spiegare il mio problema:


#include <cstdlib>
#include <iostream>
#include <math.h>
using namespace std;

struct punto {double x, y;};
class poligono
{ int nvertici; punto* pp;
public:
poligono();
poligono (int n, const punto v[]);
~poligono();
poligono(const poligono& pol);
};
poligono::poligono()
{
nvertici = 1; pp = new punto[nvertici];
}
poligono::poligono(int n, const punto v[])
{
nvertici = n;
pp = new punto[nvertici];
for (int i=0; i<nvertici; i++)
{
(pp+i)->x = v[i].x;
(pp+i)->y = v[i].y;
}
}
poligono::~poligono()
{
delete[] pp;
}

poligono::poligono(const poligono& pol)
{
cout<< "costruttore di copia\n";
nvertici = pol.nvertici;
pp = new punto[nvertici];
for (int i= 0; i < nvertici; i++)
{
(pp+i)->x = (pol.pp+i)->x;
(pp+i)->y = (pol.pp+i)->y;
}
}

int main()
{
punto cc[3];
poligono bz();
poligono test = bz; // errore a tempo di compilazione "conversion from poligono()() to non-scalar type poligono requested
poligono test2 = poligono(3, cc);
system("PAUSE");
return EXIT_SUCCESS;
}



Nel main ho aggiunto i commenti alle righe che non mi quadrano, innanzitutto l'inizializzazione dell'ogetto "test" di tipo "poligono" con l'ogetto "bz" sempre di tipo "poligono",non capisco proprio perchè mi esca fuori quell'errore quando compilo.
Secondo cosa che non mi spiego, perchè quando inizializzo l'ogetto di tipo "poligono" test2 con il costruttore "poligono(3, cc)" non viene richamato il costruttore di copia? Da quello che so in quel caso dovrebbe intervenire proprio il costruttore di copia che ho ridefinito, però se mando in esecuzione l'exe non mi esce il cout del costruttore di copia.

Sapreste darmi qualche dritta?

Bane
04-12-2005, 12:30
Temo che "poligono bz();" venga interpretato come un prototipo di
funzione, quindi la sintassi corretta nel tuo caso e' "poligono bz;".

Per la seconda domanda, sinceramente non ricordo, quindi potrei andare
ad intuito, cioe' che "poligono test2 = poligono(3, cc);" equivalga a
"poligono test2(3, cc);".
Ma a questo immagino ci eri gia' arrivato da solo... =)

jappilas
04-12-2005, 12:57
da C++ Lessons:
Il costruttore di copia è uno speciale costruttore che prende come argomento un reference a un oggetto della stessa classe e crea un nuovo oggetto che ne è una copia. Per default, il compilatore mette a disposizione un costruttore di copia che effettua una copia membro a membro dall' oggetto originale a quello che viene creato (cosiddetta copia member-wise)...

nel tuo caso, la riga poligono test2 = poligono(3, cc); non dovrebbe chiamare mai il costruttore di copia, e perchè cc è di tipo punto[], e perchè i parametri passati corrispondono agli argomenti del costruttore
PS: di solito (per come mi hanno insegnato) è consigliabile implementare un default (NULL) constructor e ridefinire l' operatore di assegnazione...

Qu@ker
04-12-2005, 14:00
In questo caso:

poligono test2 = poligono(3, cc);

si parla di 'inizializzazione per copia' (copy-initialization) e viene usato proprio il copy-constructor.
Pero', visto che viene creato un oggetto temporaneo utilizzato dal copy-constructor, al compilatore e' permesso saltare quest'ultimo e creare direttamente l'oggetto.
Praticamente, come ha detto Bane, e' come fosse scritto:

poligono test2(3, cc);

silvertears
05-12-2005, 13:26
Secondo il libro del mio prof con quell'inizializzazione dovrebbe essere richiamato proprio il costruttore di copia.
A questo punto, visto che sto esaurendo con questo argomento vi riporto un altro piccolo pezzo di codice:


#include <cstdlib>
#include <iostream>
#include <string.h>

using namespace std;

class stringa
{ char* str;
public:
stringa(const char s[]);
~stringa();
};
stringa::stringa(const char s[])
{
str = new char[strlen(s)+1];
strcpy(str, s);
cout << "ogetto costruito \n";
}
stringa::~stringa()
{
delete[] str;
cout << "ogetto distrutto \n";
}
stringa ff()
{
stringa sg("acqua");
return sg;
}


int main()
{
stringa sa = ff();
system("PAUSE");
return EXIT_SUCCESS;
}


Allora, secondo quello che ci sta scritto sul libro questo programma non riuscirebbe ad inizializzare l'oggetto classe sa con il risultato della funzione ff poichè al termine della funzione verrebbe chiamato il distruttore dell'oggetto sa, distruttore che va a deallocare l'array str di sa e quindi il risultato sarebbe che sa dovrebbe avere il membro str puntatore ad area di memoria deallocata.

Io ho provato a compilarlo e va tutto bene, alla fine sa risulta proprio inizializzato con il valore restituito da ff, e non solo, il distruttore dell'oggetto sg non viene mai richiamato.

Dove sta la verità?

Bane
05-12-2005, 16:47
Vediamo, cosi' ad occhio dovrebbe succedere quanto segue:

1) "stringa sg("acqua");" -> Richiama il costruttore di sg.
2) "return sg;" Costruttore di copia e distruttore di sg.

Ma il costruttore di copia non e' stato definito, quindi viene richiamato quello
di default, che inizializza il puntatore (char*) del nuovo oggetto in base al
puntatore (char*) dell'oggetto sg... punteranno quindi entrambi alla stessa
locazione di memoria, che pero' verra' deallocata subito dopo dal distruttore
di sg.

Qu@ker
05-12-2005, 17:05
Secondo il libro del mio prof con quell'inizializzazione dovrebbe essere richiamato proprio il costruttore di copia.

Per ragioni di efficienza non viene creato l'oggetto temporaneo da dare in pasto al costruttore di copia, ma viene creato direttamente l'oggetto, diciamo cosi', finale.
Dovrebbe esserci scritto qualcosa di simile, nel tuo testo, a meno che non sia vecchio/scadente.


Allora, secondo quello che ci sta scritto sul libro questo programma non riuscirebbe ad inizializzare l'oggetto classe sa con il risultato della funzione ff poichè al termine della funzione verrebbe chiamato il distruttore dell'oggetto sa, distruttore che va a deallocare l'array str di sa e quindi il risultato sarebbe che sa dovrebbe avere il membro str puntatore ad area di memoria deallocata.
Io ho provato a compilarlo e va tutto bene, alla fine sa risulta proprio inizializzato con il valore restituito da ff, e non solo, il distruttore dell'oggetto sg non viene mai richiamato.

Dove sta la verità?

Ma dovresti vedere che un oggetto e' costruito e distrutto.
Una possibile spiegazione potrebbe essere la sequenza:

>> Passaggio 1
---
stringa ff()
{
return stringa("acqua");
}
...
stringa sa = ff();
---
>> Passaggio 2
---
stringa sa = stringa("acqua");
---
>> Passaggio 3
---
stringa sa("acqua");
---

Con cui il compilatore arriverebbe a creare e distruggere un solo oggetto..