一、拷貝構造的定義
拷貝構造是一種特殊的構造函數,它是用來初始化同一類中的另一個對象的;其實就是將一個對象中的數據,拷貝到另一個對象中。
class Point{
public:
Point(int a,int b) {
x = a;
y = b;
}
// 拷貝構造函數
Point(const Point &p) {
x = p.x;
y = p.y;
}
int x;
int y;
};
二、拷貝構造的默認實現
如果不顯示定義拷貝構造函數,編譯器會默認提供一個拷貝構造函數,其實現方式是逐個成員進行複製。
class Point{
public:
Point(int a,int b) {
x = a;
y = b;
}
// 拷貝構造函數(默認實現)
Point(const Point &p) {
x = p.x;
y = p.y;
}
int x;
int y;
};
三、防止拷貝的實現方式
有時候,我們需要限制對象的拷貝行為,可以採用以下兩種方式。
1. 聲明拷貝構造函數為私有(禁用拷貝)
將拷貝構造函數聲明為私有的可以禁用對象的拷貝行為,防止對象被複制。
class SingleObject{
public:
static SingleObject* getInstance() {
if (m_instance == nullptr) {
m_instance = new SingleObject();
}
return m_instance;
}
private:
// 禁止拷貝行為
SingleObject(const SingleObject&);
SingleObject();
static SingleObject* m_instance;
};
2. 將拷貝構造函數定義為delete
C++11標準提供了delete關鍵字,用於防止使用某個函數或者操作符,可以將其置為刪除狀態。
class SingleObject{
public:
static SingleObject* getInstance() {
if (m_instance == nullptr) {
m_instance = new SingleObject();
}
return m_instance;
}
// 禁止拷貝行為
SingleObject(const SingleObject&) = delete;
SingleObject& operator=(const SingleObject&) = delete;
private:
SingleObject();
static SingleObject* m_instance;
};
四、拷貝構造的調用時機
拷貝構造函數在以下幾種情況下會被調用。
1. 用一個對象去初始化另一個同類對象
class Point{
public:
Point(int a,int b) {
x = a;
y = b;
}
// 拷貝構造函數
Point(const Point &p) {
x = p.x;
y = p.y;
}
int x;
int y;
};
Point p1(1,2);
Point p2 = p1; // 通過p1去初始化p2,會調用拷貝構造函數
2. 函數傳參時,按值傳遞
class Point{
public:
Point(int a,int b) {
x = a;
y = b;
}
// 拷貝構造函數
Point(const Point &p) {
x = p.x;
y = p.y;
}
int x;
int y;
};
void print(Point p) {} // 按值傳遞的方式調用函數
Point p(1,2);
print(p); // 調用函數時按值傳遞,會調用拷貝構造函數
3. 函數返回對象時,按值返回
class Point{
public:
Point(int a,int b) {
x = a;
y = b;
}
// 拷貝構造函數
Point(const Point &p) {
x = p.x;
y = p.y;
}
int x;
int y;
};
Point getPoint() {
Point p(1,2);
return p; // 返回對象時按值返回,會調用拷貝構造函數
}
五、深拷貝和淺拷貝
當類中有指針類型成員時,拷貝構造將會帶來深拷貝和淺拷貝的問題。
1. 深拷貝
拷貝構造時,為指針成員重新申請內存,並將原來內存中的數據複製到新內存中,這樣兩個指針成員指向的內存是不同的。
class String{
public:
String() {
m_data = new char[1];
*m_data = '\0';
}
String(const char* data) {
m_data = new char[strlen(data) + 1];
strcpy(m_data, data);
}
String(const String& other) {
m_data = new char[strlen(other.m_data) + 1];
strcpy(m_data, other.m_data);
}
~String() {
delete[] m_data;
}
private:
char* m_data;
};
2. 淺拷貝
拷貝構造時,直接將指針成員的值複製到新對象中,這樣兩個指針成員指向的內存是相同的,會帶來指針重複刪除的問題。
class String{
public:
String() {
m_data = new char[1];
*m_data = '\0';
}
String(const char* data) {
m_data = new char[strlen(data) + 1];
strcpy(m_data, data);
}
String(const String& other) {
m_data = other.m_data; // 淺拷貝
}
~String() {
delete[] m_data; // 重複刪除指針會導致錯誤
}
private:
char* m_data;
};
六、拷貝賦值運算符重載
拷貝賦值運算符(=)和拷貝構造函數的作用很相似,都是進行對象的拷貝,但兩者的調用時機不同。拷貝構造在對象創建時被調用,拷貝賦值在對象創建後被調用。
class String{
public:
String() {
m_data = new char[1];
*m_data = '\0';
}
String(const char* data) {
m_data = new char[strlen(data) + 1];
strcpy(m_data, data);
}
String(const String& other) {
m_data = new char[strlen(other.m_data) + 1];
strcpy(m_data, other.m_data);
}
~String() {
delete[] m_data;
}
// 拷貝賦值運算符重載
String& operator=(const String& other) {
if (this != &other) { // 防止自我賦值
delete[] m_data;
m_data = new char[strlen(other.m_data) + 1];
strcpy(m_data, other.m_data);
}
return *this;
}
private:
char* m_data;
};
七、小結
拷貝構造是一種特殊的構造函數,其作用是用於初始化同一類中的另一個對象。對於指針類型成員,需要注意深拷貝和淺拷貝的問題,避免指針重複刪除。
原創文章,作者:BMAS,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/146224.html