昨天師兄又出了道測試題,讓我們實現類似于string類的沒有MyString類,剛開始很頭疼,可是真正在自己寫代碼的時候又很興奮的發現,這個過程真的是個很寶貴的機會,讓我又有機會可以很好的熟悉回顧C++的很多知識―類設計,構造析構函數,成員函數,友元函數,引用,重載,字符串操作,動態內存分布。。。。。于是昨天花了半天時間寫了300多行代碼,并認真的進行了相關測試、修改和總結。因為內容有點豐富,所以想分幾次寫出來,條理也清楚些。
類的空間分配:類給它的每個對象都分配了獨立的空間去存儲它的數據成員,所有的對象公共的訪問類方法進行操作。同時在對象的獨立空間中,不包括數據成員動態分配的空間,對象只是記錄了動態分配空間的地址(所以在析構函數調用的時候只是刪除了對像空間,同時需要用new來刪除動態分配的地址)。
一、類聲明―mystring.h:
1. 構造函數:
專門用于構建新對象,給成員數據分配必要的內存空間并將值賦給新對象的成員數據。
默認構造函數:
在未提供顯式初始化值時,被用來創建對象的構造函數(所以它一般沒有參數)
MyString();
復制構造函數:
用于將一個對象復制到新創建的對象中(當然這個被復制的對象必須已經存在)。
MyString(const MyString &str);
給定了一定初始化參數的構造函數:
參數列表中的值會一次賦給新創建對象的各個成員函數:
MyString(const char*str);
2.析構函數:
當對象過期時刪除對象所占的內存空間,并且當對象創建時有用New請求的內存空時,在析構函數中同時要調用delete對原來分配的 內存空間進行釋放,以防止內存泄露。
~MyString();
3.成員函數:
重載賦值成員函數:
MyString &operator=(const MyString &str); //利用已有的string對象通過=給一個對象進行賦值MyString &operator=(const char*str); //直接用常量字符串進行賦值
一般賦值函數:
MyString &assign(const MyString&str);MyString &assign(const char*sstr);
幾個處理字符串的成員函數:
size_t getsize()const; //返回字符串大小void clear(); //把字符串清空bool empty(); //判斷字符串是否為空void swap(MyString &str); //交換兩個字符串int compare(const MyString &str)const; //比較2個字符串的大小//第一個const說明顯式調用的字符串不可更改,括號外面的const說明隱式調用的字符串不可更改,只讀數據 int compare(const char*str);
追加函數:
MyString &operator+=(const MyString&str);MyString &operator+=(const char*str);MyString &append(const MyString&str);MyString &append(const char *str);
生成字串:
MyString substr(size_t pos = 0,n=npos) const;生成字串,從第0個位置開始長度為n,若N超過長度,則為輸出整個字符串的長度
4.友元函數(運算符重載):
友元函數一般都是在類得聲明中進行定義,它不屬于類得成員函數,但是它和類得成員函數一樣同樣的可以對類得所有數據成員進行訪問。
friend bool operator==(const MyString &str1,const MyString &str2);friend bool operator==(const char *str,const MyString &str2);friend bool operator==(const MyString &str1,const MyString *str2);friend bool operator>(const MyString &str1,const MyString &str2);friend bool operator>(const char*str1,const MyString &str2);friend bool operator>(const MyString &str1,const char*str2);
同樣還有<等各種比較。
friend MyString operator+(const MyString &str1,const MyString &str2);friend MyString operator+(const char*str1,const MyString &str2); //兩個字符串進行相加friend MyString operator+(const MyString &str1,const char*str2);friend ostream & operator<<(ostream &os,const MyString &str); //輸出命令符的重載
5.成員數據變量:
char *string; //指向字符串的指針int length; //字符串的長度static const int string_number = 0; //計數創建的字符串的數目
二、實現.cpp文件:
1.構造函數和析構函數:
MyString::MyString() { length = 0; string = new char; char *s = "/0"; memcpy(string,s,1); ++string_number; } MyString::MyString(const char*str) { length = strlen(str); string = new char(length+1); memcpy(string,s,length); ++string_number; } MyString::MyString(MyString &str) { length = str.length; string = str.string; ++string_number; } MyString::~MyString() { delete[]string; --string_number; }
幾個注意的問題:
1)構造函數中必須給所有的數據成員進行初始化。
2)注意在給指向字符串的指針賦值時,左右類型的對應。
char *s代表一個指向字符串的指針,所有右邊必須是一個字符串常量“/0”,而不能是‘/0'.
3)一個指針只能指向一個地址,不能同時指向兩個。
在給string分配了地址之后,下一步我們肯定是確定分配的地址中存放的具體內容,那么這個時候我們都是使用strcpy()或者是
memcpy()把對應的字符串存入地址中。
如果原來我們成這樣實現:
MyString::MyString() { length = 0; string = new char; string = "/0"; ++string_number; }
那么我們在編譯和實現的時候都不會發現有什么錯,但是析構函數使用delete【】釋放內存使執行結果會出現亂碼,因為string=“/0”
讓它指向了一個字符串,并沒有分配內存空間,所以在釋放的時候就會出現錯誤。
4)析構函數中的重要語句 delete【】不要忘
析構函數在使用的時候只會釋放為對象分配的空間,但是對象的空間中只是存儲了數據成員分配內存的地址,所以并沒有釋放數據成員
的內存空間,必須使用delete[]來進行釋放,防止內存泄露
2.重載運算符的成員函數:
MyString &MyString::operator+=(const MyString&str) { char *dest; dest = new char[str.length+length+1]; memcpy(dest,string,length); memcpy(dest+length,str.string,str.length+1); delete[]string; length = length+str.length; string = dest; return*this; } MyString &MyString::operator+=(const char*str) { char *dest; dest = new char[strlen(str)+length+1]; memcpy(dest,string,length); memcpy(dest+length,str,strlen(str)+1); delete[]string; string = dest; return *this; } //字符串賦值 MyString &MyString::operator=(const MyString&str) { if(&str == this) return *this; delete[]string; string = new char[str.length]; memcpy(string,str.string,str.length); length = str.length; return *this; }