Newsgroup: it.comp.lang.c++
Titolo: Aituo: Costruttori standard

Salve a tutto l'NG,
sto studiando il C++ da un po' di tempo e mi e' sorta una domanda:
Che differenza c'e' tra il costruttore di copia e quello di
assegnamento ?
Se possibile vorrei ricevere anche un esempio.
Grazie anticiapatamente e se non ci sentiamo prima
Buon natale e felice anno nuovo
Donald

Un costruttore di copia e' caratterizzato dalla dichiarazione, detta
pippo la tua classe:
pippo::pippo(const pippo&);
e costruisce un pippo (alloca la memoria etc.) a partire da un altro
pippo. Vieie usato normalmente nei passagi di argomenti a funzioni,
oppure puo' essere chiamato esplicitamente:
pippo a;
..
..
pippo p(a);
Un costruttore di assegnamento non e' propriamente un costruttore. E'
l'overload del operatore di assegnamento =, con dichiarazione:
pippo& pippo::operator=(const pippo&);
e serve per gestire i casi in cui scrivi:
p = a;
Una serie di consigli tipici:
1) tutte le volte che una classe alloca dinamicamente la memoria,
occorre provvedere un copy constructor, un operatore di asssegnamento e
un distruttore
2) per assegnare il "valore" di un oggetto ad un altro e' meglio usare
il copy constructor che l'assignment, non appena possibile: p.e.
pippo a;
pippo b = a;
e' sconveinente rispetto a
pippo b(a);
perche' nel primo caso viene prima chiamato il defaul constructor per un
oggetto b, e poi a viene assegnato a b (due operazioni), mentre nel
secondo caso viene solo chiamato il copy constructor (una operazione)
Questa e' la risposta semplice: ci saranno poi altri che ti daranno
risposte piu' complete!


 Se possibile vorrei ricevere anche un esempio.
Supponiamo di volere rivestire delle chiamate di una
certa libreria L che gestiscono una certa risorsa R; esse
definiscono un tipo opaco HR, "handle di risorsa R",
e delle funzioni:
    HR HR_Apri(const char * nomerisorsa);
    HR HR_Clona(HR altrarisorsa);
    void HR_Chiudi(HR larisorsa);
oltre a varie altre che accettano un HR, tipo
    double HR_Smizzola(HR larisorsa, double pykzed);
eccetera.
L'approccio naturale per incapsulare queste API in
modo C++ consiste nel definire una classe:
    class R {
        HR larisorsa;
    public:
        R(const char* nomerisorsa) {
            HR = HR_Apri(nomerisorsa);
        }
        ~R() { HR_Chiudi(larisorsa); }
        double Smizzola(double pykzed) {
            return HR_Smizzola(larisorsa,pykzed);
        }
        // ecc, ma, tra l'altro:
        R(const R&altro);                        // copy constructor
        void operator=(const R&altro);    // assegnamento
    };
Ma concentriamoci sulla differenza CRUCIALE fra copy
ctor e assignment, che implemento fuori dalla classe
proprio allo scopo di evidenziarli meglio...:
R::R(const R&altro): 
    larisorsa(HR_Clona(altro.larisorsa)) {}
void R::operator=(const R&altro)
{
    HR_Chiudi(larisorsa);
    larisorse = HR_Clona(altro.larisorsa);
}
Vedi qual e` questa cruciale differenza?  Il ctor e`
chiamato quando l'oggetto ancora NON e` stato
inizializzato, quindi il suo lavoro e` appunto quello
di fare questa inizializzazione (idealmente, come
qui, nel ctor-initializer-list).
L'operator e` chiamato su di un oggetto che e`
GIA` stato inizializzato, quindi esso deve invece
_MODIFICARE_ uno stato GIA` ESISTENTE.  In
questo caso, ed e` abbastanza tipico, deve
LIBERARE ("chiudere") la "vecchia" risorsa, prima
di ASSEGNARE la nuova (ottenuta, come nel
copy ctor, come "clone" della risorsa "altra").
Alex

 secondo caso viene solo chiamato il copy constructor (una operazione)
Nossignore: nel primo caso si ha l'uso di un copy constructor, esattamente
come nel secondo.
Quello che sarebbe sicuramente meno conveniente (default constructor,
poi assignment operator) sarebbe invece:
    pippo b;
    b = a;
Questo e` *MOLTO* diverso da
 
    pippo b = a;
che e` invece identicamente equivalente (se a e` un pippo) a:
    pippo b(a);
e non bisogna *ASSOLUTAMENTE* confondere il primo caso (default
construction, poi assignment) con i casi del secondo tipo (copy ctor,
espressa in due modi sintatticamente distinti, semanticamente eguali).
Alex

 assegnamento ?
Il costruttore di copia viene utilizzato nell'inizializzazione tramite
oggetto dello stesso tipo:
T a;
T b = a;
nel passaggio di parametri oggetto per valore a funzioni:
void miaFunz( T );
e nella restituzione di valori oggetto da parte di funzioni:
T miaFunz();
L'assegnamento viene invece utilizzato solamente nell'operazione
omonima:
T a;
T b;
a = b;
o magari
a = 1;
(tutto dipende dal numero e tipo di operatori di assegnamento definiti).
EMG

 espressa in due modi sintatticamente distinti, semanticamente eguali).
Hai ragione! Ho risparmiato gesso e scritto una cavolata. Meglio essere MOLTO precisi quando si
risponde alle domande...
Guido