Newsgroup: it.comp.lang.c++
Titolo: Costruttore di copia e ridefinizione dell'operatore = nella classe "pila"

Siano p1 e p2 oggetti della classe Pila, costituita da elementi di tipo 
struct contenenti puntatore all'elemento successivo, e allocati 
dinamicamente. Per ridefinire l'operatore = posso richiamare il 
costruttore di copia della classe, o devo riscrivere tutto?

 ha scritto nel messaggio 
news:mlsdi.11038$y05....@tornado.fastwebnet.it...

Questo esempio funziona in tre compilatori che ho provato ma francamente non 
ho idea se sia corretto dal punto di vista dello standard e addirittura se sia 
quello che volevi:
#include <iostream>
class A
{
    public:
        A() {}
        A( const A & )
        {
            std::cout << "copy constructor\n";
        }
        A &operator = ( const A & )
        {
            std::cout << "operator = and ";
            A::A( *this );
            return *this;
        }
};
int main()
{
    A a;
    A b( a );
    A c;
    c = b;
    return 0;
}
EMG
--
EMAG Software Homepage:     
http://www.emagsoftware.it
The EMG's ZX-Spectrum Page: 
http://www.emagsoftware.it/spectrum
The Best of Spectrum Games: 
http://www.emagsoftware.it/tbosg
The EMG Music page:         
http://www.emagsoftware.it/emgmusic
 

 quello che volevi:
No, non funziona:
#include <iostream>
class A {
    public:
        A(int v) : val(v) {}
        A(const A & a) : val(a.val) {
            std::cout << "copy constructor\n";
        }
        A &operator = ( const A & ) {
            std::cout << "operator = and ";
            A::A( *this );
            return *this;
        }
        int val;
};
int main() {
    A a(2);
    A b( a );
    A c(3);
    c = b;
    std::cout << "a: " << a.val << "\nb: " <<
            b.val << "\nc: " << c.val << '\n';
}
Ciao,
Nicola


Dipende da cosa intendi per "richiamare il costruttore di copia" e
"riscrivere tutto".
Ciao,
Nicola

Nicola Musatti ha scritto:

Immaginiamo che io abbia istanziato un oggetto della classe Pila, che 
chiameremo p1, e che poi io voglia crearne una copia p2 attraverso 
l'operatore =, cioč p2=p1. Dato che gli elementi della pila contengono 
puntatori a dati allocati dinamicamente, il costruttore di copia di 
default č inadatto, poichč i puntatori dell'oggetto copiato punterebbero 
alle stesse aree puntate dai puntatori dell'oggetto di origine; quindi 
mi tocca scrivere un costruttore di copia ad hoc. Una volta scritto 
questo costruttore di copia, come effettuo la ridefinizione 
dell'operatore = ?

 ridefinizione dell'operatore = ?
opretor=(A &a)
{
 A tmp(a);
 (*this) = tmp
};

 };
Questa e' una ricorsione infinita.
Ciao,
Nicola

 dell'operatore = ?
Dipende dall'effettiva struttura della tua classe e dal grado di
sicurezza rispetto alle eccezioni che intendi darle. Con una cosa come
la seguente tendenzialmente non sbagli:
Pila & Pila::operator= (Pila const & rhs) {
  Pila tmp(rhs);
  swap(tmp);
  return *this;
}
Dove
void Pila::swap(Pila & rhs) {
  // scambio uno per uno i membri dati di *this e di rhs
}
Ciao,
Nicola

 ha scritto:

mi sono scordato swap(tmp); ero molto di fretta :)
Ciao

 No, non funziona:
Hai ragione. La soluzione più semplice allora è fattorizzare.


 No, non funziona:
Va bene cosě?
#include <iostream>
class A
{
    public:
        A( int v ): val( v ) {}
        A( const A &a )
        {
            copia( a );
        }
        A &operator = ( const A &a )
        {
            copia( a );
            return *this;
        }
        int val;
    private:
        void copia( const A &a )
        {
            val = a.val;
        }
};
int main()
{
    A a( 2 );
    A b( a );
    A c( 3 );
    c = b;
    std::cout << "a: " << a.val << '\n';
    std::cout << "b: " << b.val << '\n';
    std::cout << "c: " << c.val << '\n';
    return 0;
}
-- 


 }
Funziona in questo caso, ma in generale con questo approccio e'
necessario verificare nell'operatore di assegnamento che non si stia
assegnando una variabile su se stessa.
Tendenzialmente preferisco scrivere esplicitamente costruttori e
assegnamento. L'eventuale vantaggio di fattorizzazione cerco di
ottenerlo assicurandomi di scrivere le classi che uso come membri dati
in modo che le classi che li contengano funzionino correttamente con
la versione di default di costruttori e assegnamento.
In un altro post ho descritto un approccio alla scrittura
dell'operatore di assegnamento che garantisce di comportarsi
ragionevolmente in caso di eccezioni.
Ciao,
Nicola

 assegnando una variabile su se stessa.
