C++編譯器會為每個類自動生成一個默認的構造函數、析構函數、賦值函數、拷貝構造函數,這當然是在你沒有為你的類聲明這些函數的時候。這些默認的功能函數在為你提供方便的時候,也會給你帶來麻煩。
例如:
class string {
public:
string(const char *value);
~string();
... // 沒有拷貝構造函數和operator=
private:
char *data;
};
string::string(const char *value)
{
if (value) {
data = new char[strlen(value) + 1];
strcpy(data, value);
}
else {
data = new char[1];
*data = '\0';
}
}
inline string::~string() { delete [] data; } //注意:new 和delete 要采用相同的形式。
如果有string的兩個對象,
string a("hello");
string b("world");
當b=a時,因為你自己沒為類定義那些函數,所以C++編譯器會提供默認的賦值函數,這個缺省的賦值操作符會執行從a的成員到b的成員的逐個成員的賦值操作,對指針(a.data和b.data) 來說就是逐位拷貝。這種情況下至少有兩個問題。
第一,b曾指向的內存永遠不會被刪除,因而會永遠丟失。這是產生內存泄漏的典型例子。
第二,現在a和b包含的指針指向同一個字符串,那么只要其中一個離開了它的生存空間,其析構函數就會刪除掉另一個指針還指向的那塊內存,重復析構的問題。
下面的語句:
string a("hello"); // 定義并構造 a
{ // 開一個新的生存空間
string b("world"); // 定義并構造 b
...
b = a; // 執行 operator=, 調用默認賦值函數
// 丟失b的內存,造成內存泄露。
} // 離開生存空間, 調用
// b的析構函數
string c = a; // c.data 的值不能確定! 調用默認的拷貝構造函數
// 但是a.data 已被刪除,無法進行拷貝構造。
當這類對象進行函數參數按值傳遞時,形參會按照缺省的拷貝構造函數進行初始化,形參擁有一個指向該對象指針的一個拷貝,當函數結束時,形參會調用析構函數,該對象的指針也被銷毀。
class stack
{
public:
stack(const char *value);
~stack();
char * data;
};
stack::stack(const char *value)
{
if(value)
{
data= new char[strlen(value)+1];
strcpy(data,value);
}
else
{
data= new char[1];
*data = '\0';
}
}
inline stack::~stack()
{
delete []data;
}
void dosth(stack pstk)
{
cout<<pstk.data<<endl;
}
int main()
{
stack str("iamxczhang");
dosth(str);
}
在析構函數出設立斷點,你會看到析構函數執行兩次!
牢記:只要類里有指針變量就得自己寫拷貝構造函數和賦值函數,但是你確定用不著這些函數時,可以把這些函數做private聲明而不去實現它,這就防止了會有人去調用它們,也防止了編譯器去生成它們。