Kursinhalt
C++ OOP
C++ OOP
Kopierkonstruktoren
Es gibt zwei spezialisierte Konstruktoren: den Kopierkonstruktor und den Move-Konstruktor. Das Verständnis dieser Konstruktoren ist entscheidend für effektives Ressourcenmanagement, Speicherzuweisung und Objektmanipulation.
Kopierkonstruktor
Beim Kopieren von zwei Variablen eines primitiven Typs ist der Prozess unkompliziert und erfordert normalerweise keine besondere Berücksichtigung. Das Kopieren von Objekten hingegen ist eine komplexere und kniffligere Aufgabe. Der Unterschied zwischen flacher Kopie und tiefer Kopie wird deutlich, wenn Sie den folgenden Code untersuchen:
main
#include <iostream> class Example { public: // Constructor that initializes p_data with a new integer value Example(int data) : p_data(new int(data)) { } // Destructor that prints the value of p_data and deallocates memory ~Example() { std::cout << *p_data << std::endl; delete p_data; } int* p_data; // Pointer to an integer }; int main() { Example obj1(25); // Creating an object with an initial value of 25 Example obj2(obj1); // Creating a second object using the obj1 *obj2.p_data = 1000; // Modifying the value of by p_data in the obj2 }
Hinweis
Es erzeugt Fehler:
free(): double free detected
Hinweis
Genau deshalb ist ein Kopierkonstruktor unerlässlich. Er ermöglicht uns, eine tiefe Kopie durchzuführen und dabei Sicherheit zu gewährleisten.
Syntax des Kopierkonstruktors
Das Erstellen eines Kopierkonstruktors weist einige Ähnlichkeiten mit dem Erstellen eines Konstruktors auf, hat jedoch seine eigenen besonderen Merkmale. Der allgemeine Ansatz zur Erstellung eines Kopierkonstruktors ist wie üblich unten aufgeführt:
- Name: Der Kopierkonstruktor hat denselben Namen wie die Klasse. Er hat kein einzigartiges Präfix oder Symbol, um ihn vom regulären Konstruktor zu unterscheiden.
- Parametertyp: Er nimmt einen einzelnen Parameter, typischerweise eine Referenz auf ein konstantes Objekt derselben Klasse.
- Kein Rückgabewert: Ähnlich wie andere Konstruktoren und der Destruktor hat der Kopierkonstruktor keinen Rückgabewert, nicht einmal void.
- Besondere Verwendung: Er wird automatisch vom Compiler in bestimmten Szenarien aufgerufen, wie zum Beispiel, wenn ein Objekt als Wert an eine Funktion übergeben, von einer Funktion zurückgegeben oder mit einem anderen Objekt derselben Klasse initialisiert wird.
main
#include <iostream> class Example { public: // Constructor that initializes p_data with a new integer value Example(int data) : p_data(new int(data)) { } // Copy constructor that performs deep copy of the Example object Example(const Example& other) : p_data(new int(*other.p_data)) { } // Destructor that prints the value pointed to by p_data and deallocates memory ~Example() { std::cout << *p_data << std::endl; delete p_data; } int* p_data; // Pointer to an integer }; int main() { Example first_obj(25); // Creating an Example object with an initial value of 25 Example second_obj(first_obj); // Creating a obj2 using obj1 *second_obj.p_data = 1000; // Modifying the value pointed }
Die Regel der Drei
Es gibt eine Richtlinie für Klassen, die dynamisch zugewiesenen Speicher oder andere Ressourcen verwalten. Sie besagt, dass eine benutzerdefinierte Implementierung für eine der folgenden drei Methoden bereitgestellt werden muss:
- Destruktor (
~Example()
). - Kopierkonstruktor (
Example(const Example&)
). - Zuweisungsoperator (
Example& operator=(const Example&)
).
Dann muss es oft Implementierungen für alle drei bereitstellen. Dies liegt daran, dass, wenn eine Klasse Ressourcen verwaltet, die benutzerdefinierte Bereinigungs- oder Kopierverhalten erfordern, die vom Compiler bereitgestellten Standardimplementierungen möglicherweise nicht geeignet sind.
1. Was ist der Zweck eines Kopierkonstruktors?
2. Welche der folgenden Aussagen beschreibt eine tiefe Kopie?
Danke für Ihr Feedback!