2017-02-08 5 views
1

Ich bin ein Anfänger in der Programmierung und ich lerne über Kopie Konstruktoren. Aus verschiedenen Quellen kann ich sehen, dass Kopierkonstruktoren nützlich sind, wenn ich ein Klassenobjekt "tief kopieren" möchte, so dass die Zeigerelemente des neuen Objekts auf neue Speicherorte zeigen.Kopieren Konstruktor und dynamischer Speicher

Meine Frage ist, was ist der Vorteil der Definition des Kopierkonstruktors wie in der Klasse CopyCat in meinem Beispiel, wenn ich das gleiche Ergebnis mit einem leeren Kopierkonstruktor bekomme (wie in der Klasse EmptyCat)?

Meine zweite Frage ist, warum funktionieren Klasse Cat und Klasse EmptyCat anders? Der einzige Unterschied zwischen ihnen ist, dass ich in EmptyCat einen leeren Kopierkonstruktor definiere. Aber während ich das Programm ausführe, sehe ich, dass in EmptyCat nach dem Kopieren das Zeigerelement auf eine neue Position zeigt, während es in der Klasse Cat als flache Kopie arbeitet.

#include "iostream" 

class Cat 
{ 
    public: 
    void GetMem() { std::cout << itsAge << "\n"; } 
    private: 
    int * itsAge = new int; 
}; 

class EmptyCat 
{ 
    public: 
    EmptyCat() {} 
    ~EmptyCat() {} 
    EmptyCat(EmptyCat&obj) {} 
    void GetMem() { std::cout << itsAge << "\n"; } 
    private: 
    int * itsAge = new int; 
}; 

class CopyCat 
{ 
    public: 
    CopyCat() {} 
    ~CopyCat() {} 
    CopyCat(CopyCat&obj); 
    int GetAge() { return *itsAge; } 
    void GetMem() { std::cout << itsAge << "\n"; } 
    private: 
    int * itsAge = new int; 
}; 

CopyCat::CopyCat(CopyCat & obj) 
{ 
    itsAge = new int; 
    *itsAge = obj.GetAge(); 
} 

int main() 
{ 
    Cat Garfield; 
    Cat Kitty(Garfield); 

    std::cout << "Memory addresses for the objects' <itsAge> member:" << std::endl; 
    std::cout << "Garfield and Kitty (Class Cat):" << std::endl; 
    Garfield.GetMem(); 
    Kitty.GetMem(); 

    EmptyCat Meow; 
    EmptyCat Purr(Meow); 

    std::cout << std::endl << "Meow and Purr (Class EmptyCat):" << std::endl; 
    Meow.GetMem(); 
    Purr.GetMem(); 

    CopyCat Fluffy; 
    CopyCat Felix(Fluffy); 

    std::cout << std::endl << "Fluffy and Felix (Class CopyCat):" << std::endl; 
    Fluffy.GetMem(); 
    Felix.GetMem(); 

    system("pause"); 
    return 0; 
} 

Wenn ich das Programm ausführen ich dieses:

Memory addresses for the objects' <itsAge> member: 
Garfield and Kitty (Class Cat): 
00BBDA60 
00BBDA60 

Meow and Purr (Class EmptyCat): 
00BB46A0 
00BB8280 

Fluffy and Felix (Class CopyCat): 
00BB82B0 
00BBE8A0 
Press any key to continue . . . 
+3

Ihre Klasse verliert Speicher, es ist also kein gutes Beispiel für irgendetwas. –

+0

Ich wollte es so einfach wie möglich machen und konzentriere mich auf das Verhalten des Kopierkonstruktors – SVG

+0

Aber damit vermisst du alles, was interessant ist. Wenn Ihr Destruktor die Zuweisung löschen würde, würden Sie sofort die Regel von drei und all das entdecken. –

Antwort

0

Meine Frage ist, was ist der Vorteil der Definition des Copykonstruktor als ich in der Klasse CopyCat in meinem Beispiel zu tun, wenn ich das gleiche Ergebnis mit einem leeren Copykonstruktor (wie in der Klasse EmptyCat) bekommen?

