2009-04-23 17 views
11

Ho scoperto di recente che quando ho dei puntatori all'interno di una classe, devo specificare un costruttore di copia.Copia costruttore con puntatori

Per apprendere, ho realizzato il seguente codice semplice. Si compila, ma mi dà errore di runtime quando si esegue il costruttore di copie.

Sto tentando di copiare solo il valore dal puntatore dell'oggetto copiato, ma evitando di assegnare lo stesso indirizzo.

Quindi, cosa c'è che non va qui?

class TRY{ 
     public: 
     TRY(); 
    ~TRY(); 
     TRY(TRY const &); 

     int *pointer; 

     void setPointer(int); 
    }; 


    void TRY::setPointer(int a){ 
     *pointer = a; 

     return; 
    } 


    TRY::TRY(){} 


    TRY::~TRY(){} 


    TRY::TRY(TRY const & copyTRY){ 
     int a = *copyTRY.pointer; 
     *pointer = a; 
    } 



    int main(){ 

     TRY a; 
     a.setPointer(5); 

     TRY b = a; 

     b.setPointer(8); 

     cout << "Address of object a = " << &a << endl; 
     cout << "Address of object b = " << &b << endl; 

     cout << "Address of a.pointer = " << a.pointer << endl; 
     cout << "Address of b.pointer = " << b.pointer << endl; 

     cout << "Value in a.pointer = " << *a.pointer << endl; 
     cout << "Value in b.pointer = " << *b.pointer << endl; 

     return 0; 
    } 

sarò con questo concetto per le altre classi con un sacco di puntatori in esso, in cui ho bisogno di copiare tutti i valori da sul oggetto all'altro. La copia è inizialmente necessaria per questo codice, quindi mi piacerebbe mantenere la possibilità di copia (non nasconderò il costruttore di copie come privato).

Inoltre, la vera classe che devo implementare ha come 10 puntatori e potrebbe cambiare nel tempo. Non c'è un modo un po 'più intelligente di avere un costruttore di copia profonda in C++? ...

risposta

12

Con la dichiarazione int* pointer che avete appena definito un puntatore, ma non ha assegnato alcuna memoria. Innanzitutto dovresti puntare a una locazione di memoria appropriata allocando una memoria come questa: int* pointer = new int. Quindi, nel costruttore di copie, è necessario allocare la memoria per l'oggetto copiato. Inoltre, non dimenticare di rilasciare la memoria usando delete nel distruttore.

Spero che questo esempio aiuta:

class B 
{ 

public: 
    B(); 
    B(const B& b); 
    ~B(); 
    void setVal(int val); 

private: 
    int* m_p; 
}; 

B::B() 
{ 
    //Allocate the memory to hold an int 
    m_p = new int; 

    *m_p = 0; 
} 

B::B(const B& b) 
{ 
    //Allocate the memory first 
    m_p = new int; 

    //Then copy the value from the passed object 
    *m_p = *b.m_p; 
} 

B::~B() 
{ 

    //Release the memory allocated 
    delete m_p; 
    m_p = NULL; 
} 

void B::setVal(int val) 
{ 
    *m_p = val; 
} 
+3

Non dimenticare di eliminare m_p prima di assegnare un nuovo oggetto per esso. – xtofl

+0

Non ho definito l'operatore di assegnazione, è solo un costruttore di copie. Quindi non è necessario eliminare m_p. – Naveen

+3

In un costruttore, m_p sarà inizialmente indefinito a meno che non lo si inserisca esplicitamente nell'elenco di inizializzazione. Se si tenta di eliminare un puntatore non definito, accadranno cose brutte. –

1

se si ha un puntatore a un tipo regolare, allora

A::A(const A& a): 
    pointer_(new int(*a.pointer_)) 
{ 
} 

se ha un puntatore a una certa classe di base poi

A::A(const &a): 
    pointer_(a.pointer_->clone()) 
{ 
} 

Clone è un implementazione di un prototype pattern

non dimenticare di eliminare il puntatore nel distruttore

A::~A() 
{ 
    delete pointer_; 
} 

per risolvere il tuo esempio

TRY::TRY(TRY const & copyTRY){ 
    int a = *copyTRY.pointer; 
    pointer = new int(a); 
} 
3

Se si vuole fare una copia profonda, è ovviamente necessario anche allocare nuova memoria per contenere i valori. Se l'originale ha un puntatore a un int e non si desidera che la copia utilizzi lo stesso valore del puntatore, è necessario allocare nuova memoria per contenere un int e quindi copiare il valore lì.

L'esempio non è molto chiaro, non mostra l'implementazione del costruttore di copie o il modo in cui il membro pointer viene inizializzato.

1

Il tuo problema è in questa linea proprio qui:

*pointer = a; 

tutte le cose che normalmente accade nel costruttore di default non è ancora successo, compresa la dotazione di memoria per *pointer.

La correzione è di allocare memoria per un intero.Puoi usare malloc e gli amici o new per questo, ma assicurati che sia lo stesso metodo che usi nel costruttore predefinito, perché ottieni solo un distruttore e le chiamate devono corrispondere.

1

Se una copia di membro (superficiale) va bene, non è necessario fare nulla. Se si desidera una copia profonda, è necessario allocare una nuova memoria per le copie di tutti i membri.

0

Quando si scrive un Copy Constructor, è necessario allocare memoria per tutti i membri. Nel tuo caso:

TRY::TRY(TRY const & copyTRY){ 
    pointer = new int(*(copyTry.pointer)); 
} 

operatore = è in qualche modo simile, ma senza allocazione di memoria.

TRY& operator=(TRY const& otherTRY){ 
     this->a = *(otherTry.pointer) 
     return *this 
} 
8

Di recente ho scoperto che quando ho ho puntatori all'interno di una classe, ho bisogno specificare un costruttore di copia.

Non è completamente vero. Quando hai punti nella tua classe e assegni la memoria usando new allora devi preoccuparti del costruttore di copie. Inoltre, non dimenticare l'operatore di assegnazione e il distruttore. È necessario cancellare la memoria allocata utilizzando delete.

Si chiama Law Of The Big Three.

Esempio:

~Matrix(); //Destructor 
    Matrix(const Matrix& m); //Copy constructor 
    Matrix& operator= (const Matrix& m); //Assignment operator 
1

Di recente ho scoperto che quando ho ho puntatori all'interno di una classe, ho bisogno specificare un costruttore di copia

Più spesso di quanto non si tratta di un buona idea semplicemente disabilitarlo dichiarandolo (e l'operatore di assegment) privato e non implementandolo.

+0

Perché? Copiare qualcosa di concettualmente "proibito" nella programmazione, o è perché la copia può portare a problemi nei linguaggi OO? – Biga

+0

Non è "vietato" - spesso non ha senso e/o è costoso. –

0

Molto spesso, se è necessario scrivere un costruttore di copia o un operatore di assegnazione, si sta facendo qualcosa di sbagliato. Lasciare i costruttori di copia e gli operatori di assegnazione agli implementatori della libreria standard. Componi le tue classi di elementi già copiabili e assegnabili e non dovrai scriverne di tuoi.

Ad esempio, forse il membro int * dovrebbe essere invece un vettore std ::.

Se non è possibile rendere la copia/assegnabile predefinita della classe, è possibile renderla non copiabile/assegnabile dichiarando, ma non implementando, un costruttore di copia privata e un operatore di assegnazione.

Solo se nessuno dei precedenti è fattibile, è necessario implementare il proprio costruttore di copia o l'operatore di assegnazione.