Deep Copy VS Shallow Copy em C++
- A cópia superficial é usada pelo construtor de cópia padrão em C++
- Use o construtor de cópia personalizada para implementar o comportamento de cópia profunda em C++
Este artigo demonstrará vários métodos sobre como usar a cópia superficial do VS em cópia profunda em C++.
A cópia superficial é usada pelo construtor de cópia padrão em C++
As classes C++ são geralmente definidas com várias operações, coletivamente referidas como controle de cópia
, especificadas explicitamente pelo usuário ou implicitamente pelo compilador. Essas funções-membro são denotadas como: construtor de cópia
, operador de atribuição de cópia
, construtor de movimentação
, operador de atribuição de movimentação
e destruidor
. O construtor de cópia e o construtor de movimento implementam operações que acontecem quando o objeto é inicializado a partir de outro objeto do mesmo tipo. Embora, quando essas funções são sintetizadas pelo compilador implicitamente, alguns tipos de classe podem se comportar incorretamente. Por exemplo, as classes que gerenciam a memória dinâmica compartilharão membros de dados que precisam ser alocados manualmente. Portanto, o programador é responsável por implementar as funções-membro acima explicitamente.
Nesse caso, demonstramos o caso de um construtor de cópia em uma classe chamada Person
com dois membros de dados std::string
, um dos quais é alocado usando o operador new
. O código de exemplo a seguir mostra o que acontece quando o construtor de cópia não é definido explicitamente e inicializamos um objeto Person
com outro objeto Person
. Observe que P1
armazenou strings - Buddy
/Rich
após a inicialização e P2
tem os mesmos valores depois que o construtor de cópia é chamado na instrução - Person P2 = P1;
. Após a função renamePerson
ser executada no objeto P1
, o membro de dados surname
do objeto P2
também é modificado.
#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);
}
Resultado:
Buddy Rich
Buddy Rich
Heinz Lulu
Buddy Lulu
Use o construtor de cópia personalizada para implementar o comportamento de cópia profunda em C++
Por outro lado, quando implementamos um construtor de cópia personalizado para a classe Person
, ele se comporta corretamente e não modifica o objeto P2
após a instrução P1.renamePerson("Heinz", "Lulu")
. No fragmento de código anterior, o membro surname
do objeto P2
apontava para a mesma string que o objeto P1
e renamePerson
modificou ambos os objetos. Desta vez, P2
tem seu próprio membro surname
alocado na memória dinâmica e não o compartilha com o objeto 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);
}
Resultado:
Buddy Rich
Buddy Rich
Heinz Lulu
Buddy Rich
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