Newsgroup: it.comp.lang.c++
Titolo: Costruttore di Copia

Dato il seguente listato:
#include <iostream.h>
#include <string.h>
#define MAXSTRING	32
class pointer {
private:
  char		*psStringa;
protected:
public:
  pointer(char *S = "") {
    psStringa = new char[MAXSTRING];
    memcpy(psStringa, S, MAXSTRING - 1);
    psStringa[MAXSTRING - 1] = '\0';
    cout <<endl
	 <<"Chiamata al Costruttore di default.";
  }
  pointer(const pointer &p) {
    psStringa = new char[MAXSTRING];
    bzero(psStringa, MAXSTRING - 1);
    memcpy(psStringa, p.psStringa, MAXSTRING - 2);
    cout <<endl
	 <<"Chiamata al Costruttore di Copia.";
  }
  ~pointer() {
    delete psStringa;
    psStringa = 0;
    cout <<endl
	 <<"Chiamata al Distruttore.";
  }
  friend ostream& operator << (ostream& os, const pointer &p) {
    os <<"Valore della Stringa: "
       <<p.psStringa;
    return os;
  }
};
int main (int argc, char** argv) {
  pointer	*pP_Primo = new pointer("Primo");
  pointer	*pP_Secondo = new pointer("Secondo");
  cout <<endl
       <<"pP_Primo ->\t"
       <<*pP_Primo
       <<endl
       <<"pP_Secondo ->\t"
       <<*pP_Secondo
       <<endl;
  cout <<"pP_Secondo = pP_Primo"
       <<endl;
  pP_Secondo = pP_Primo;
^^^^^^^^^^^^^^^^^^^^^^^^^^^
  cout <<endl
       <<"pP_Primo ->\t"
       <<*pP_Primo
       <<endl
       <<"pP_Secondo ->\t"
       <<*pP_Secondo
       <<endl;
  return 0;
}
Ho il problema che la riga sottolineata (^^^^) non chiama il Costruttore
di Copia: pointer(const pointer &)
Ovviamente ho tentato di modificare la riga citata in tutti i modi che
mi venivano in mente, nonche' creato piu' di un costruttore di copia
tipo: pointer(const pointer *)
Invano :(
***
C'e' qualcuno che ha voglia di spiegarmi dove sto sbagliando ?
Ringrazio anticipatamente ed Auguro Buone Ferie a tutti (quelli che ci
devono ancora andare ;) .... gli altri spero le abbiano passate bene).
U D A.

  }


ciao a tutti,
potreste spiegarmi cos'e' il costruttore di copia?
grazie
--
Tiberio Grasso

 potreste spiegarmi cos'e' il costruttore di copia?
Il copy constructor e' quello che viene richiamato per istanziare un
oggetto a partire da un altro oggetto della stessa classe: ad esempio
string s1("La vispa Teresa");           // ctor
string s2(s1);                          // copy ctor
A questo punto, anche s2 contiene "La vispa Teresa"; ed e' stato
costruito 'copiando' l'istanza s1.  Se il copy ctor non e' definito
esplicitamente, il default stabilito dal linguaggio consiste nel
generare una copia bit-per-bit; questo comporta gravi problemi se
viene allocata memoria.  Ad esempio:
class c {
 private:
  double * _pd;
 public:
  c(const double d = 0.0) throw(bad_alloc) { _pd = new double(d); };
  ~c() throw() { delete _pd; };
};
Una istruzione "c c1(5.0);" alloca memoria con operator new, ed il
pointer alla memoria allocata e' conservato in _pd.  Una successiva "c
c2(c1);" creerebbe una copia bit-per-bit di c1 e la metterebbe in c2:
il _pd dell'istanza c2 punta allora alla stessa memoria allocata da
c1.  Se c1 esce di scopo, la memoria puntata da _pd viene deallocata;
e se anche c2 esce di scopo, si tenta di deallocare nuovamente la
stessa memoria e... boom!  Oppure, se esce di scopo prima c2, c1
contiene un pointer a memoria deallocata: e usando c1... boom!
In casi come questo (operator new nel ctor) bisogna *sempre* definire
tutta la "santissima trinita'": il distruttore (con operator delete),
il copy constructor e l'assignment operator (che ha gli stessi
problemi): ad esempio
 public:
  ...
  c(const c & rhs) throw(bad_alloc) { _pd = new double(*(rhs._pd)); };
  c & operator = (const c & rhs) throw(bad_alloc) {
    if (this != &rhs) _pd = new double(*(rhs._pd));
    return *this;
  };