Sie erhalten nicht das gleiche Ergebnis. CopyCat reserviert neuen Speicher und kopiert den Wert aus der alten Klasse. Das EmptyCat reserviert nur neuen Speicher aber kopiert den Wert nicht.

Meine zweite Frage ist, warum funktionieren Klasse Cat und Klasse EmptyCat anders? Der einzige Unterschied zwischen ihnen ist, dass ich in EmptyCat einen leeren Kopierkonstruktor definiere. Aber während ich das Programm ausführe, sehe ich, dass in EmptyCat nach dem Kopieren das Zeigerelement auf eine neue Position zeigt, während es in der Klasse Cat als flache Kopie arbeitet.

In Cat haben Sie keinen Kopierkonstruktor deklariert, so dass der Compiler bei Bedarf einen solchen generiert. Der standardmäßige Kopierkonstruktor erstellt eine stichwortweise Kopie aus dem Original. In Ihrem Fall kopiert dies den Zeiger (so dass er die gleiche Adresse wie das Original speichert).

In der EmptyCat haben Sie einen benutzerdefinierten Kopierkonstruktor. Da dieser Zeiger nicht mit dem Zeigerelement arbeitet, wird der Standardwert verwendet.

int * itsAge = new int; 

Dies ist, was ein neues int zuordnet und bekommt man einen anderen Zeigerwert.

+0

Basierend auf Ihrer Antwort habe ich einige Änderungen vorgenommen und mir gezeigt, wie der leer definierte Kopierkonstruktor funktioniert. Copy-Konstruktor mit leerer Definition ist eigentlich kein Copy-Konstruktor, es kopiert nichts, es funktioniert tatsächlich als eine neue Objekterzeugung. Es erzeugt ein neues Objekt basierend auf der Klassendefinition, und deshalb bin ich verwirrt worden, weil ich die Anweisung 'int * itsAge = new int;' in der Klassendefinition hatte (also basierend darauf, dass der leere Kopierkonstruktor neuen Speicher zugewiesen hat) das neue Objekt). – SVG

+0

Ich habe weitere Nachforschungen angestellt und herausgefunden, dass der sogenannte implizite Kopierkonstruktor (der verwendet wird, wenn es keinen deklarierten Kopierkonstruktor gibt) äquivalent ist: 'Class :: Class (const Class & object): x (object.x), y (object.y), z (object.z) {} ' Also macht der implizite Kopierkonstruktor mehr als ein leer definiertes. – SVG

0

Sie sind nicht das gleiche Verhalten immer mit und ohne einen leeren Copykonstruktor. EmptyCat(EmptyCat& obj) { } tut absolut nichts.

CopyCat(CopyCat& obj) { 
    itsAge = new int; 
    *itsAge = obj.GetAge(); 
} 

ordnet dynamisch ein neues int und weist sie einen Wert aus den obj.

0

Tiefes Kopieren und flaches Kopieren ist eher ein C-Konzept, in dem Sie nur Strukturen und rohe Zeiger haben. Ein Zeiger kann besessen sein, in diesem Fall muss die Kopie tief sein, oder sie kann geteilt werden, in diesem Fall ist die Kopie flach (und Sie müssen vorsichtig sein, wenn Sie sie freigeben, wenn sie mit malloc zugewiesen wird).

In C++ ist new jetzt effektiv veraltet. Wir haben eindeutige Zeiger, die "Besitzer von Zeigern" und "gemeinsame Zeiger" sind. Zeiger sind jedoch relativ selten. Array-Member von Klassen sind std :: vectors, string-Member sind std :: strings. Und Kopien sind automatisch tief (Sie verwenden eine Referenz, wenn Sie eine flache Kopie wünschen).

Zeiger werden für relativ ungewöhnliche Situationen wie Bäume und Graphen zurückgehalten.

Verwandte Themen