1. 程式人生 > >C++中String類的程式崩潰問題

C++中String類的程式崩潰問題

#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,可是,s2str空間卻因此與s1相同了有木有!!!任何一個物件的str改變都會造成另一個的
str也改變的好不好!!!
然後,main()結束,呼叫解構函式析構掉s1s2.此時,出問題了,因為兩個物件要析構兩次,也就是說,要釋放兩次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;
	}
};