Attenzione che un copy ctor puo' essere invocato di nascosto; se ad
esempio passi ad una procedura un oggetto della classe c "per valore",
viene creata una copia di quell'oggetto da usare al posto del dummy
argument.
-- 
Maurizio Loreti                      
http://wwwcdf.pd.infn.it/~loreti/mlo.html
Un. of Padova, Dept. of Physics - Padova, Italy          
lor...@padova.infn.it

 non chiama il Costruttore di Copia: pointer(const pointer &)
Certo. Intanto tu stai assegnando un puntatore ad un altro e non credo
sia quello che vuoi. Dovresti scrivere
*pP_Secondo = *pP_Primo;
E poi questa è un'assegnazione non una copia. Il costruttore di copia
viene chiamato quando crei un oggetto e lo inizializzi con un altro
oggetto dello stesso tipo. Devi quindi definire l'operatore di
assegnamento in un modo simile a questo (non sono sicuro dei vari
MAXSTRING, ho adattato al volo il tuo costruttore di copia):
pointer &operator = (const pointer &p) {
  if ( this == &p ) return *this;
  delete [] psStringa;
  psStringa = new char[MAXSTRING];
  bzero(psStringa, MAXSTRING - 1);
  memcpy(psStringa, p.psStringa, MAXSTRING - 2);
  cout <<endl
       <<"Chiamata all'operatore di assegnamento.";
  return *this;
}
EMG

   }
[snip]
Non sono sicuro che questo metodo funzioni sempre e dovunque,
infatti potrebbe accedere (anche se solo in lettura) a zone 
di memoria fuori dal tuo spazio di indirizzamento.
Se infatti S punta a una stringa molto corta, perdipiu' in 
fondo alla tua area dati, la memcpy() potrebbe sforare, leggendo
fino a S + MAXSTRING - 1 che potrebbe andare fuori.
Ad esempio, credo che Digital Unix andrebbe subito in memory fault.
In questi casi, se S e' effettivamente una stringa C, quindi terminata
da \0, conviene usare strncpy(psStringa, S, MAXSTRING - 1) che copia
al piu' MAXSTRING - 1 caratteri ma si ferma al primo '\0' che incontra.
*Fabio* 
-- 
It doesn't matter whether you win or lose -- until you lose.

   };
Scusa, forse sbaglio, ma non si dovrebbe prima deallocare la memoria
allocata in precedenza dal costruttore?
EMG

 It doesn't matter whether you win or lose -- until you lose.
Grazie per avermi corretto !
Ciao, U D A.

 }
Grazie mille !!
Ciao, U D A.

 EMG
Grazie e ciao, U D A.

 U D A.
