C++中String類的程式崩潰問題
阿新 • • 發佈:2019-01-05
#include <stdio.h> #include <iostream> using namespace std; class String { private: char *str; public: String(char *p = NULL) { if (p != NULL) { str = new char[strlen(p) + 1]; strcpy(str,p); } else { str = NULL; } } ~String() { delete []str; } void print() const { if (str != NULL) { printf("%s\n",str); } } }; int main() { char ch1[] = "hello world"; String s1(ch1); s1.print(); return 0; }
以上,是一個普通的String類,編譯執行也不會出現任何錯誤。
但是,當在main()中再構造一個S2,用S1調動系統的拷貝建構函式構造S2時,則出現崩潰。
int main()
{
char ch1[] = "hello world";
String s1(ch1);
String s2(s1);
s1.print();
s2.print();
return 0;
}
這是為什麼呢~~~
首先,我們都知道,當拿一個物件去初始化另一個物件的時候,如果沒有自定義的拷貝建構函式,則要呼叫系統的預設拷貝建構函式,在String類中,預設的拷貝建構函式如下
String(const String &s):s(s.str)
{}
系統預設的拷貝建構函式是怎麼做到拷貝構造的呢?
待圖解:
於是,在main()函式開始後,首先調動建構函式開闢空間構造s1,並且用ch1字串為其初始化。假如,這片空間的首地址是:0x0065fa82
那麼,當函式構造s2物件的時候,則需要呼叫系統預設的拷貝建構函式去構造s2,可是,s2的str空間卻因此與s1相同了有木有!!!任何一個物件的str改變都會造成另一個的
str也改變的好不好!!!
然後,main()結束,呼叫解構函式析構掉s1、s2.此時,出問題了,因為兩個物件要析構兩次,也就是說,要釋放兩次str空間,可是,明明兩個物件的str都指向了同一片空間,這樣,就意味著~~~~~~你懂得。於是,就崩潰了。
這就是傳說中的,淺拷貝。
可以設想一下,假如系統預設的拷貝構造都是這麼簡單的話,那麼當你所定義的任何一個類中,它的資料成員中有指標存在時,你是否會擔心發生相同的問題呢。這是很嚴肅很現實的。
怎麼解決?
答案就是:自己定義拷貝建構函式。
當拿一個物件去初始化另一個物件的時候,老老實實的根據所拿物件的指標空間大小來開闢相同大小的空間,是字串就strcpy()過去,是int等等就挨個賦值過去。
記著,字串末尾的'\0'在記憶體中是要佔空間的,但是,strlen()出來的大小,卻不包括字串末尾的'\0',so~~~~開闢空間的時候,你得自己多開一個。
說的我口乾,喝口水去,貼上原始碼,自己看吧。
class String
{
private:
char *str;
public:
String(char *p = NULL)
{
if(p != NULL)
{
str = new char[strlen(p) + 1];
strcpy(str,p);
}
else
{
str = new char[1];
*str = '\0';
}
}
String(const String &s)
{
if(str != s.str)
{
str = new char[strlen(s.str) + 1];
strcpy(str,s.str);
}
}
~String()
{
delete []str;
str = NULL;
}
void Print() const
{
cout<<str<<endl;
}
};