淺拷貝問題的提出
首先,我們先看一段代碼:
#include<string.h>
using namespace std;
class test
{
private:
char* p;
int len;
public:
test(const char* p)
{
len = strlen(p);
this->p = new char[len + 1];
strcpy(this->p,p);
}
~test()
{
delete p;
p = NULL;
len = 0;
}
};
int main()
{
test t1("hello");
test t2(t1);
return 0;
}
這段代碼可以編譯通過,但是當運行時會出現 ”core dumped“的錯誤:
1] 6928 abort (core dumped) ./a.out
那麼這種錯誤是咋樣引起的呢?這就是由於淺拷貝所引起的問題,下面,我們就來詳細介紹一下淺拷貝以及解決方案。
原因分析
在分析原因之前,我們需要先了解一個問題,那就是拷貝構造函數的調用時機:
當用一個已初始化過了的自定義類類型對象去初始化另一個新構造的對象的時候,拷貝構造函數就會被自動調用。也就是說,當類的對象需要拷貝時,拷貝構造函數將會被調用。以下情況都會調用拷貝構造函數:
(1)、一個對象以值傳遞的方式傳入函數體
(2)、一個對象以值傳遞的方式從函數返回
(3)、一個對象需要通過另外一個對象進行初始化。
如果在類中沒有顯式地聲明一個拷貝構造函數,那麼,編譯器將會自動生成一個默認的拷貝構造函數,該構造函數完成對象之間的位拷貝,也稱爲淺拷貝。
默認對象的拷貝構造函數在完成對象成員的簡單複製時,沒有問題,但是當對象的數據資源是由指針指向的堆時,默認拷貝構造函數僅僅只複製指針,而沒有重新開闢空間,這就導致有兩個指針指向同一塊堆上空間,當其中一個之後對象執行析構函數,釋放內存 ,另一個對象的成員指針就變成了一個“野指針”,當其再次執行析構函數時,就會再次釋放一次已經釋放了的內存空間,就會出現“core dumped”錯誤!
注意:需要說明的是,在c++編譯器提供的“=”操作也是淺拷貝
解決方法
採取深拷貝技術,即當這個類的對象發生複製過程的時候,資源重新分配,這個過程就是深拷貝。
在C++ 中具體的方法就是:
(1)、顯式提供拷貝構造函數;
(2)、不使用編譯器自帶的等號運算符,重載“=”運算符。
#include<string.h>
using namespace std;
class test
{
private:
char* p;
int len;
public:
test(const char* p)
{
len = strlen(p);
this->p = new char[len + 1];
strcpy(this->p,p);
}
//拷貝構造函數
test(test & obj)
{
len = obj.len;
p = new char[len + 1];
strcpy(p,obj.p);
}
//重載等號運算符
void operator = (test &obj)
{
if(p != NULL )
{
delete p;
p = NULL;
len = 0;
}
p = new char(obj.len + 1);
strcpy(p,obj.p);
len = obj.len;
}
~test()
{
if(p != NULL)
{
delete[] p;
p = NULL;
len = 0;
}
}
};
int main()
{
test t1("hello");
//test t2(t1);
test t2("asd");
t2 = t1;
return 0;
}
採用深拷貝可以避免這個問題。所以說,在定義一個應該類時,顯式提供拷貝構造函數,重載“=”運算符,避免一些不必要的問題。