C++字符串類的封裝你真的了解嗎
字符串類的封裝
常規(guī)代碼
頭文件
#pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> using namespace std; class MyString//字符串應該是維護著一個字符數(shù)組的,在堆區(qū) { public: MyString(const char* str);//有參構(gòu)造函數(shù) MyString(const MyString& str);//拷貝構(gòu)造函數(shù) ~MyString();//析構(gòu)函數(shù)(是需要的,因為這個類中還維護著一個指針,需要自己釋放) private: char* pString;//維護在堆區(qū)開辟的字符數(shù)組 int m_Size;//字符串長度(不統(tǒng)計\0) };
在頭文件中定義了MyString類中應該有的屬性和三個函數(shù)。(有參構(gòu)造函數(shù)接收字符串并創(chuàng)建對象)(拷貝構(gòu)造函數(shù)可以接收同類型的對象并且復制出一個新的對象)(析構(gòu)函數(shù)負責在釋放掉本對象的同時釋放掉對象中維護的指針)
函數(shù)實現(xiàn)文件
#define _CRT_SECURE_NO_WARNINGS 1 #include"myString.h"; //MyString str = "123"; MyString::MyString(const char* str)//有參構(gòu)造函數(shù),傳進去的參數(shù)是一個字符串,返回的是MyString類型的對象。 { cout<<"MyString的有參構(gòu)造函數(shù)調(diào)用"<<endl; this->pString = new char[strlen(str) + 1];//給本對象的pString準備空間。 strcpy(this->pString, str);//將傳進來帶的字符串的地址也傳給本對象的pString處,str是字符串,不能調(diào)用pString屬性。 this->m_Size = strlen(str); } MyString::MyString(const MyString& str)//拷貝構(gòu)造函數(shù) { //這個需要深拷貝,因為類中有個指針。注意:深拷貝是對指針進行的深拷貝,也就是會傳進來個對象,然后給這個對象中的指針 分配傳進來的對象中的指針的大小的空間,然后將這個值也賦值到本指針中。 cout << "MyString的拷貝函數(shù)調(diào)用" << endl; this->pString = new char[strlen(str.pString)+1]; strcpy(this->pString , str.pString); this->m_Size = str.m_Size; } MyString::~MyString()//析構(gòu)(是需要的,因為這個類中還維護著一個指針,需要自己釋放) { cout << "MyString的析構(gòu)函數(shù)調(diào)用" << endl; if (this->pString != NULL) { delete[] this->pString; this->pString = NULL; } }
拷貝構(gòu)造和有參構(gòu)造的區(qū)別就是:
1,有參構(gòu)造傳進去的是一個字符串,返回成一個對象。所以要的是對象的各個屬性與字符串的屬性的匹配。
2,拷貝構(gòu)造傳進去的是一個對象,返回的也是一個對象,所以要的是傳進來的對象和本對象(要創(chuàng)建的對象)之間屬性的對應。
3,如果拷貝構(gòu)造的時候發(fā)現(xiàn)需要拷貝個指針,那么就不能直接使用編譯器的拷貝構(gòu)造函數(shù)了,因為編譯器的拷貝構(gòu)造函數(shù)構(gòu)造出來的對象的指針是和傳進來的對象的指針是指向同一塊地方的,那么等到需要釋放指針的時候就會出現(xiàn)重釋放的錯誤。**(這也是為什么需要自己重新創(chuàng)建拷貝構(gòu)造的原因)**如果類中不需要維護指針,那么就不需要自己寫拷貝構(gòu)造(自己為指針再創(chuàng)建空間)。
Test文件
#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> using namespace std; #include "myString.h"; int main() { MyString str = "abc";//調(diào)用默認構(gòu)造 //和后面的一樣MyString str("abc"); MyString str2 = str;//調(diào)用拷貝構(gòu)造 return 0; }
注意,這里的“abc”就是傳進來的字符串str,然后這里的str就是創(chuàng)建出來的MyString類型的對象。
運行的結(jié)果:
MyString的有參構(gòu)造函數(shù)調(diào)用
MyString的拷貝函數(shù)調(diào)用
MyString的析構(gòu)函數(shù)調(diào)用
MyString的析構(gòu)函數(shù)調(diào)用
重載左移>>
如果想進行
cout<<str<<endl;
代碼肯定會報錯,因為str是MyString類型的對象,<<不認識這個。
所以,這個時候就需要重載一下<<左移運算符,可以在函數(shù)文件中實現(xiàn)。(需要用全局函數(shù)配合友元進行重載)
實現(xiàn)函數(shù):
ostream& operator<<(ostream& cout, MyString& str) //重載左移運算符 { cout << str.pString;//這里的pString是類中的私有屬性,所以需要在原類(在頭文件中)中給整個重載函數(shù)設置友元 return cout; }
頭文件:
class MyString { friend ostream& operator<<(ostream& cout, MyString& str);//設置的友元 public: private: };
這樣cout << str << endl;
這行代碼就可以調(diào)用了。
重載右移<<
如果想進行
cin>>str;
代碼肯定也會報錯,因為str是MyString類型的對象,>>不認識這個。
所以,這個時候就要重載一下>>右移運算符,可以在函數(shù)文件中實現(xiàn)。(需要用全局函數(shù)配合友元進行重載)
實現(xiàn)函數(shù):
istream& operator>>(istream& cin, MyString& str)//重載右移運算符 { //應該先清空原來的堆區(qū)數(shù)據(jù) if (str.pString)//這里的pString是對象中的私有屬性,所以需要在原類中加上這個重載函數(shù)的友元聲明 { delete[] str.pString; str.pString = NULL; } //不用急著直接將輸入的內(nèi)容傳給pString,可以先開辟臨時數(shù)組,記錄著輸入內(nèi)容。 char buf[1024]; cin >> buf; //因為是自己重載的函數(shù),剛才將str的pString刪除了,現(xiàn)在需要重新申請空間。 str.pString = new char[strlen(buf) + 1]; strcpy(str.pString, buf); str.m_Size = strlen(buf);//別忘了還要把大小考進str的size中,因為傳進來一串字符以后,對象中的長度還保持著原先的長度,所以需要進行修改。 cout << str.m_Size << endl; return cin; }
頭文件:
class MyString//字符串應該是維護著一個字符數(shù)組的,在堆區(qū) { friend istream& operator>>(istream& cin, MyString& str); public: private: };
然后就能給str的pString賦值了。
重載右移運算符的時候的清空原來字符串中的內(nèi)容好像不太重要,刪除了也能正常運行。創(chuàng)建臨時數(shù)組記錄(數(shù)組大小夠大即可),然后將賦值,最后別忘了更改對象中的size。
重載賦值=
如果想進行:
str2 = str1
直接將兩個對象進行=運行起來代碼肯定會崩,因為:全拷貝了,刪除對象的時候會出現(xiàn)淺拷貝的問題。
如果想進行:
str2 = “abc"
直接將字符串賦值給字符串肯定也是不行的。
所以需要重載兩個不同參數(shù)的 = 運算符。(一種參數(shù)是對象,一種參數(shù)是字符串)
MyString& operator=(const MyString& str); MyString& operator=(const char* str);
返回值必須要是MyString& ,因為使用完=運算符要返回的是自身(str2). 注意在頭函數(shù)中聲明完了以后到實現(xiàn)文件中去實現(xiàn)的時候要寫范圍MyString::,而且這個類的范圍需要寫在返回值類型的后面,函數(shù)名的前面。
重載=運算符,與重載左移和右移運算符不同,不用再像<<和>>一樣使用全局函數(shù)重載了,需要使用成員函數(shù)
頭文件:
#pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> using namespace std; class MyString { friend ostream& operator<<(ostream& cout, MyString& str); friend istream& operator>>(istream& cin, MyString& str); public: MyString(const char* str);//有參構(gòu)造函數(shù) MyString(const MyString& str);//拷貝構(gòu)造函數(shù) ~MyString();//析構(gòu)(是需要的,因為這個類中還維護著一個指針,需要自己釋放) //重載兩個=運算符 MyString& operator=(const MyString& str); MyString& operator=(const char* str); private: char* pString;//維護在堆區(qū)開辟的字符數(shù)組 int m_Size;//字符串長度(不統(tǒng)計\0) };
實現(xiàn)文件:
#define _CRT_SECURE_NO_WARNINGS 1 #include"myString.h"; MyString & MyString::operator=(const MyString & str) { //先判斷原堆區(qū)有沒有內(nèi)容,如果有先釋放。 if (this->pString) { delete[]this->pString; this->pString = NULL; } //進行深拷貝 this->pString = new char[strlen(str.pString) + 1]; strcpy(this->pString, str.pString); this->m_Size = strlen(str.pString); return *this; } MyString & MyString::operator=(const char* str) { //先判斷原堆區(qū)有沒有內(nèi)容,如果有先釋放。 if (this->pString) { delete[]this->pString; this->pString = NULL; } //進行深拷貝 this->pString = new char[strlen(str) + 1]; strcpy(this->pString, str); this->m_Size = strlen(str); return *this; }
Test文件:
#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> using namespace std; #include "myString.h"; int main() { MyString str = "abc";//調(diào)用默認構(gòu)造 MyString str2 = "bcd"; str = str2; MyString str3 = "abc"; cout << str << endl;//bcd cout << str3 << endl;//abc return 0; }
重載中括號[ ]
如果項進行
str2[0] = 'a';
是不可以的,因為[ ]不認識str。
所以需要重載中括號[]。直接使用成員函數(shù)進行重載。
//在頭文件中: char operator[](int index); //在實現(xiàn)函數(shù)中: char MyString::operator[](int index)//重載中括號 { return this->pString[index]; }
正常來說就返回char類型的數(shù)值就行了,這樣就可讀了。
但是如果想將str2p[1]作為運算左值來修改,那么就需要返回本體char&
//在頭文件中: char& operator[](int index); //在實現(xiàn)函數(shù)中: char& MyString::operator[](int index)//重載中括號 { return this->pString[index]; } //在Test文件中 int main() { MyString str2 = "bcd"; cout << str2[1] << endl;//c str2[1] = 'z'; cout << str2[1] << endl;//z return 0; }
重載加號+
如果想實現(xiàn):
MyString str3 = "abc"; MyString str4 = "def"; MyString str5 = str3 + str4; MyString str6 = str5+"abc";
這樣肯定會報錯,因為+不認識對象,也不認識這樣的字符串
所以,需要對+進行重載,使用的還是成員函數(shù),只有一個參數(shù)。
從題意得,傳進去一個對象,然后返回出一個對象,或者是傳進去一個字符串,返回出一個對象。(前提是將第一個傳進去的看作是調(diào)用對象)
頭文件:
MyString operator+(const MyString& str); MyString operator+(const char* str);
實現(xiàn)文件:
//重載+運算符 MyString MyString:: operator+(const MyString& str) { //本身abc,傳入的是def,剛開始應該先計算一下需要開辟的內(nèi)存空間。 int newSize = this->m_Size + strlen(str.pString) + 1; char* temp = new char[newSize];//然后將這塊空間開辟出來,temp指針指向它。 memset(temp, 0, newSize);//將空間里面的內(nèi)容全部清空。 strcat(temp, this->pString);//將this->pString扔進了temp中。 strcat(temp, str.pString);//然后再將str字符串扔進去,這樣它們就自己結(jié)合了。 //但是創(chuàng)建好的新的字符串不能直接返回,因為需要返回一個對象 //所以就創(chuàng)建一個新的對象,然后通過構(gòu)造函數(shù)將字符串賦給新對象,最后再返回新對象。 MyString newString = temp; //還有一點,創(chuàng)建的類是空間temp用完了需要釋放 delete[]temp; return newString; } MyString MyString:: operator+(const char* str) { //本身abc,傳入的是def,剛開始應該先計算一下需要開辟的內(nèi)存空間。 int newSize = this->m_Size + strlen(str) + 1; char* temp = new char[newSize];//然后將這塊空間開辟出來,temp指針指向它。 memset(temp, 0, newSize);//將空間里面的內(nèi)容全部清空。 strcat(temp, this->pString);//將this->pString扔進了temp中。 strcat(temp, str);//然后再將str字符串扔進去,這樣它們就自己結(jié)合了。 //但是創(chuàng)建好的新的字符串不能直接返回,因為需要返回一個對象 //所以就創(chuàng)建一個新的對象,然后通過構(gòu)造函數(shù)將字符串賦給新對象,最后再返回新對象。 MyString newString = temp; //還有一點,創(chuàng)建的類是空間temp用完了需要釋放 delete[]temp; return newString; }
兩種+重載函數(shù)幾乎一樣
TEST文件
int main() { MyString str3 = "abc"; MyString str4 = "def"; MyString str5 = str3 + str4; MyString str6 = str5 + "abc"; cout << str5 << endl;//abcdef cout << str6 << endl;//abcdefabc return 0; }
重載==
ps補充:strcmp函數(shù)中,如果兩個字符串相等,那么就返回0,如果不相等,那么就返回1。
也是提供兩種重載函數(shù):
//頭文件: //重載==運算符 bool operator==(const MyString& str); bool operator==(const char* str); //實現(xiàn)文件: //重載==運算符 bool MyString::operator==(const MyString & str) { if (strcmp(this->pString, str.pString) == 0) return true; else return false; } bool MyString::operator==(const char* str) { if (strcmp(this->pString, str) == 0) return true; else return false; } //Test文件 int main() { MyString str3 = "abc"; MyString str4 = "def"; MyString str5 = str3 + str4; cout << str5 << endl;//abcdef if (str5 == str5) { cout << "是相等的" << endl; } else { cout << "是不相等的" << endl; } //結(jié)果是相等的。 return 0; }
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注腳本之家的更多內(nèi)容!
相關文章
ros項目調(diào)試:vscode下配置開發(fā)ROS項目的詳細教程
這篇文章主要介紹了ros項目調(diào)試:vscode下配置開發(fā)ROS項目,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08C語言經(jīng)典算法例題求100-999之間的“水仙花數(shù)”
本文的主要內(nèi)容,設計一個程序,找出100-999之間的“水仙花數(shù)”,需要的朋友可以參考下2015-07-07C語言變長數(shù)組 struct中char data[0]的用法詳解
下面小編就為大家?guī)硪黄狢語言變長數(shù)組 struct中char data[0]的用法詳解。小編覺得挺不錯的現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01