Giusto, grazie.


 ha scritto:

Sinceramente io non sono sicuro ne' di aver capito la domanda
ne' di aver capito la risposta che dai tu.
Rileggendo il thread iniziale ho provato a fare una implementazione
della classe Pila (implementazione che con termine tecnico definirei "alquanto
bislacca", ma e' quello che posso capire dal post iniziale) e ho
provato a fornire una implementazione (senza testare molto). Nella tua
soluzione sinceramente non ho capito bene come dovrebbe essere fatta la
funzione swap. Secondo me per evitare che il programma vada in crash 
all'esecuzione del distruttore e, contemporaneamente, gestire correttamente
la deallocazione della memoria, e' necessario scrivere l'operatore= in
modo tale che deallochi cio' che era allocato "all'interno" (si fa per
dire) di p1 e riallochi la memoria necessaria
La mia implementazione (non sufficientemente testata: possibili memory leak
e altre amenita' del genere) e' la seguente:
#include <iostream>
class Pila;
typedef struct nodo_di_lista
{
  int value;
  struct nodo_di_lista *next;
} ListaDiStrutture;
class Pila
{
  ListaDiStrutture lista; 
public:
  Pila(int n, int val);
  ~Pila();
  Pila &operator=(Pila const &rhs);
//  void swap(Pila & rhs) ;
  void Print();
};
void Pila::Print()
{
  std::cout<< lista.value << " " << std::flush;
  ListaDiStrutture *actual_node=lista.next;
  while(actual_node != 0) 
  {
    std::cout << actual_node->value << " " <<std::flush;
    actual_node=actual_node->next;
  }
  std::cout << std::endl;
}
Pila::Pila(int n, int val) 
{
  ListaDiStrutture *actual_node=lista.next;
  ListaDiStrutture *index=&lista;
  lista.value=val;
  for(int i=0; i < n-1; i++) 
  {
    actual_node=new ListaDiStrutture;
    actual_node->value=val+i+1;
    actual_node->next=0; 
    index->next=actual_node;
    index=index->next;
 
  }
}
Pila::~Pila()
{
  ListaDiStrutture *actual_node=lista.next;
  while (actual_node != 0) 
  {
    ListaDiStrutture *tmp=actual_node->next;
    delete actual_node;
    actual_node=tmp;
  }
}
Pila & Pila::operator= (Pila const & rhs) 
{
 // Pila tmp(rhs);
 // swap(tmp);
  int temp=lista.value;
  ListaDiStrutture *actual_node=lista.next;
  while (actual_node != 0) 
  {
    ListaDiStrutture *tmp=actual_node->next;
    delete actual_node;
    actual_node=tmp;
  }
  lista=rhs.lista;
  ListaDiStrutture *index;
  index=rhs.lista.next;
  lista.value=rhs.lista.value;
  actual_node=&lista;
  while(index != 0)
  {
    actual_node->next=new ListaDiStrutture;
    actual_node=actual_node->next;
    actual_node->value=index->value;
    actual_node->next=0;
    index=index->next;
  }
 
  return *this;
}
/*
void Pila::swap(Pila & rhs) 
{
  // scambio uno per uno i membri dati di *this e di rhs
}
*/
int main()
{
  Pila p1(5,4);
  Pila p2(3,80);
  std::cout << "--p1 prima----" <<std::endl;
  p1.Print();
  std::cout << "--p2 prima ---" <<std::endl;
  p2.Print();
  std::cout << "--------" <<std::endl;
 
  p1=p2;
  std::cout << "--p1 dopo ----" <<std::endl;
  p1.Print();
  std::cout << "--p2 dopo---" <<std::endl;
  p2.Print();
  std::cout << "-----------" <<std::endl;
 
}
Facendo girare il programma si ottiene:
$ Pila
--p1 prima----
4 5 6 7 8
--p2 prima ---
80 81 82
--------
--p1 dopo ----
80 81 82
--p2 dopo---
80 81 82
-----------
Ciao,
Marco 

 ne' di aver capito la risposta che dai tu.
La domanda non era affatto chiara e con la mia prima risposta speravo
di ottenere che l'OP fornisse una spiegazione migliore. Sulla tecnica
di implementazione dell'operatore di assegnamento che ho descritto
puoi leggere questo:
http://gotw.ca/gotw/059.htm
Ciao,
Nicola