C++實現MyString的範例程式碼
2022-02-17 16:01:40
MyString的構造、解構、拷貝構造、賦值運算
class String { char* str; public: String(const char* p = NULL) :str(NULL) { if (p != NULL) { str = new char[strlen(p) + 1];//strlen()計算至' '截至的字元數 strcpy(str, p); } else { str = new char[1]; //額外提供一個空間 *str = ' '; } } ~String() { if (str != NULL) { delete[] str; } str = NULL; } //ostream& operator<<(const String* const this, ostream &out) ostream& operator<<(ostream& out)const //過載插入操作符 { if (str != NULL) { out << str; } return out; } String(const String& s):str(NULL) { //str = s.str; 淺拷貝 是同一個空間,會造成一個空間釋放兩次 //深拷貝 str = new char[strlen(s.str)+1]; strcpy(str, s.str); } String& operator=(const String& s) { if(this != &s) { delete[]str; str = new char[strlen(s.str)+1] strcpy(str,s.str); } return *this; } }; ostream& operator<<(ostream& out, const String& s) { s << out; //s.operator<<(cout); //operator<<(&s1,cout); return out; } int main() { String s1("123"); s1 << cout; //s1.operator<<(cout); //operator<<(&s1,cout); cout << s1 << endl; //operator<<(cout, s1); }
前面之所以對空指標構建物件提供一個空間的原因:使其在賦值過載中只有指向堆區一種情況進行處理
通過此方式進行等號運運算元過載,然後調動拷貝構造對s2進行重寫構造
輸出流重寫
class String { char* str; public: String(const char* p = NULL) :str(NULL) { if (p != NULL) { str = new char[strlen(p) + 1]; strcpy(str, p); } else { str = new char[1]; //額外提供一個空間 *str = ' '; } } ~String() { if (str != NULL) { delete[] str; } str = NULL; } //ostream& operator<<(const String* const this, ostream &out) ostream& operator<<(ostream& out)const //過載插入操作符 { if (str != NULL) { out << str; } return out; } }; int main() { String s1("123"); s1 << cout; //s1.operator<<(cout); //operator<<(&s1,cout); }
在這裡通過改寫前的程式碼 operator<<(&s1,cout);
不難看出,將cout初始化out,隨後將this.str輸出至out
ostream& operator<<(ostream& out)const
此處只能使用參照,因為cout在ostream類中進行轉移,該類將拷貝建構函式定義為保護存取屬性,無法使用cout初始化out,繼而只能使用參照;同樣若我們不想使用實參去初始化形參,可以將拷貝建構函式定義為私有或保護型別
若希望輸出符合cout << s1 << endl;
此種形式,需要再寫一個全域性函數
class String { char* str; public: String(const char* p = NULL) :str(NULL) { if (p != NULL) { str = new char[strlen(p) + 1]; strcpy(str, p); } else { str = new char[1]; //額外提供一個空間 *str = ' '; } } ~String() { if (str != NULL) { delete[] str; } str = NULL; } //ostream& operator<<(const String* const this, ostream &out) ostream& operator<<(ostream& out)const //過載插入操作符 { if (str != NULL) { out << str; } return out; } }; ostream& operator<<(ostream& out, const String& s) { s << out; //s.operator<<(cout); //operator<<(&s1,cout); return out; } int main() { String s1("123"); s1 << cout; //s1.operator<<(cout); //operator<<(&s1,cout); cout << s1 << endl; //operator<<(cout, s1); }
通過此種形式進行翻轉,繼而達到符合 cout << s1 << endl;
的形式
MyString加號運運算元過載
int main() { String s1("123"); String s2("456"); String s3; s3 = s1 + s2; S3 = s1 + "789"; s3 = "789" + s1; }
分別寫三個加號運運算元過載,來對應上面的三個情況(類+類、類+字串、字串+類)
String operator+(const String& s)const { char *p = new char(strlen(this->str) + strlen(s.str) + 1); strcpy(p, this->str); strcat(p, s.str); return String(p); }
第一個為成員函數,但是存在記憶體漏失,需要進行下面的步驟
在私有成員變數中,建立一個新的建構函式,直接將p給到str,而沒有建立新的空間;並且在加號運運算元過載進行修改使其呼叫私有的建構函式
private: String(char*p,int)//兩個引數與公有構造區分 { str = p; } public: String operator+(const String& s)const { char *p = new char(strlen(this->str) + strlen(s.str) + 1); strcpy(p, this->str); strcat(p, s.str); return String(p,1); }
這樣就解決了原本記憶體漏失的問題
接下來完成剩餘兩個等號運運算元過載
String operator+(const char* s)const { char* p = new char(strlen(this->str) + strlen(s) + 1); strcpy(p, this->str); strcat(p, s); return String(p, 1); //return *this + String(s) //上面的方式更方便,但是會構造兩個臨時物件 }
此處需要寫在類外,並且需要類內新增友元函數
friend String operator+(const char* t, const String s);
String operator+(const char* t, const String s) { char* p = new char(strlen(s.str) + strlen(t) + 1); strcpy(p, s.str); strcat(p, t); return String(p, 1); //return String(p) + s; 與上面同理,並且不需要友元函數 }
討論一個衍生問題
class String { private: char* str; public: String(const char* p = NULL) :str(NULL) { if (p != NULL) { str = new char[strlen(p) + 1]; strcpy(str, p); } else { str = new char[1]; //額外提供一個空間 *str = ' '; } } ~String() { if (str != NULL) { delete[] str; } str = NULL; } String(const String& s) { //str = s.str; 淺拷貝 是同一個空間,會造成一個空間釋放兩次 //深拷貝 str = new char[strlen(s.str)]; strcpy(str, s.str); } String& operator=(const String& s) { if (this != &s) { delete[]str; str = new char[strlen(s.str)]; strcpy(str, s.str); } return *this; } }; String fun() { String s2("456"); return s2; } int main() { String s1; s1 = fun(); return 0; }
討論此程式執行的過程總共建立了多少個物件:
主函數執行首先開闢main函數棧幀,建立s1物件,預設構造只有大小為一的空間存放“