Overload operatotre =

di il
13 risposte

Overload operatotre =

Ciao, sono nuovo della programmazione in c++.. Ho la seguente classe
#include <iostream>
using namespace std;

class Persona{
      public:
             Persona(char*,char*,char,int);
             ~Persona(){}
             Persona(const Persona&); //costruttore di copia
             Persona& operator= (const Persona&); //operatore di assegnazione
             void Print();
             void setNome(char*);
             void setCognome(char*);
             void setSex(char);
             void setEta(int);
      private:
              char *nome, *cognome, sex;
              int eta;
};
Non capisco come deve essere implementato l'operatore di assegnazione.. inoltre il costruttore di copia l'ho implementato così:
Persona::Persona(const Persona& originale){
     this->nome=originale.nome;
     this->cognome=originale.cognome;                      
     this->sex=originale.sex;
     this->eta=originale.eta;
}
E' corretto?
Grazie in anticipo dell'aiuto

13 Risposte

  • Re: Overload operatotre =

    = è un operatore binario...cioè per fare un operazione utilizzando tale operatore bisogna avere due elementi...

    si può overlodare un operatore in 2 modi:tramite un metodo della classe a cui si riferisce o tramite funzione friend

    nel primo caso avrai:
    
        #include <iostream>
        using namespace std;
    
        class Persona{
              public:
                     Persona(char*,char*,char,int);
                     ~Persona(){}
                     Persona(const Persona&); //costruttore di copia
                     Persona operator=(Persona persona2); //operatore di assegnazione
                     void Print();
                     void setNome(char*);
                     void setCognome(char*);
                     void setSex(char);
                     void setEta(int);
              private:
                      char *nome, *cognome, sex;
                      int eta;
        };
    
    
    dove lo sviluppo sarà:
    
    Persona Persona::operator=(Persona persona2){
         nome=persona2.nome;
         cognome=persona2.cognome;                     
         sex=persona2.sex;
         eta=persona2.eta;
         return *this;
    }
    


    nel secondo caso:
    
        #include <iostream>
        using namespace std;
    
        class Persona{
              public:
                     Persona(char*,char*,char,int);
                     ~Persona(){}
                     Persona(const Persona&); //costruttore di copia
                     friend void operator=(Persona *persona1,Persona persona2); //operatore di assegnazione
                     void Print();
                     void setNome(char*);
                     void setCognome(char*);
                     void setSex(char);
                     void setEta(int);
              private:
                      char *nome, *cognome, sex;
                      int eta;
        };
    
    
    dove lo sviluppo sarà:
    
    void operator=(Persona *persona1,Persona persona2){
         persona1.nome=persona2.nome;
         persona1.cognome=persona2.cognome;                     
         persona1.sex=persona2.sex;
         persona1.eta=persona2.eta;
    }
    
    PS:il costruttore di copia non ti serve a nulla in questo caso...infatti i costruttori di copia sono necessari se effettui allocazione di memoria all'interno di un oggetto. In questo caso, non puoi semplicemente limitarti a copiare il puntatore (come fa il costruttore di copia di default), ma hai davvero bisogno all'atto della copia di allocare una parte della memoria, in modo che i valori associati ai due oggetti possano essere differenti. Quindi i costruttori di copia risolvono il problema della condivisione di memoria indesiderata che si avrebbe all'atto dell'assegnamento di un oggetto con parti dinamiche (cioè con membri puntatori).

    Ad esempio in un programma del genere:
    
    class Vector
    {
      int n;
      float *v;
    public:
      Vector();
      Vector (const Vector &);
    };
    Vector::Vector()
    {
      v = new float[100];
      n = 100;
    }
    Vector::Vector (const Vector &vector)
    {
      n = vector.n;                         // Copia del campo n
      v = new float[100];                   // Crea un nuovo array
      for (int i = 0; i < 100; i++)
        v[i] = vector.v[i];                 // Copia l'array
    }
    
    
  • Re: Overload operatotre =

    Grazie mille! ora mi è chiaro tranne il seguente punto: il prototipo dell'operatore di selezione non deve essere:
    Persona& operator=(const Persona& persona2);
    ?
  • Re: Overload operatotre =

    Nel tuo caso non serve un prototipo scritto in quella maniera poichè mi pare di aver capito che non ti serve allcocare dinamicamente nuova memoria e che non hai calssi che possiedono al loro interno puntatori...vero?

    per saperne di più sul sul caso in cui devi scrivere un protptipo come l'ultimo che hai postato, leggi l'ultima parte di questa pagina:http://www.gaburri.net/didattica/materiale/operatori.html
  • Re: Overload operatotre =

    No in questo esempio non mi serve. Ma cercavo di capire un po' in generale come realizzare il costruttore di copia e l'operatore di assegnazione. Ora dovrebbe essermi più chiaro! Grazie
  • Re: Overload operatotre =

    Ho modificato le classe in questo modo, aggiungendo un puntatore alla classe lavoro:
    #include <iostream>
    using namespace std;
    
    class Lavoro{
          public:
                 Lavoro(char *nome){
                     this->nome=nome;
                 }
                 ~Lavoro(){}
                 void Update(char*);
                 void Print();
                 char *getName();
          private:              
                 char *nome;    
    };
    
    class Persona{
          public:
                 Persona(char*,char*,char,int,char *);
                 ~Persona(){}
                 Persona(const Persona&); //costruttore di copia
                 Persona& operator= (const Persona&); //operatore di assegnazione
                 void Print();
                 void setNome(char*);
                 void setCognome(char*);
                 void setSex(char);
                 void setEta(int);
                 void setLavoro(char*);
          private:
                  char *nome, *cognome, sex;
                  int eta;
                  Lavoro *lavoro;
    };
    
    Il costruttore di copia l'ho implementato in questo modo:
    Persona::Persona(const Persona& originale){
         this->nome=originale.nome;
         this->cognome=originale.cognome;                      
         this->sex=originale.sex;
         this->eta=originale.eta;
         this->lavoro= new Lavoro(originale.lavoro->getName());
    }
    e questo è l'operatore di selezione:
    Persona& Persona::operator=(const Persona& originale){
        this->nome=originale.nome;
        this->cognome=originale.cognome;                      
        this->sex=originale.sex;
        this->eta=originale.eta;
        delete lavoro;
        this->lavoro= new Lavoro(originale.lavoro->getName());
        return *this;
    }
    Sono corretti vero? Perchè creo un nuovo oggetto e quindi i puntatori fanno riferimento ad aree diverse della memoria...
  • Re: Overload operatotre =

    Credo ci sia qualche piccolo errore sintattico e di forma...

    innanzi tutto cominciamo col dire che "this->" è inutile davanti ad ogni stringa di un metodo perchè, tranne i compilatori preistorici, tutti gli altri eseguono il puntamento all'oggetto chiamante in automatico...l'unico caso in cui serve è questo:
    
    Lavoro(char *nome){
                     this->nome=nome;
                 }
    
    perchè altrimenti il compilatore farebbe confusione tra i due "nome"...

    inoltre non capisco perchè nome, cognome e lavoro siano dichiarati come "char *nome"...mi sembra che una successiva immissione di input tipo "cin>>nome;" non funzioni in questo caso...a meno che tu non voglia allocare memoria in qualche metodo particolare e complesso, a mio parere, dovresti inizializzare l'array adibito a contenere il nome (ad esempio così: "char nome[30];")...

    inoltre ti consiglio di scrivere prima i metodi e gli attributi private e poi quelli public quando costruisci la classe...in questo modo puoi anche omettere la parola "private" poichè il calcolatore comprende in automatico che tutti i dati scritti a partire dall'inizio e fino alla parola public sono private...

    detto ciò veniamo al piccolo problema all'interno del costruttore di copie e dell'overload...

    evita questo tipo di scrittura:
    
    lavoro= new Lavoro(originale.lavoro->getName());
    
    è di difficile interpretazione e non so neanche se sia corretta sintatticamente...cerca di preferire questo tipo di scrittura:
    
    lavoro= new Lavoro;
    lavoro=originale->lavoro->getname();
    
    per il resto è tutto ok
  • Re: Overload operatotre =

    ivano_schiuma ha scritto:


    #include <iostream>
    using namespace std;
    
    class Persona{
          public:
                 Persona(char*,char*,char,int);
                 ~Persona(){}
                 Persona(const Persona); //costruttore di copia
                 Persona& operator= (const Persona&); //operatore di assegnazione
                 void Print();
                 void setNome(char*);
                 void setCognome(char*);
                 void setSex(char);
                 void setEta(int);
          private:
                  char *nome, *cognome, sex;
                  int eta;
    };
    
    Persona::Persona(const Persona& originale){
         this->nome=originale.nome;
         this->cognome=originale.cognome;                      
         this->sex=originale.sex;
         this->eta=originale.eta;
    }
    E' corretto?
    Il cosiddetto "copy constructor" è il metodo che viene chiamato quando compare l'operatore '=' tra due oggetti. In pratica scrivere:
    
    Persona P1,P12;
    
    ... 
    
    P2 = P1
    
    ...
    
    è esattamente identico alla chiamata:
    
    P2.P2(P1);
    
    In altre parole il costruttore di copia è già un overloading dell'operatore '='.
    Questo per quanto riguarda le dichiarazioni. Sull'implementazione appoggio Mr. Crow.

    Michele
  • Re: Overload operatotre =

    hilbert ha scritto:


    In altre parole il costruttore di copia è già un overloading dell'operatore '='.
    Si, questo è corretto...entrambi fanno l'overload di "="...ed entrambi hanno ragione d'essere in presenza di oggetti che contengono puntatori a zone di memoria che non vanno condivise...ma vi è una evidente differenza tra il costruttore di copia e l'overload dell'operatore di assegnazione:

    -il costruttore di copia serve semplicemente per creare una copia di un oggetto dal "nulla"(è questo è evidente)
    -l'overload dell'operatore di assegnazione serve invece per rendere un oggetto (che è già istanziato in precedenza) uguale ad un altro...

    perciò, mentere nel costruttore di copie ci si limita a creare una nuova area di memoria dinamica uguale a quella dell'oggetto originale, nell'overload dell'operatore di assegnazione, prima di far questo, bisogna liberare lo spazio di memoria dinamica dell'oggetto che chiama l'operatore...
  • Re: Overload operatotre =

    Ma se è come dici tu, in presenza di entrambi (overload di '=' e copy constructor), a quale viene riferita un'assegnazione del tipo seguente?

    a = b;
  • Re: Overload operatotre =

    -se "a" è stato solo inizializzato in questo modo:
    
    oggetto a;
    oggetto b:
    b.aggiungi_dati();//funzione che crea dinamicamente memoria;il puntatore di tale memoria è membro di b;
    
    è neceassario chiamare il costruttore di copie e per farlo bisogna fare così:
    
    a.oggetto(b);
    
    se invece, oltre all'inizializzazione di "a", il programmatore ha gia allocato dinamicamente della memoria puntata da un puntatore (scusa il gioco di parole) che si trova all'interno dell'oggetto "a" in questo modo:
    
    oggetto a;
    oggetto b:
    b.aggiungi_dati(); //funzione che crea dinamicamente memoria;il puntatore di tale memoria è membro di b;
    a.aggiungi_dati();//funzione che crea dinamicamente memoria;il puntatore di tale memoria è membro di a;
    
    è necessario chiamare l'overload dell'operatore di assegnazione poichè prima di allocare nuova memoria bisogna cancellare quella precedentemente allocata nel momento in cui abbiamo chiamato il metodo "aggiungi_dati" per "a"...lo si fa in questo modo:
    
    a=b;
    
    Se poi mi stai cheiedendo se il compilarotore distingue in automatico l'uguale del costruttore di copie dall'uguale dell'overload dell'operatore di assegnazione, questo non te lo so dire...anch'io vorrei saperlo...
  • Re: Overload operatotre =

    Mr.Crow ha scritto:


    -se "a" è stato solo inizializzato in questo modo:
    
    oggetto a;
    oggetto b:
    b.aggiungi_dati();//funzione che crea dinamicamente memoria;il puntatore di tale memoria è membro di b;
    
    è neceassario chiamare il costruttore di copie e per farlo bisogna fare così:
    
    a.oggetto(b);
    
    Qui ti devo contraddire. Sebbene nulla vieta di chiamare il copy constructor in questo modo, anche l'operatore di assegnazione viene tradotto in una chiamata al copy constructor. Tanto è vero che una tecnica che ho visto utilizzare in passato per impedire la copia diretta di oggetti consiste nel dichiarare il copy constructor vuoto (cioè che non esegue nessuna operazione) come private.
    Provare per credere: dichiara due oggetti di una classe con copy constructor privato, e l'espressione 'a = b' causerà in fase di compilazione l'errore "Impossibile chiamare un membro privato fuori dalla classe"

    Mr.Crow ha scritto:


    ...
    Se poi mi stai cheiedendo se il compilarotore distingue in automatico l'uguale del costruttore di copie dall'uguale dell'overload dell'operatore di assegnazione, questo non te lo so dire...anch'io vorrei saperlo...
    La domanda non era esattamente questa, ma quasi. È certo che il compilatore distingue in automatico quale . La mia domanda era: in che modo?
  • Re: Overload operatotre =

    Mi devo contraddire da solo. Ho fatto un piccolo test, per chiarire questi meccanismi. Ecco il sorgente del programmino di test:
    #include <iostream>
    
    using namespace std;
    
    class pippo
    {
    public:
      pippo(const char *name);
      ~pippo();
      char *GetName() {return data;};
      unsigned long GetSize() {return data ? size : 0;};
      pippo(pippo&);
      pippo& operator=(pippo &src);
    
      char *data;
      unsigned long size;
    };
    
    int main(int argc, char **argv)
    {
      cout<< "alloco a e b" <<endl;
      pippo a("a"),b("b");
      cout<< "a = b" <<endl;
      a = b;
      cout <<"dichiaro c e gli assegno b" <<endl;
      pippo c = b; 
      cout <<"fine" <<endl;
      return 0;
    }
    
    pippo::pippo(const char *name)
    {
    
      for(size=0 ; name[size] ; ++size);
    	
      data = new char[size+1];
      if(data)
        {
          for(int i=0 ; i<size ; ++i)
            data[i] = name[i];
          data[size] = '\0';
        }
      cout <<"Chiamato il costruttore standard dell'oggetto ";
      if(data)
        cout <<data;
      else
        cout <<"non allocato";
       cout <<endl;
    }
    
    pippo::~pippo()
    {
      cout <<"Chiamato il distruttore dell'oggetto ";
      if(data)
        cout<<data;
      else
        cout<<"non allocato";
      cout <<endl;
      if(data)
        delete[] data;
    }
    
    pippo::pippo(pippo& src)
    {
      const char *prefix = "copy of ";
      char *srcdata = src.GetName();
      int ns = src.GetSize();
      data = new char[8+ns+1];
      if(data)
        {
          int i;
          for(i=0 ; prefix[i] ; ++i)
            data[i] = prefix[i];
          for( ; *srcdata ; srcdata++,i++)
            data[i] = *srcdata;
          data[i] = '\0';
        }
      cout <<"Chiamato il copy constructor dell'oggetto ";
      if(data)
        cout <<data;
      else
        cout <<"non allocato";
      cout <<endl;
    }
    
    pippo& pippo::operator=(pippo &src)
    {
      const char *prefix = "copy of ";
      cout <<"sovrascrivo l'ggetto " <<data <<" con l'oggetto " <<src.data <<endl;
    
      delete[] data;
    
      char *srcdata = src.GetName();
      int ns = src.GetSize();
      data = new char[8+ns+1];
      if(data)
        {
          int i;
          for(i=0 ; prefix[i] ; ++i)
            data[i] = prefix[i];
          for( ; *srcdata ; srcdata++,i++)
            data[i] = *srcdata;
          data[i] = '\0';
        }
    }
    
    Ed ecco l'output che stampa a video:
    alloco a e b
    Chiamato il costruttore standard dell'oggetto a
    Chiamato il costruttore standard dell'oggetto b
    a = b
    sovrascrivo l'ggetto a con l'oggetto b
    dichiaro c e gli assegno b
    Chiamato il copy constructor dell'oggetto copy of b
    fine
    Chiamato il distruttore dell'oggetto copy of b
    Chiamato il distruttore dell'oggetto b
    Chiamato il distruttore dell'oggetto copy of b
    
    Concludendo, il copy constructor viene chiamato quando un oggetto viene inizializzato in fase di definizione, mentre l'overloading dell'operatore di assegnazione viene chiamato nelle assegnazioni successive alla dichiarazione dell'oggetto, ovvero quando gli eventuali blocchi di memoria dinamica che utilizza al suo interno sono già stati assegnati.

    Michele
  • Re: Overload operatotre =

    hilbert ha scritto:


    Concludendo, il copy constructor viene chiamato quando un oggetto viene inizializzato in fase di definizione, mentre l'overloading dell'operatore di assegnazione viene chiamato nelle assegnazioni successive alla dichiarazione dell'oggetto, ovvero quando gli eventuali blocchi di memoria dinamica che utilizza al suo interno sono già stati assegnati.
    In pratica ciò che ti avevo detto
Devi accedere o registrarti per scrivere nel forum
13 risposte