Copia profonda e copia superficiale in C++

Jinku Hu 12 ottobre 2023
  1. La copia superficiale viene utilizzata dal costruttore di copia predefinito in C++
  2. Utilizza il Costruttore di copia personalizzato per implementare il comportamento della copia profonda in C++
Copia profonda e copia superficiale in C++

In questo articolo verranno illustrati più metodi su come utilizzare la copia completa e la copia superficiale in C++.

La copia superficiale viene utilizzata dal costruttore di copia predefinito in C++

Le classi C++ sono generalmente definite con diverse operazioni, denominate collettivamente controllo della copia, specificate esplicitamente dall’utente o implicitamente dal compilatore. Queste funzioni membro sono indicate come: costruttore di copia, operatore di assegnazione di copia, costruttore di spostamento, operatore di assegnazione di spostamento e distruttore. Copia costruttore e sposta costruttore implementano operazioni che si verificano quando l’oggetto viene inizializzato da un altro oggetto dello stesso tipo. Sebbene, quando queste funzioni vengono sintetizzate dal compilatore in modo implicito, alcuni tipi di classe potrebbero comportarsi in modo non corretto. Ad esempio, le classi che gestiscono la memoria dinamica condivideranno i membri dei dati che devono essere allocati manualmente. Pertanto, il programmatore è responsabile dell’implementazione esplicita delle funzioni membro di cui sopra.

In questo caso, mostriamo il caso di un costruttore di copia in una classe chiamata Person con due membri di dati std::string, uno dei quali è allocato usando l’operatore new. Il seguente codice di esempio mostra cosa succede quando il costruttore di copia non è definito esplicitamente e inizializziamo un oggetto Person con un altro oggetto Person. Notare che P1 ha delle stringhe memorizzate - Buddy/Rich dopo l’inizializzazione e P2 ha gli stessi valori dopo che il costruttore di copia è stato chiamato nell’istruzione - Person P2 = P1;. Dopo che la funzione renamePerson è stata eseguita sull’oggetto P1, viene modificato anche il membro di dati surname dell’oggetto P2.

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

Produzione:

Buddy Rich
Buddy Rich
Heinz Lulu
Buddy Lulu

Utilizza il Costruttore di copia personalizzato per implementare il comportamento della copia profonda in C++

D’altra parte, quando implementiamo un costruttore di copia personalizzato per la classe Person, si comporta correttamente e non modifica l’oggetto P2 dopo l’istruzione P1.renamePerson("Heinz", "Lulu"). Nel frammento di codice precedente, il membro surname dell’oggetto P2 puntava alla stessa stringa dell’oggetto P1 e renamePerson modificava entrambi gli oggetti. Questa volta, P2 ha il proprio membro cognome allocato sulla memoria dinamica e non lo condivide con l’oggetto 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);
}

Produzione:

Buddy Rich
Buddy Rich
Heinz Lulu
Buddy Rich
Autore: 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

Articolo correlato - C++ Class