2009-04-23 15 views
11

Ich habe kürzlich entdeckt, dass, wenn ich Zeiger innerhalb einer Klasse habe, ich einen Copy-Konstruktor angeben muss.Kopieren Konstruktor mit Zeigern

Um das zu lernen, habe ich den folgenden einfachen Code gemacht. Es kompiliert, aber gibt mir Laufzeitfehler beim Ausführen des Kopierkonstruktors.

Ich versuche, nur den Wert aus dem Zeiger des kopierten Objekts zu kopieren, aber die gleiche Adresse zu vermeiden.

Also, was ist hier falsch?

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; 
    } 

Ich werde dieses Konzept für andere Klassen mit vielen Zeiger in es verwenden, wo ich auf Objekt zum anderen alle Werte kopieren müssen. Das Kopieren ist für diesen Code zunächst notwendig, daher möchte ich die Kopiermöglichkeit behalten (ich werde den Kopierkonstruktor nicht als privat verstecken).

Außerdem muss die reale Klasse, die ich implementieren muss, 10 Zeiger haben, und es könnte sich mit der Zeit ändern. Ist das nicht eine etwas intelligentere Weise einen tiefen Copykonstruktor in C++ haben? ...

Antwort

12

Mit der Anweisung int* pointer Sie nur einen Zeiger definiert haben aber alle keinen Speicher zugewiesen hat. Zuerst sollten Sie auf einen richtigen Speicherort zeigen, indem Sie Speicher wie folgt zuweisen: int* pointer = new int. Dann müssen Sie im Kopierkonstruktor erneut den Speicher für das kopierte Objekt reservieren. Vergessen Sie auch nicht, den Speicher mit dem Löschbefehl im Destruktor freizugeben.

Ich hoffe, dass dieses Beispiel hilft:

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

Vergessen Sie nicht, m_p zu löschen, bevor Sie ein neues Objekt zuweisen. – xtofl

+0

Ich habe den Zuweisungsoperator nicht definiert, es ist nur ein Kopierkonstruktor. Daher muss m_p nicht gelöscht werden. – Naveen

+3

In einem Konstruktor wird m_p anfangs undefiniert sein, es sei denn, Sie fügen es explizit in die Initialisierungsliste ein. Wenn Sie versuchen, einen nicht definierten Zeiger zu löschen, werden schlimme Dinge passieren. –

1

wenn es einen Zeiger auf eine regelmäßige Art hat dann

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

wenn es einen Zeiger auf einige Basisklassen dann

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

Clone ist eine Implementierung eines prototype pattern

vergessen Sie nicht, den Zeiger in der destructor

zu löschen
A::~A() 
{ 
    delete pointer_; 
} 

zu beheben Ihr Beispiel

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

Wenn Sie eine tiefe Kopie tun möchten, können Sie natürlich auch neue Speicher, um die Werte zu halten zuweisen müssen. Wenn das Original einen Zeiger auf ein int hat und Sie nicht möchten, dass die Kopie denselben Zeigerwert verwendet, müssen Sie neuen Speicher zuweisen, um ein int zu enthalten, und dann den Wert dort kopieren.

Ihr Beispiel ist nicht sehr klar, es zeigt nicht die Implementierung Ihres Kopierkonstruktors oder wie das pointer Mitglied initialisiert wird.

1

Ihr Problem ist hier in dieser Zeile:

*pointer = a; 

All die Dinge, die normalerweise in Ihrem Standard-Konstruktor passiert ist noch nicht geschehen, einschließlich der Zuweisung von Speicher für *pointer.

Die Lösung besteht darin, Speicher für eine ganze Zahl zuzuweisen.Sie können malloc und Freunde oder new dafür verwenden, aber stellen Sie sicher, es ist die gleiche Methode, die Sie in Ihrem Standardkonstruktor verwenden, da Sie nur einen Destruktor erhalten, und die Aufrufe müssen übereinstimmen.

1

Wenn eine stichprobenartige Kopie in Ordnung ist, müssen Sie nichts tun. Wenn Sie eine tiefe Kopie wünschen, müssen Sie neuen Speicher für Kopien aller Mitglieder zuweisen.

0

Wenn Sie einen Kopierkonstruktor schreiben, sollten Sie Speicher für alle Mitglieder zuweisen. In Ihrem Fall:

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

Operator = ist irgendwie ähnlich, aber ohne Speicherzuweisung.

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

Ich habe das vor kurzem entdeckt, als ich Zeiger innerhalb einer Klasse haben , ich brauchen einen Copy-Konstruktor angeben.

Es ist nicht vollständig richtig. Wenn Sie Zeiger in Ihrer Klasse haben und den Speicher mit new zuweisen, dann müssen Sie sich Gedanken über den Kopierkonstruktor machen. Vergessen Sie auch nicht den Zuweisungsoperator und Destruktor. Sie müssen den zugewiesenen Speicher löschen, indem Sie delete verwenden.

Es heißt Law Of The Big Three.

Beispiel:

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

Vor kurzem habe ich entdeckt, dass, wenn ich Zeiger innerhalb einer Klasse haben , ich brauchen einen Copy-Konstruktor

Öfter angeben als es nicht a eine gute Idee, sie einfach zu deaktivieren, indem Sie sie (und den Zuweisungsoperator) als privat deklarieren und nicht implementieren.

+0

Warum das? Kopieren Sie etwas konzeptionell "verboten" in der Programmierung, oder liegt es daran, dass das Kopieren zu Problemen in OO-Sprachen führen kann? – Biga

+0

Es ist nicht "verboten" - es macht oft keinen Sinn und/oder ist teuer. –

0

Meistens müssen Sie etwas falsch machen, wenn Sie einen Kopierkonstruktor oder einen Zuweisungsoperator schreiben müssen. Belassen Sie die Kopierkonstruktoren und Zuweisungsoperatoren den Implementierern der Standardbibliothek. Verfassen Sie Ihre Klassen von bereits kopierbaren und zuweisbaren Elementen und Sie müssen nicht Ihre eigenen schreiben.

Zum Beispiel könnte das int * Mitglied stattdessen ein std :: Vektor sein.

Wenn Sie die Klasse nicht kopierbar/zuweisbar machen können, können Sie sie möglicherweise nicht kopierbar/zuweisbar machen, indem Sie einen privaten Kopierkonstruktor und Zuweisungsoperator deklarieren, aber nicht implementieren.

Nur wenn keine der oben genannten Möglichkeiten realisierbar sind, sollten Sie einen eigenen Kopierkonstruktor oder Zuweisungsoperator implementieren.