C'erano 2 cose essenzialmente che non andavano nel tuo programma:
1) non puoi scrivere *oggetto per dire ciò che punta l'oggetto.Hai fatto
confusione fra la nozione di oggetto di una classe e puntatore.Siamo
daccordo che in fine dei conti il tuo oggetto è un puntatore a caratteri MA
NON E' la stessa cosa sintatticamente parlando.Se vuoi accedere in generale
a ciò che punta il campo privato psStringa devi definire una apposita
funzione pubblica (SELETTORE)
2) non avevi ben chiaro la differenza fra assegnazione  e inizializzazione
(ora sono sicuro che ce l'hai perchè ci sono state delle ottime risposte sul
newsgroup...).Riassumendo nella prima agisce l'operatore di assegnazione che
viene passato gratis alla classe e che nel nostro caso non c'è necessità di
ridefinire.(in altri casi si deve farlo per evitare fenomeni di
interferenza).Nella seconda agisce il costruttore di copia (che agisce anche
nei passaggi per valore e nei return delle funzioni).
Ho corretto il tuo programma così potrai chiarire ogni altro eventuale
dubbio.
Ciao
Giorgio
#include <iostream.h>
#include <string.h>
#include <conio.h>
#ifndef COSTR_H
#define COSTR_H
class pointer
{ private: char* psStringa;
  public:
  pointer()     //costruttore senza argomenti
  {if (psStringa)
    { this->~pointer();//chiamata al distruttore
       psStringa=0;
    }
  }
  pointer(char * S)         //costruttore unario
  { psStringa = new char[strlen(S)+1];
     for(int k=0;k<=strlen(S);k++)
     psStringa[k]=S[k];
  }
  pointer(const pointer &p)   //costruttore di copia
  { psStringa = new char[strlen(p.psStringa)+1];
     for(int k=0;k<=strlen(p.psStringa);k++)
     psStringa[k]=p.psStringa[k];
     cout<<"chiamato il costruttore di copia"<<endl;
  }
  ~pointer()   //distruttore
  { delete psStringa;
     psStringa = 0;
   }
  friend ostream& operator << (ostream& os, const pointer &p)
  { return os <<"Valore della Stringa: "<<p.psStringa;}
};
#endif
void main ()
{ clrscr();
  pointer  pP_Primo("Gino");
  pointer  pP_Secondo(pP_Primo); //Qui chiama il costruttore di copia
  pointer  pP_Terzo("Franco");
  cout <<endl<<"pP_Primo ->\t"<<pP_Primo<<endl
       <<"pP_Secondo ->\t"<<pP_Secondo<<endl
       <<"pP_Terzo ->\t"<<pP_Terzo<<endl;
  cout <<"pP_Secondo = pP_Primo"<<endl;
  pP_Secondo = pP_Terzo;                           //qui chiama
l'assegnazione che é passata gratis
                                                                     //e per
questa classe,non é necessario ridefinirla
                                                                     //ora
secondo e terzo valgono "Franco"
  pointer pP_Quarto(pP_Primo);                  //qui chiama il costruttore
di copia
  cout <<endl<<"pP_Primo ->\t"<<pP_Primo<<endl
       <<"pP_Secondo ->\t"<<pP_Secondo<<endl
       <<"pP_Terzo ->\t"<<pP_Terzo<<endl
       <<"pP_Quarto ->\t"<<pP_Quarto<<endl;
}

 giusto (?:-(
No. Il tipo char * è un puntatore come tutti gli altri.
EMG



Giustissimo.  Ecco cosa capita a scrivere senza pensare (e senza provare).
Scusate:
  c & operator = (const c & rhs) throw(bad_alloc) {
    if (this != &rhs) { delete _pd; _pd = new double(*(rhs._pd)); }
    return *this;
  };


 pP_Secondo = pP_Terzo; //qui chiama l'assegnazione che é passata gratis
Ma ha senso una cosa del genere? Stai assegnando un puntatore ad un
altro.
EMG

 
Ho dato solo un'occhiata veloce ma secondo me non si puo'
fare come dici tu (non ridefinire l'assegnamento).
Questo perche' se ho capito bene accade quanto segue:
quando esegui 
    pP_Secondo = pP_Terzo;
con l'operatore = di default viene fatto un assegnamento del
tipo 
    pP_Secondo.psStringa=pP_Terzo.psStringa.
Quando la funzione main termina viene chiamato
sia il distruttore di pP_Secondo che di pP_Terzo.
Supponiamo che per primo sia chiamato il distruttore
di pP_Secondo. Questo esegue una delete che "libera" la zona
di memoria puntata da pP_Secondo.psStringa. Poi assegna 0
a pP_Secondo.psStringa. Ma pP_Terzo.psStringa non viene azzerato e
punta ancora alla locazione di memoria a cui puntava prima che
venisse chiamato il distruttore di pP_Secondo (la locazione
iniziale della zona appena liberata dalla delete del
distruttore di pP_Secondo). A questo punto viene chiamato il
distruttore di pP_Terzo che esegue una delete della zona di
memoria puntata da pP_Terzo.psStringa. Ma questa zona
era gia' stata dellocata dalla delete del distruttore di pP_Secondo.
Inoltre mi sembra che nessuno deallochi la memoria allocata
quando e' stato chiamato il costruttore per pP_Terzo.
Spero di non aver detto troppe scemenze anche perche' ho
dato solo una rapida occhiata al codice.
Se sbaglio qualcosa vi sarei grato se me lo faceste sapere.
Marco Guidi

 EMG
Scusate se rispondo in maniera parziale (sono le 2 di notte e devo andare a
dormire...)
Comunque se volete risposte particolareggiate (cosa che mi vede disponibile)
gradirei che almeno si controlli con un p? pi? di attenzione il codice.
Ancora una volta fate confusione (vedi mia risposta originaria) fra puntatore e
oggetto.
pP_Secondo e pP_Terzo NON SONO PUNTATORI  ma oggetti della classe pointer.
Cio? viene allocato spazio in memoria per i campi privati e per dei puntatori ai
codici delle funzioni pubbliche......
Fare pP_Secondo = pP_Terzo non solo ? corretto ma ? l'unica cosa che si pu? fare.
Scrivere *pP_Secondo ? una tale bestialit? che il mio compilatore quasi mi
sputava addosso...
Giorgio

 > questa classe,non ? necessario ridefinirla

Vado a risponderti (prego vedere che ora mi fai fare grrr.....)
Se fosse come dici tu (cio? che ci fosse condivisione di memoria)
allora se faccio:
pP_Secondo=pP_Terzo;
//supponendo che il campo privato di pP_Terzo valeva "Franco", ora anche
//quello di pP_Secondo vale "Franco"
ora faccio:
pP_Terzo=pP_Primo;
//supponendo che il campo privato di pP_Primo valeva "Gino", ora anche
//quello di pP_Terzo vale "Gino"
Ma quanto vale ora quello di pP_Secondo ?
RISPOSTA: vale ancora "Franco" cio? non c'? interferenza in quanto non c'? condivisione di
memoria.Di conseguenza tutto il tuo ragionamento si annulla.
MORALE: l'assegnazione non deve necessariamente sempre essere ridefinita,in quanto ? passata
gratis per ogni classe.
Buonanotte

 Ciao, U D A.
Questa volta ti rispondo solo dal punto di vista teorico, cio? non ho tempo di
correggerti il listato.
Nel caso che nella classe derivata NON sia definito alcun costruttore di copia,
viene invovato quello della classe base: i campi in pi? sono copiati "membro a
membro".
Nel caso che nella classe derivata sia invece definito,allora viene invocato solo
quest'ultimo
che copier? tutti i campi, quelli derivati e quelli propri, EVENTUALMENTE
invocando il costruttore dela classe base.
PS:Piccole annotazioni stilistiche
1)quando scrivi una classe definisci prima il costruttore senza argomenti e poi
quelli unari ,binari,ternari ecc.ecc
2)se le sezioni private e protected sono vuote non le scrivere proprio.
3)l'inizializzazione di un oggetto della classe si scrive molto semplicemente:
Es:
  pointerD      *pPD_Quarto = new pointerD("*pPD_Terzo"); //NO
  pointerD   pPD_Quarto(pPTerzo);//OK
