PDA

Visualizza versione completa : Costruttore di copia C++


GiulioCesare
29-06-2004, 17.54.27
Salve ragazzi sono alle prese con lo studio del costruttore di copia, se il mio libro non si sbaglia, i casi in cui è opportuno usarlo sono 3: quando un oggetto inizializza esplicitamente un altro oggetto, quando la copia di un oggetto viene passata come paramentro ad una funzione e quando viene generato un oggetto temporaneo, più comunemente come valore di restituzione. Premesso questo devo implementare una lista che si ritrovi in una delle 3 situazioni prima citate, per così utilizzare un costruttore di copia, ma non riesco a creare una situazione per metterlo in atto. Cosa vuol dire che un oggetto inizializza esplicitamente un altro, a cosa serve passare la copia di un oggetto ad una funzione e soprattutto perchè dovrei restituire un oggetto in una funzione? Spero che qualcuno vorrà aiutarmi a capire meglio come e perchè usare un costruttore di copia, grazie :)

P8257 WebMaster
30-06-2004, 10.38.45
Ciao,
L'argomento è abbastanza complesso e capisco che molti manuali non diano l'esatta visione della cosa .. soprattutto a livello di "utilità" infatti dal tuo post si intende che forse più che il procedimento per utilizzarlo, non ne hai compreso l'utilità .. ed è invece molto importante.

Provo a fare un po' di chiarezza.

Partiamo da un esempio pratico:

Mettiamo il caso che io abbia una classe "Studente" con diversi metodi, attributi ecc. implementata a grandi linee come questa:


class Studente
{
public:
Studente(char *, int);
~Studente();

int Metodo1(...);
void Metodo2(...);
}

Studente::Studente(char *Nome, int Anni) // constr.
{
bla bla bla...
}

Studente::~Studente() // destr.
{
delete[] _name;
}

Studente::Metodo1(...)
{
bla bla bla...
}

Studente::Metodo2(...)
{
bla bla bla...
}




Come potrai notare è solo un modello...

Ora passo alla creazione di un oggetto seguendo la logica del costruttore:


Studente Primo("Marco", 12);


ed opero con l'oggetto...


Mario.Metodo1(...);
Mario.Metodo2(...);


Fin qui .. tutto normale.. il mio oggetto fino a questo momento, si è fatto carico di svolgere operazioni a seconda della sua implementazione senza dare fastidio...

Supponiamo però che io abbia bisogno di creare un oggetto "parallelo" a questo con caratteristiche praticamente identiche sul quale però devo fare altre operazioni ..

per esempio potrei avere un altro oggetto studente di nome "Secondo" di età 12 ma su cui io non decida di applicare il "Metodo2" implementato nella mia classe e su cui io voglia agire diversamente.

Qualcuno potrebbe pensare di creare un oggetto a sé.. ma questo implica dover richiamare il costruttore, istanziare di nuovo le strutture interne della classe e sprecare tempo e risorse .. la potenza del C invece mi permette di costruire una copia esatta (copia bit per bit) dell'oggetto già istanziato e già allocato.

Qualcuno per logica potrebbe pensare che una cosa del genere si possa scrivere se voglio costruire una copia di Primo chiamata Secondo:


Studente.Secondo(&Primo);


Cosa c'é di male?... qualcuno dirà.. : richiamo il costruttore e gli passo direttamente l'area di memoria contenente l'istanza di Primo, cosiche si possa costruire il secondo oggetto:

in realtà il risultato di questa espressione è UNA CATASTROFE perché vado a consegnare a Secondo direttamente la copia originale dell'oggetto Primo, l'oggetto Secondo continua quindi ad esistere anche se Primo dovesse per qualche motivo essere distrutto .. e se Primo fosse distrutto gli effetti sarebbero catastrofici in quanto Secondo punterebbe ad un'area di memoria non più referenziata.. questa espressione è quindi da DIMENTICARE.

Per ovviare a questo c'é il costruttore di copia appunto, cioè un'entità a che esegua una copia indpnedente dell'oggetto e non mi faccia lavorare più sull'originale:


Studente(Studente &nome);


In questo caso, il costruttore Studente prende come parametro una nuova istanza dell'oggetto studente che verrà creata ancora prima di eseguire il costruttore stesso che poi procederà a completare l'opera.
In questo caso staremo sicuri che andremo ad operare con una copia indipendente e del tutto identica all'oggetto Primo... senza andare ad "intaccare" la versione originale.

Passare oggetti tra funzioni, chiamate ecc. è molto utile perché un oggetto conserva in se tutte le caratteristiche date dall'implementazione di classe e dall'applicazione in esso dei metodi consentiti.

