C++ 中的深拷貝 VS 淺拷貝
Jinku Hu
2023年10月12日
本文將演示如何在 C++ 中使用深拷貝 VS 淺拷貝的多種方法。
在 C++ 中預設使用淺拷貝的拷貝構造器
C++ 類通常由幾個操作定義,這些操作統稱為複製控制
,由使用者顯式指定或由編譯器隱式指定。這些成員函式表示為:複製建構函式
,複製賦值運算子
,移動建構函式
,移動賦值運算子
和解構函式
。複製建構函式和移動建構函式實現從相同型別的另一個物件初始化該物件時發生的操作。雖然,當這些函式由編譯器隱式地綜合時,某些類型別可能行為不正確。例如,管理動態記憶體的類將共享需要手動分配的資料成員。因此,程式設計師負責顯式實現上述成員函式。
在這種情況下,我們演示了一個名為 Person
的類中具有兩個 std::string
資料成員的複製建構函式的情況,其中一個成員是使用 new
運算子分配的。下面的示例程式碼顯示了未明確定義複製建構函式,並且我們用另一個 Person
物件初始化了 Person
物件時會發生的情況。請注意,初始化之後,P1
儲存了字串-Buddy
/Rich
,並且在語句 Person P2 = P1;
中呼叫了複製建構函式之後,P2
具有相同的值。在 P1
物件上執行 renamePerson
函式之後,還將修改 P2
物件的 surname
資料成員。
#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);
}
輸出:
Buddy Rich
Buddy Rich
Heinz Lulu
Buddy Lulu
在 C++ 中使用自定義拷貝構造器來實現深度拷貝行為
另一方面,當我們為 Person
類實現自定義複製建構函式時,它的行為正確,並且不會在 P1.renamePerson("Heinz", "Lulu")
語句之後修改 P2
物件。在先前的程式碼片段中,P2
物件的 surname
成員指向與 P1
物件相同的字串,並且 renamePerson
修改了兩個物件。這次,P2
在動態記憶體上分配了自己的 surname
成員,並且不與 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);
}
輸出:
Buddy Rich
Buddy Rich
Heinz Lulu
Buddy Rich
作者: Jinku Hu