PROBLEMA CON IL COSTRUTTORE DI COPIA

di il
10 risposte

PROBLEMA CON IL COSTRUTTORE DI COPIA

Salve a tutti
Quando una funzione restituisce un oggetto (una classe) per inizializzare un' altro oggetto (un' altra classe), dovrebbe entrare in azione il costruttore di copia, ma ciò non accade. Ecco codice del programma in questione (preso dallo Schildt):

#include <conio.h>
#include <iostream>

using namespace std;

class myclass{
public:
       myclass() { cout << "Costruttore normale.\n"; }
       myclass( const myclass &obj ) { cout << "Costruttore di copia.\n"; }
       ~myclass(){ cout << "Distruttore.\n"; }
};

myclass f()
{
        myclass ob;
        return ob;
}

int main()
{
    myclass a; //chiamata al costruttore normale
    
    a = f();//chiamata al costruttore di copia
    
    getch();
    return 0;
}
L' output che visualizzo è il seguente:

Costruttore normale.
Costruttore normale.
Distruttore.
Distruttore.

e da questo è chiaro che il costruttore di copia non viene chiamato; perchè? Come compilatore uso il devC++.

10 Risposte

  • Re: PROBLEMA CON IL COSTRUTTORE DI COPIA

    In certi casi il compilatore può benissimo ottimizzare la chiamata usando la Return Value Optimization ( RVO), costruendo l'oggetto restituito direttamente sul risultato. E' una tecnica introdotta da vari anni appunto per evitare il più possibile copie inutili di un oggetto.
    In ogni caso è il compilatore a decidere se e quando adottare tale tecnica, per cui il costruttore di copia (in teoria) andrebbe sempre esplicitato.
    Se invece passi per copia l'oggetto alla funzione vedrai la chiamata a cctor.
  • Re: PROBLEMA CON IL COSTRUTTORE DI COPIA

    Mi è chiaro quanto hai detto!!!...Però, partendo da un esempio appositamente sbagliato dello Schildt, ho provato io a correggerlo, realizzando un costruttore; posto il codice:
    
    #include <conio.h>
    #include <iostream>
    #include <cstring>
    #include <cstdlib>
    
    using namespace std;
    
    class sample
    {
          char *s;
    public:
          sample() { s = '\0'; }
          sample( const sample &obj );//costruttore di copia
          ~sample() { if(s) delete [] s; cout << "Rilascio di s\n"; }
          void show() { cout <<"Funzione show() " << s << '\n'; }
          void set(char *str);
    };
    
    sample::sample( const sample &obj )
    {
         s = new char[80];
         if(!s)
         {
               cout << "Impossibile allocare\n";
               exit(1);
         }
         cout << "Costruttore di copia.\n";
         
         strcpy(s, obj.s);
    }     
         
    void sample::set(char *str)
    {
         s = new char[strlen(str) + 1];
         if(!s)
         {
               cout << "Impossibile allocare\n";
               exit(1);
         }
         
         strcpy(s, str);
    }
    
    //Restituzione di un oggeto del tipo sample
    sample input()
    {
           char instr[80];
           sample str;
           
           cout << "Inserire una stringa:\n";
           cin >> instr;
           
           str.set(instr);
           
           return str;
    }
    
    int main()
    {
        sample ob;
        
        ob = input();
        ob.show();
        
        getch();
        return 0;
    }
    Se lo mando in esecuzione ho il seguente output:

    Inserire una stringa:
    Ciao
    Rilascio di s
    Funzione show 0?'
    Rilascio di s

    Dopo la scritta "Funzione show" dovrebbe esserci scritto "Ciao" invece di "0?'"; questo mi fa capire che il costruttore di copia non è stato chiamato e quindi quando viene liberata l' area di memoria puntata da s dell' oggetto str, il puntatore s di ob punta a chissà quale locazione che contiene i caratteri "0?'". Perchè questa volta il costruttore di copia non viene chiamato? Ho sbagliato a implementarlo?

    Inoltre, la prima scritta "Rilascio di s" si riferisce al distruttore invocato quando l' oggetto str (della funzione input() ) va fuori scopo; però subito dopo dovrebbe apparire un' altra scritta "Rilascio di s", che invece non c' è, dovuta alla distruzione dell' oggetto temporaneo invisibile che contiene il valore restituito. Perchè non appare questa scritta?
  • Re: PROBLEMA CON IL COSTRUTTORE DI COPIA

    Attenzione: quando fai
    
        sample ob;
        ob = input();
    
    non stai invocando il cctor, ma l'operatore di assegnamento.
    Per invocare il cctor devi fare:
    
        sample ob = input();
    
  • Re: PROBLEMA CON IL COSTRUTTORE DI COPIA

    Grazie 1000 Shodan!!! Ora funziona alla grande!!! ma Il cctor comunque non viene chiamato, sarà per il motivo che hai detto prima ( "In certi casi il compilatore può benissimo ottimizzare la chiamata usando la Return Value Optimization ( RVO)" ) ... a questo punto credo ci sia un errore proprio sul testo, perchè nel primo codice che ho scritto (preso dallo Schildt) il costruttore viene invocato in questo modo:
    myclass a; //chiamata al costruttore normale
        
        a = f();//chiamata al costruttore di copia
  • Re: PROBLEMA CON IL COSTRUTTORE DI COPIA

    Lo Schildt gode di pessima fama tra i divulgatori C++ e il codice che hai mostrato dimostra il perché.
    Tra l'altro ho l'impressione che sia piuttosto vecchiotto come codice (pre 98) dato che mostra l'uso di idiomi sorpassati.
    Uno per tutti:
    
         s = new char[80];
         if(!s)
         {
               cout << "Impossibile allocare\n";
               exit(1);
         }
    
    Nello standard attuale l'operatore new lancia un'eccezione se non riesce ad allocare memoria, pertanto l'if è inutile.
    Se accetti un paio di consigli, lascia perdere Schildt e inizia a leggere il libro di Buce Eckel: Thinking in C++ http://mindview.net/Books/TICPP/ThinkingInCPP2e.htm che l'autore rende diponibile gratuitamente nel suo sito; se non sai l'inglese (praticamente obbligatorio nei testi tecnici), considera l'acquisto del libro di Stroustrup "Il linguaggio C++" di cui tra poco esce la nuova edizione con le novità del C++11.
    Infine cambia compilatore. DevC++ è una schifezza acclamata da anni di abbandono.
    Il minimo sindacale è Codeblocks di questi tempi. Se stai programmando su Windows una scelta più seria è Visual Studio Express (gratuito) versione 2010 o meglio 2012 (con il quale il codice che ti ho corretto richiama il cctor correttamente).
  • Re: PROBLEMA CON IL COSTRUTTORE DI COPIA

    Ok, ho trovato l' eBook di Bruce Eckel e mi sono installato visual studio express 2010 che il mio pc e sistema operativo riescono meglio a digerire rispetto al 2012. Ora il cctor va a meraviglia.
    Scusami, vorrei esser sicuro di aver afferrato bene una tua frase precedente; quando hai detto che
    in certi casi il compilatore può benissimo ottimizzare la chiamata usando la Return Value Optimization ( RVO), costruendo l'oggetto restituito direttamente sul risultato.
    intendevi dire che quando la funzione ritorna l' oggetto ob, non viene creata una copia temporanea di tale oggetto, ma viene direttamente copiato l' oggetto ob in a?
  • Re: PROBLEMA CON IL COSTRUTTORE DI COPIA

    Concettualmente, nei casi in cui viene applicata la RVO, il compilatore può trasformare (quando gli fa comodo):
    
    myclass a = f();
    
    in:
    
    myclass a;
    f(a);
    
    e da
    
    myclass f();
    
    a
    
    f(myclass&)
    
    Ma come ho detto, il programmatore non ha nessun controllo su questo; pertanto se la classe deve maneggiare puntatori raw (quelli classici per intendersi) è obbligatorio sia il cctor sia l'operatore di assegnamento.
  • Re: PROBLEMA CON IL COSTRUTTORE DI COPIA

    Ok, chiaro...un' ultimo dubbio che mi è rimasto: quando mando in esecuzione il programma che mi hai corretto (te lo riscrivo con una piccola modifica ininfluente, ho solamente aggiunto
    cout << "Costruttore normale\n";
    nel costruttore normale):
    #include <conio.h>
    #include <iostream>
    #include <cstring>
    #include <cstdlib>
    
    using namespace std;
    
    class sample
    {
          char *s;
    public:
          sample() { cout << "Costruttore normale\n"; s = '\0'; }
          sample( const sample &obj );//costruttore di copia
          ~sample() { if(s) delete [] s; cout << "Rilascio di s\n"; }
          void show() { cout <<"Funzione show() " << s << '\n'; }
          void set(char *str);
    };
    
    sample::sample( const sample &obj )
    {
         s = new char[80];
         if(!s)
         {
               cout << "Impossibile allocare\n";
               exit(1);
         }
         cout << "Costruttore di copia.\n";
         
         strcpy(s, obj.s);
    }     
         
    void sample::set(char *str)
    {
         s = new char[strlen(str) + 1];
         if(!s)
         {
               cout << "Impossibile allocare\n";
               exit(1);
         }
         
         strcpy(s, str);
    }
    
    //Restituzione di un oggeto del tipo sample
    sample input()
    {
           char instr[80];
           sample str;
           
           cout << "Inserire una stringa:\n";
           cin >> instr;
           
           str.set(instr);
           
           return str;
    }
    
    int main()
    {
        sample ob = input();
        ob.show();
        
        getch();
        return 0;
    }
    ho il seguente output:

    Costruttore normale
    Inserire una stringa:
    Ciao
    Costruttore di copia.
    Rilascio di s
    Funzione show() Ciao
    Rilascio di s

    Premettendo che presumo che l' ultima riga (penso sia solo una) sia "Rilascio di s", in quanto il termine dell' esecuzione del programma mi chiude la finestra dell' output, il primo "Rilascio di s" si riferisce alla chiamata del distruttore per l' oggetto str che, terminata la funzione input(), va fuori scopo?
    Il secondo "Rilascio di s" si riferisce alla chiamata del distruttore che distrugge ob?
    La copia che viene generata dal cctor (sia nell' esempio, che in generale), chiama prima o poi il distruttore? Se si, quando?


    Mi sapresti dire se esiste qualche "trucco" con Visual Studio Express per poter "bloccare" la schermata di output quando il programma termina, in modo da poter leggere anche l' ultima (o le ultime) righe?
  • Re: PROBLEMA CON IL COSTRUTTORE DI COPIA

    Forse così
    int main()
    {
        {
           sample ob = input();
           ob.show();
       
           getch();
       }
        return 0;
    }
    Scusate l'intromissione
  • Re: PROBLEMA CON IL COSTRUTTORE DI COPIA

    Si a tutto. Del resto basta contare costruzioni e distruzioni e constatare che non c'è nessun crash del programma.
Devi accedere o registrarti per scrivere nel forum
10 risposte