Tiefes Kopieren VS Untiefe Kopieren in C++

Jinku Hu 12 Oktober 2023
  1. Flache Kopie wird vom Standardkopierkonstruktor in C++ verwendet
  2. Verwenden des benutzerdefinierten Kopierkonstruktors zur Implementierung des Deep-Copy-Verhaltens in C++
Tiefes Kopieren VS Untiefe Kopieren in C++

In diesem Artikel werden verschiedene Methoden zur Verwendung von Deep Copy VS Flat Copy in C++ beschrieben.

Flache Kopie wird vom Standardkopierkonstruktor in C++ verwendet

C++ Klassen werden im Allgemeinen mit mehreren Operationen definiert, die zusammen als Kopiersteuerung bezeichnet werden und vom Benutzer explizit oder implizit vom Compiler angegeben werden. Diese Elementfunktionen werden bezeichnet als: Kopierkonstruktor, Kopierzuweisungsoperator, Verschiebungskonstruktor, Verschiebungszuweisungsoperator und Destruktor. Kopierkonstruktor und Verschiebungskonstruktor implementieren Operationen, die auftreten, wenn das Objekt von einem anderen Objekt desselben Typs initialisiert wird. Wenn diese Funktionen vom Compiler implizit synthetisiert werden, verhalten sich einige Klassentypen möglicherweise falsch. Beispielsweise teilen die Klassen, die den dynamischen Speicher verwalten, Datenelemente, die manuell zugewiesen werden müssen. Somit ist der Programmierer dafür verantwortlich, die obigen Mitgliedsfunktionen explizit zu implementieren.

In diesem Fall demonstrieren wir den Fall eines Kopierkonstruktors in einer Klasse mit dem Namen Person mit zwei Datenelementen std::string, von denen eines mit dem Operator new zugewiesen wird. Der folgende Beispielcode zeigt, was passiert, wenn der Kopierkonstruktor nicht explizit definiert ist und wir ein Person-Objekt mit einem anderen Person-Objekt initialisieren. Beachten Sie, dass P1 Zeichenketten gespeichert hat - Buddy / Rich nach der Initialisierung und P2 dieselben Werte hat, nachdem der Kopierkonstruktor in der Anweisung - Person P2 = P1; aufgerufen wurde. Nachdem die Funktion renamePerson für das Objekt P1 ausgeführt wurde, wird auch das Datenelement Nachname des Objekts P2 geändert.

#include <iostream>
#include <string>
#include <utility>
#include <vector>

using std::cout;
using std::endl;
using std::string;
using std::vector;

class Person {
 public:
  Person() = default;
  Person(string n, string s) {
    name = std::move(n);
    surname = new string(std::move(s));
  }

  ~Person() { delete surname; }

  void renamePerson(const string &n, const string &s) {
    name.assign(n);
    surname->assign(s);
  };

  string &getName() { return name; };
  string &getSurname() { return *surname; };

  void printPerson() { cout << name << " " << *surname; }

 private:
  string name;
  string *surname{};
};

int main() {
  Person P1("Buddy", "Rich");
  Person P2 = P1;

  P1.printPerson();
  cout << endl;
  P2.printPerson();
  cout << endl;

  P1.renamePerson("Heinz", "Lulu");

  P1.printPerson();
  cout << endl;
  P2.printPerson();
  cout << endl;

  exit(EXIT_SUCCESS);
}

Ausgabe:

Buddy Rich
Buddy Rich
Heinz Lulu
Buddy Lulu

Verwenden des benutzerdefinierten Kopierkonstruktors zur Implementierung des Deep-Copy-Verhaltens in C++

Wenn wir dagegen einen benutzerdefinierten Kopierkonstruktor für die Klasse Person implementieren, verhält er sich korrekt und ändert das Objekt P2 nach der Anweisung P1.renamePerson("Heinz", "Lulu") nicht. Im vorherigen Codeausschnitt zeigte das Mitglied Nachname des Objekts P2 auf dieselbe Zeichenkette wie das Objekt P1, und renamePerson hat beide Objekte geändert. Dieses Mal hat P2 ein eigenes Mitglied mit dem Namen Nachname im dynamischen Speicher und teilt es nicht mit dem Objekt P1.

#include <iostream>
#include <string>
#include <utility>
#include <vector>

using std::cout;
using std::endl;
using std::string;
using std::vector;

class Person {
 public:
  Person() = default;
  Person(string n, string s) {
    name = std::move(n);
    surname = new string(std::move(s));
  }
  Person(Person &p) {
    name = p.name;
    surname = new string(*p.surname);
  }

  ~Person() { delete surname; }

  void renamePerson(const string &n, const string &s) {
    name.assign(n);
    surname->assign(s);
  };

  string &getName() { return name; };
  string &getSurname() { return *surname; };

  void printPerson() { cout << name << " " << *surname; }

 private:
  string name;
  string *surname{};
};

int main() {
  Person P1("Buddy", "Rich");
  Person P2 = P1;

  P1.printPerson();
  cout << endl;
  P2.printPerson();
  cout << endl;

  P1.renamePerson("Heinz", "Lulu");

  P1.printPerson();
  cout << endl;
  P2.printPerson();
  cout << endl;

  exit(EXIT_SUCCESS);
}

Ausgabe:

Buddy Rich
Buddy Rich
Heinz Lulu
Buddy Rich
Autor: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Verwandter Artikel - C++ Class