Ciao

 
[SNIPPATO TUTTO]
Da quanto segue forse si capira' perche' io vieterei per decreto legge ogni
linguaggio di programmazione diverso dall'assembler :-).
Sul fatto che ci sia condivisione dello spazio di memoria non ci possono essere
dubbi visto che il processo che esegue tutto il programma e' unico.
Cio' che accade  e' quanto segue:
    Ho compilato e debuggato il codice da te postato che riporto di seguito
    (Linux Red-Hat release 5.2, Kernel 2.0.36,
     compilatore egcs-2.90.29 980515 (egcs-1.0.3 release),
     debugger GNU gdb 4.17.0.4 ).
file a.h
#include <iostream.h>
#include <string.h>
/* #include <conio.h> */
#ifndef COSTR_H
#define COSTR_H
class pointer
{ private: char* psStringa;
  public:
  pointer()     //costruttore senza argomenti
  {if (psStringa)
    { this->~pointer();//chiamata al distruttore
       psStringa=0;
    }
  }
  pointer(char * S)         //costruttore unario
  { psStringa = new char[strlen(S)+1];
     for(int k=0;k<=strlen(S);k++)
     psStringa[k]=S[k];
  }
  pointer(const pointer &p)   //costruttore di copia
  { psStringa = new char[strlen(p.psStringa)+1];
     for(int k=0;k<=strlen(p.psStringa);k++)
     psStringa[k]=p.psStringa[k];
     cout<<"chiamato il costruttore di copia"<<endl;
  }
  ~pointer()   //distruttore
  { delete psStringa;
     psStringa = 0;
   }
  friend ostream& operator << (ostream& os, const pointer &p)
  { return os <<"Valore della Stringa: "<<p.psStringa;}
};
#endif
file a.cc
#include "a.h"
 void main ()
 {
   pointer  pP_Primo("Gino");
   pointer  pP_Secondo(pP_Primo); //Qui chiama il costruttore di copia
   pointer  pP_Terzo("Franco");
   cout <<endl<<"pP_Primo ->\t"<<pP_Primo<<endl
        <<"pP_Secondo ->\t"<<pP_Secondo<<endl
        <<"pP_Terzo ->\t"<<pP_Terzo<<endl;
   cout <<"pP_Secondo = pP_Primo"<<endl;
   pP_Secondo = pP_Terzo;    //qui chiama l'assegnazione che ? passata gratis
                             //e questa classe,non ? necessario ridefinirla
                             //ora secondo e terzo valgono "Franco"
   pointer pP_Quarto(pP_Primo);  //qui chiama il costruttore di copia
   cout <<endl<<"pP_Primo ->\t"<<pP_Primo<<endl
        <<"pP_Secondo ->\t"<<pP_Secondo<<endl
        <<"pP_Terzo ->\t"<<pP_Terzo<<endl
        <<"pP_Quarto ->\t"<<pP_Quarto<<endl;
}
Il debugger mostra in modo inequivocabile che:
        1.  Dopo che e' stato eseguito il codice del costruttore di pP_Primo,
            pP_Primo.psStringa vale 0x804a090 (non "Gino")! cioe'
            pP_Primo.psStringa e' un puntatore e in quanto tale il suo
            contenuto e' un indirizzo di memoria!! Questo indirizzo e'
            appunto nel mio caso 0x804a090
            che nello spazio di memoria del mio processo
            individua una locazione di memoria che e' la prima delle
            locazioni necessarie per 'contenere' la stringa "Gino". Cioe'
            Nello spazio di memoria del mio processo le locazioni (byte) di
            memoria che partono da 0x804a090 vengono viste cosi'
            0x804a090 'G' (un byte)
            0x804a091 'i' (un byte)
            0x804a092 'n' (un byte)
            0x804a093 'o' (un byte)
            infatti con il debugger posso farmi facilmente stampare il
            contenuto della locazione 0x804a091 con
            p *((char *)0x804a091)
            (p sta per print).
            Invece l'indirizzo di pP_Primo.psStringa e' 0xbffffabc.
            Cioe' in memoria all'indirizzo 0xbffffabc ci sono 4 bytes
            (questa e' la dimensione assegnata dal mio sistema ai puntatori)
            che condengono la rappresentazione del numero 0x804a090. Cioe':
                                 <-- 4 bytes ---->
           locazione 0xbffffabc [numero 0x804a090] -------
                                                          |
                                                          |
                                 1 byte                   |
            locazione 0x804a090   ['G'] <------------------
            locazione 0x804a091   ['i']
            locazione 0x804a092   ['n']
            locazione 0x804a093   ['o']
            Dopo l'esecuzione del codice di pP_Secondo si ha che
            pP_Secondo.psStringa vale 0x804a0a0 e a questo punto la
            situazione in memoria e' la seguente:
                 indirizzo di      contenuto di
             pP_Primo.psStringa   pP_Primo.psStringa
           locazione 0xbffffabc [   numero 0x804a090   ] ----
                                                            |
                                                            |
            0x804a090 'G' (un byte)  <----------------------|
            0x804a091 'i' (un byte) zona di memoria riempita dal costruttore
            0x804a092 'n' (un byte) di pP_Primo
            0x804a093 'o' (un byte)
                 indirizzo di        contenuto di
             pP_Secondo.psStringa   pP_Secondo.psStringa
           locazione 0xbffffab8   [   numero 0x804a0a0  ] -------
                                                                |
                                                                |
            0x804a0a0 'G' (un byte)  <--------------------------
            0x804a0a1 'i' (un byte) zona di memoria riempita dal costruttore
            0x804a0a2 'n' (un byte) di pP_Secondo
            0x804a0a3 'o' (un byte)
            Dopo l'esecuzione del codice del costruttore di
            pP_Terzo si ha la seguente situazione in memoria:
           locazione 0xbffffabc [numero 0x804a090] -------
                                                         |
                                                         |
            0x804a090 'G' (un byte)  <-------------------|
            0x804a091 'i' (un byte) zona di memoria riempita dal costruttore
            0x804a092 'n' (un byte) di pP_Primo
            0x804a093 'o' (un byte)
           locazione 0xbffffab8 [numero 0x804a0a0] -------
                                                         |
                                                         |
            0x804a0a0 'G' (un byte)  <--------------------
            0x804a0a1 'i' (un byte) zona di memoria riempita dal costruttore
            0x804a0a2 'n' (un byte) di pP_Secondo
            0x804a0a3 'o' (un byte)
                 indirizzo di        contenuto di
             pP_Terzo.psStringa   pP_Terzo.psStringa
           locazione 0xbffffab4  [ numero 0x804a0b0 ] ----
                                                         |
                                                         |
            0x804a0b0 'F' (un byte)  <--------------------
            0x804a0b1 'r' (un byte) zona di memoria riempita dal costruttore
            0x804a0b2 'a' (un byte) di pP_Terzo
            0x804a0b3 'n' (un byte)
            0x804a0b4 'c' (un byte)
            0x804a0b4 'o' (un byte)
Dopo l'assegnamento
            pP_Secondo = pP_Terzo
In memoria si ha:
           locazione 0xbffffabc [numero 0x804a090] -------
                                                         |
                                                         |
            0x804a090 'G' (un byte)  <-------------------|
            0x804a091 'i' (un byte) zona di memoria riempita dal costruttore
            0x804a092 'n' (un byte) di pP_Primo
            0x804a093 'o' (un byte)
                 indirizzo di        contenuto di
             pP_Secondo.psStringa   pP_Secondo.psStringa
           locazione 0xbffffab8 [numero 0x804a0b0] -------
                                                         |
                                                         |
            0x804a0a0 'G' (un byte)  <--------------------
            0x804a0a1 'i' (un byte) zona di memoria riempita dal costruttore
            0x804a0a2 'n' (un byte) di pP_Secondo
            0x804a0a3 'o' (un byte)
                 indirizzo di        contenuto di
             pP_Terzo.psStringa   pP_Terzo.psStringa
           locazione 0xbffffab4  [ numero 0x804a0b0 ] ----
                                                         |
                                                         |
            0x804a0b0 'F' (un byte)  <--------------------
            0x804a0b1 'r' (un byte) zona di memoria riempita dal costruttore
            0x804a0b2 'a' (un byte) di pP_Terzo
            0x804a0b3 'n' (un byte)
            0x804a0b4 'c' (un byte)
            0x804a0b4 'o' (un byte)
             NOTA BENE!!!!!!! *L'indirizzo* di pP_Secondo.psStringa
             e' *invariato* rispetto a prima dell'assegnamento!!
             invece il *contenuto* di pP_Secondo.psStringa (ricordare
             che pP_Secondo.psStringa e' un puntatore!!) e' cambiato
             in seguito all'assegnamento.
DOMANDA 1:   Chi ha deallocato la zona di memoria che iniza all'indirizzo
             0x804a090 e che era stata allocata dal costruttore di
             pP_Primo ?
RISPOSTA1:   Nessuno (a meno che l'operatore di assegnamento di
             default non provveda alla deallocazione! A me fu detto
             esplicitamente a suo tempo che tale deallocazione
             *non* viene eseguita dall'operatore di assegnamento di
             default. Se lo standard e' cambiato prego qualcuno di
             informarmi).
CONSEGUENZA1: Rimane una zona di memoria che per il sistema operativo
              risulta allocata ma che non e' piu' accessibile dal programma.
              Se in un programma complesso di queste zone ne vengono allocate
              molte senza mai deallocarle puo' accadere che si finisca la
              memoria (qui "Gino" occupa poca memoria ma in un programma
              complesso potrebbe essere allocata memoria per qualcosa di
              molto piu' grande di "Gino")
Nel mio sistema viene eseguito prima il distruttore di pP_Terzo.
Si ha che viene eseguita la
delete psStringa
con psStringa che si trova all'indirizzo 0xbffffab4 e ha un contenuto che
vale 0x804a0b0 (indirizzo della prima locazione della stringa "Franco")
La delete dealloca la zona di memoria "puntata" da psStringa,
cioe' quella che inizia alla locazione 0x804a0b0
A questo punto viene eseguito il distruttore di pP_Secondo.
Viene eseguita la delete psStringa con psStringa che si trova
all'indirizzo 0xbffffab8 (diverso dall'indirizzo di pP_Terzo.psStringa)
ma che ha contenuto 0x804a0b0
(identico al contenuto di pP_Terzo.psStringa!!!). Ora sul fatto che
due indirizzi uguali corrispondano alla stessa zona di memoria fisica
non c'e' dubbio, poiche' e' sempre lo stesso processo che e' in esecuzione.
Diverso potrebbe essere il caso in cui ci fossero due processi in
esecuzione che potrebbero avere meccanismi diversi di traduzione della
memoria da virtuale a fisica. Ma non e' il nostro caso.
Quindi anche la delete psStringa  invocata dal distruttore di pP_Secondo
va a deallocare la zona di memoria che inizia alla locazione 0x804a0b0.
Questa zona era gia' stata deallocata dalla delete del distruttore
di pP_Terzo. Quindi si tenta di deallocare due volte una stessa zona
di memoria. Il risultato di questa operazione e' di tipo
"unpredictable" e quindi potrebbe anche, casualmente, funzionare.
Cio' non toglie che sia comunque un errore.
Per inciso con il mio sistema in corrispondenza della esecuzione della
delete nel distruttore di pP_Secondo si ottiene un errore del tipo
Segmentation fault (core dumped) e se si va a vedere con il debugger
si vede che proprio in corrispondenza di tale istruzione si ha
Program received signal SIGSEGV, Segmentation fault.
CONSEGUENZA2:
        L' operatore di assegnamento di default non e' adatto per
        essere usato con classi al cui interno siano presenti
        membri di tipo puntatore a qualcosa!!
Spero di avere dettagliato sufficientemente.
Marco Guidi 

linguaggio di programmazione diverso dall'assembler :-).
Solo per questo io ti voterei all'istante.
GIORGIO FOR PRESIDENT!!!!!
:)
Marco

>linguaggio di programmazione diverso dall'assembler :-).
lo avevo scritto io, che pero' mi chiamo Marco.
ciao,
Marco Guidi
 
 

In article <X88ADj$MqVd$@sssup1>, 
gu...@sssup.it
 writes:
Chiedo scusa ma dopo l'assegnamento 
  pP_Secondo=pP_Terzo la situazione in memoria e' ovviamente la seguente:
           locazione 0xbffffabc [numero 0x804a090] -------
                                                         |
                                                         |
            0x804a090 'G' (un byte)  <-------------------|
            0x804a091 'i' (un byte) zona di memoria riempita dal costruttore
            0x804a092 'n' (un byte) di pP_Primo
            0x804a093 'o' (un byte)
                 indirizzo di        contenuto di
             pP_Secondo.psStringa   pP_Secondo.psStringa
           locazione 0xbffffab8 [numero 0x804a0b0] ----->
                                                         |
                                                         |
            0x804a0a0 'G' (un byte)                      |
            0x804a0a1 'i' (un byte) zona di memoria riemp|ita dal costruttore
            0x804a0a2 'n' (un byte) di pP_Secondo        |
            0x804a0a3 'o' (un byte)                      |
                                                         |
                                                         |
                 indirizzo di        contenuto di        |
             pP_Terzo.psStringa   pP_Terzo.psStringa     |
                                                         |
           locazione 0xbffffab4  [ numero 0x804a0b0 ] -->-
                                                         |
                                                         |
            0x804a0b0 'F' (un byte)  <--------------------
            0x804a0b1 'r' (un byte) zona di memoria riempita dal costruttore
            0x804a0b2 'a' (un byte) di pP_Terzo
            0x804a0b3 'n' (un byte)
            0x804a0b4 'c' (un byte)
            0x804a0b4 'o' (un byte)
Scusate per l'imprecisione ma in ogni caso i valori dell'indirizzo
e del contenuto dei vari puntatori dovrebbero essere corretti.
 
 

Non ho capito bene a chi e' diretto  il FOR PRESIDENT visto che
A te, a te, ho fatto un mistake io.
Marco for president (suona pure meglio..-;))
Marco

 sputava addosso...
Se però fossero puntatori (e il prefisso p sembra vagamente
intenderlo... :-) ) questo si può fare benissimo ed infatti il mio DJGPP
compilava perfettamente.
EMG

generare una copia bit-per-bit
Mi preme sottolineare solamente che cio` non e` del tutto esatto.
Prima dello standard la cosa era cosi` (piu` o meno), ora se non erro lo
standard sancisce che il copy ctor di default esegua una
copia membro a membro il che significa che se un certo attributo
e` a sua volta un oggetto con copy ctor esplicitato , la costruzione di
quell'attributo viene eseguita utilizzando il copy ctor fornito dal
programmatore. Ovviamente se nel processo di copia non si trovano copy 
ctor espliciti, alla fine cio` che si ottiene e` una copia bit a bit!!!!
Per quanto riguarda il resto della discussione non mi intrometto
proprio, poiche` dovendo recuperare parecchie centinaia di messaggi
non ho intenzione di buttarmi a capofitto tra le righe di codice postate
in questo thread (salvo eventuali sporadici ripensamenti).
Saluti,
Paolo
-----------------------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
-----------------------------------------------------------------