Spero di non averti fatto fare confusione, questa è la mia visione del costruttore di copia .. io lo uso moderatamente (c'é il rischio di perdere traccia degli oggetti, soprattutto in fase di distruzione) .. ma ti assicuro che è molto importante e provvidenziale a volte.

Bye :cool:

P8257 WebMaster
30-06-2004, 10.41.44
La faccina "bastonata" leggasi: "::" naturalmente.

Non si potrebbe togliere il parsing delle emoticon nelle sezioni del tag CODE ?

Bye :cool:

GiulioCesare
30-06-2004, 15.55.33
Ok grazie della spiegazione, ho provato ad implementare un costruttore di copia, in un programma con una lista e qui sono nati altri dubbi, devo fare una copia per ogni nodo ed elemento della lista, se è così significherebbe comunque dover creare un'altra lista? Ho provato a buttare giù un pò di codice, ma presenta degli errori, spero che qualcuno vorrà aiutarmi a capirli

#include <iostream>
using namespace std;
struct nodo
{
int dato;
struct nodo* next;
};
typedef struct nodo NODO;

class lista
{
private:
NODO* testa;
NODO* testa2;
NODO* scan;


public:
lista();
lista(const lista &ob);
~lista();
void creazione();
void canc();
void mostra();
};

lista::lista()
{
testa=NULL; testa2=NULL;
}

lista::lista(const lista &ob)
{
NODO* aux=testa; NODO* fine; NODO* scan;

while(aux)
{
scan= new (NODO);
*scan.dato=*ob.scan;
ob.scan=ob.scan.next;
aux=aux->next;
if(!testa2)
testa2=scan;
else
fine->next=scan;
fine=scan;
}


}

lista::~lista()
{
NODO* aux;

while(testa)
{
aux=testa;
testa=testa->next;
delete (aux);
}
}

void lista::creazione()
{
NODO* aux,fine;
int x;

cout<<"Inserire numero"<<endl;
cin>>x;

while(x)
{
aux= new (NODO);
aux->dato=x;
aux->next=NULL;
if(!testa)
testa=aux;
else
fine->next=aux;
fine=aux;
cout<<"Inserire numero"<<endl;
cin>>x;
}
}

void lista::canc()
{
NODO* aux=testa;

testa=testa->next;
delete(aux);
}

void lista::mostra()
{
NODO* aux=testa;

while(aux)
{
cout<<aux->dato<<endl;
aux=aux->next;
}
cout<<endl;
}

void mostra2(lista ob)
{

ob.mostra();
}

int main()
{
lista lista1,lista2;



lista1.creazione();
lista1.canc();
lista1.mostra();

lista2=lista1;

mostra2(lista2);

lista1.canc();

mostra2(lista2);





return 0;
}

P8257 WebMaster
30-06-2004, 16.37.09
Ci sono un po' di errori di sinstassi anche .. l'hai buttato giù tu o l'hai copiato da qualche manuale?

Se puoi postare i messaggi di errore sarebbe meglio (per tutti quelli che vogliono darti una mano, me compreso), grazie.

Bye :cool:

GiulioCesare
30-06-2004, 16.46.24
Ovviamente il codice l'ho fatto io, ecco gli errori che il compilatore segnala

copia.cpp: In copy constructor `lista::lista(const lista&)':
copia.cpp:39: request for member `dato' in `scan', which is of non-aggregate type `NODO*'
copia.cpp:40: request for member `next' in `ob->lista::scan', which is of non-aggregate type `NODO*'
copia.cpp: In member function `void lista::creazione()':
copia.cpp:80: base operand of `->' has non-pointer type `NODO'
copia.cpp:81: no match for `NODO& = NODO*&' operator
copia.cpp:4: candidates are: nodo& nodo::operator=(const nodo&)

P8257 WebMaster
01-07-2004, 09.24.45
copia.cpp:39: request for member `dato' in `scan', which is of non-aggregate type `NODO*'

Nella definizione del tipo struttura, 'dato' non è un tipo aggregato a NODO, la struttura presenta una ricorsione interna in quanto contiene già "nodo", definici la struttura in maniera più lineare, il nome della struttura dopo la sua definizione evitando riferimenti interni prima della chiusura del "typedef".

Bye :cool:

P8257 WebMaster
01-07-2004, 09.27.12
copia.cpp:40: request for member `next' in `ob->lista::scan', which is of non-aggregate type `NODO*'

Stessa cosa, membro non aggregato, riferimento a "next" prima del completamento della sua definizione.

Bye :cool:

P8257 WebMaster
01-07-2004, 09.31.25
copia.cpp:80: base operand of `->' has non-pointer type `NODO'

Se costruisci NODO, non hai un puntatore in ritorno, opera direttamente con l'operatore '.'

Bye :cool: