国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【C++初階】STL詳解(二)string類的模擬實(shí)現(xiàn)

這篇具有很好參考價(jià)值的文章主要介紹了【C++初階】STL詳解(二)string類的模擬實(shí)現(xiàn)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

本專欄內(nèi)容為:C++學(xué)習(xí)專欄,分為初階和進(jìn)階兩部分。 通過(guò)本專欄的深入學(xué)習(xí),你可以了解并掌握C++。

??博主csdn個(gè)人主頁(yè):小小unicorn
?專欄分類:C++
??代碼倉(cāng)庫(kù):小小unicorn的代碼倉(cāng)庫(kù)??
??????關(guān)注我?guī)銓W(xué)習(xí)編程知識(shí)

string各函數(shù)接口總覽

namespace NIC
{
	//模擬實(shí)現(xiàn)string類
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		//默認(rèn)成員函數(shù)
		string(const char* str = "");         //構(gòu)造函數(shù)
		string(const string& s);              //拷貝構(gòu)造函數(shù)
		string& operator=(const string& s);   //賦值運(yùn)算符重載函數(shù)
		~string();                            //析構(gòu)函數(shù)

		//迭代器相關(guān)函數(shù)
		iterator begin();
		iterator end();
		const_iterator begin()const;
		const_iterator end()const;

		//容量和大小相關(guān)函數(shù)
		size_t size();
		size_t capacity();
		void reserve(size_t n);
		void resize(size_t n, char ch = '\0');
		bool empty()const;

		//修改字符串相關(guān)函數(shù)
		void push_back(char ch);
		void append(const char* str);
		string& operator+=(char ch);
		string& operator+=(const char* str);
		string& insert(size_t pos, char ch);
		string& insert(size_t pos, const char* str);
		string& erase(size_t pos, size_t len);
		void clear();
		void swap(string& s);
		const char* c_str()const;

		//訪問(wèn)字符串相關(guān)函數(shù)
		char& operator[](size_t i);
		const char& operator[](size_t i)const;
		size_t find(char ch, size_t pos = 0)const;
		size_t find(const char* str, size_t pos = 0)const;
		size_t rfind(char ch, size_t pos = npos)const;
		size_t rfind(const char* str, size_t pos = 0)const;

		//關(guān)系運(yùn)算符重載函數(shù)
		bool operator>(const string& s)const;
		bool operator>=(const string& s)const;
		bool operator<(const string& s)const;
		bool operator<=(const string& s)const;
		bool operator==(const string& s)const;
		bool operator!=(const string& s)const;

	private:
		char* _str;       //存儲(chǔ)字符串
		size_t _size;     //記錄字符串當(dāng)前的有效長(zhǎng)度
		size_t _capacity; //記錄字符串當(dāng)前的容量
		static const size_t npos; //靜態(tài)成員變量(整型最大值)
	};
	const size_t string::npos = -1;

	//<<和>>運(yùn)算符重載函數(shù)
	istream& operator>>(istream& in, string& s);
	ostream& operator<<(ostream& out, const string& s);
	istream& getline(istream& in, string& s);
}

注:為了防止與標(biāo)準(zhǔn)庫(kù)當(dāng)中的string類產(chǎn)生命名沖突,模擬實(shí)現(xiàn)時(shí)需放在自己的命名空間當(dāng)中。

默認(rèn)成員函數(shù):

構(gòu)造函數(shù)

構(gòu)造函數(shù)設(shè)置為缺省參數(shù),若不傳入?yún)?shù),則默認(rèn)構(gòu)造為空字符串。字符串的初始大小和容量均設(shè)置為傳入C字符串的長(zhǎng)度(不包括’\0’)

//構(gòu)造函數(shù)
string(const char* str = "")
	:_size(strlen(str))//初始時(shí),字符串大小設(shè)置為字符串長(zhǎng)度
	, _capacity(_size)///初始時(shí),字符串容量設(shè)置為字符串長(zhǎng)度
{
	_str = new char[_capacity + 1];//為存儲(chǔ)字符開(kāi)辟空間(多開(kāi)一個(gè)用于存放'\0')
	strcpy(_str, str);//將字符串拷貝到已經(jīng)開(kāi)好的空間
}

拷貝構(gòu)造函數(shù)

在模擬實(shí)現(xiàn)拷貝之前,先了解一下深淺拷貝問(wèn)題:

淺拷貝:拷貝出來(lái)的目標(biāo)對(duì)象的指針和源對(duì)象的指針指向的內(nèi)存空間是同一塊空間。其中一個(gè)對(duì)象的改動(dòng)會(huì)對(duì)另一個(gè)對(duì)象造成影響。

深拷貝:深拷貝是指源對(duì)象與拷貝對(duì)象互相獨(dú)立。其中任何一個(gè)對(duì)象的改動(dòng)不會(huì)對(duì)另外一個(gè)對(duì)象造成影響。

很明顯,我們并不希望拷貝出來(lái)的兩個(gè)對(duì)象之間存在相互影響,因此,我們這里需要用到深拷貝。下面提供深拷貝的兩種寫法:

1.傳統(tǒng)寫法:

傳統(tǒng)寫法的思想:先開(kāi)辟一塊足以容納源對(duì)象字符串的空間,然后將源對(duì)象的字符串拷貝過(guò)去,接著把源對(duì)象的其他成員變量也賦值過(guò)去即可。因?yàn)榭截悓?duì)象的_str與源對(duì)象的_str指向的并不是同一塊空間,所以拷貝出來(lái)的對(duì)象與源對(duì)象是互相獨(dú)立的。

// 傳統(tǒng)寫法
// s2(s1)
string(const string& s)
{
	_str = new char[s._capacity+1];
	strcpy(_str, s._str);//將s._str拷貝一份到_str
	_size = s._size;//_size賦值
	_capacity = s._capacity;//_capacity賦值
}

2.現(xiàn)代寫法

【C++初階】STL詳解(二)string類的模擬實(shí)現(xiàn),c++,c++,java,開(kāi)發(fā)語(yǔ)言,string

現(xiàn)代寫法與傳統(tǒng)寫法的思想不同:先根據(jù)源字符串的C字符串調(diào)用構(gòu)造函數(shù)構(gòu)造一個(gè)tmp對(duì)象,然后再將tmp對(duì)象與拷貝對(duì)象的數(shù)據(jù)交換即可。拷貝對(duì)象的_str與源對(duì)象的_str指向的也不是同一塊空間,是互相獨(dú)立的。

//現(xiàn)代寫法
// s2(s1)
string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	string tmp(s._str);//調(diào)用構(gòu)造函數(shù)
	swap(tmp);//交換這兩個(gè)對(duì)象
}

注:swap成員函數(shù)的模擬實(shí)現(xiàn)在文章的后面。

析構(gòu)函數(shù)

string類的析構(gòu)函數(shù)需要我們進(jìn)行編寫,因?yàn)槊總€(gè)string對(duì)象中的成員_str都指向堆區(qū)的一塊空間,當(dāng)對(duì)象銷毀時(shí)堆區(qū)對(duì)應(yīng)的空間并不會(huì)自動(dòng)銷毀,為了避免內(nèi)存泄漏,我們需要使用delete手動(dòng)釋放堆區(qū)的空間。

//析構(gòu)函數(shù)
~string()
{
	delete[] _str;  //釋放_(tái)str指向的空間
	_str = nullptr; //及時(shí)置空,防止非法訪問(wèn)
	_size = 0;      //大小置0
	_capacity = 0;  //容量置0
}

賦值運(yùn)算符重載函數(shù)

與拷貝構(gòu)造函數(shù)類似,賦值運(yùn)算符重載函數(shù)的模擬實(shí)現(xiàn)也涉及深淺拷貝問(wèn)題,我們同樣需要采用深拷貝。下面也提供深拷貝的兩種寫法:

1.傳統(tǒng)寫法

賦值運(yùn)算符重載函數(shù)的傳統(tǒng)寫法與拷貝構(gòu)造函數(shù)的傳統(tǒng)寫法幾乎相同,只是左值的_str在開(kāi)辟新空間之前需要先將原來(lái)的空間釋放掉,并且在進(jìn)行操作之前還需判斷是否是自己給自己賦值,若是自己給自己賦值,則無(wú)需進(jìn)行任何操作。

//傳統(tǒng)寫法
string& operator=(const string& s)
{
	if (this != &s) //防止自己給自己賦值
	{
		delete[] _str; //將原來(lái)_str指向的空間釋放
		_str = new char[s._capacity + 1]; //重新申請(qǐng)一塊空間
		strcpy(_str, s._str);    //將s._str拷貝一份到_str
		_size = s._size;         //_size賦值
		_capacity = s._capacity; //_capacity賦值
	}
	return *this; //返回左值(支持連續(xù)賦值)
}

1.現(xiàn)代寫法

賦值運(yùn)算符重載函數(shù)的現(xiàn)代寫法與拷貝構(gòu)造函數(shù)的現(xiàn)代寫法也是非常類似,但拷貝構(gòu)造函數(shù)的現(xiàn)代寫法是通過(guò)代碼語(yǔ)句調(diào)用構(gòu)造函數(shù)構(gòu)造出一個(gè)對(duì)象,然后將該對(duì)象與拷貝對(duì)象交換;而賦值運(yùn)算符重載函數(shù)的現(xiàn)代寫法是通過(guò)采用“值傳遞”接收右值的方法,讓編譯器自動(dòng)調(diào)用拷貝構(gòu)造函數(shù),然后我們?cè)賹⒖截惓鰜?lái)的對(duì)象與左值進(jìn)行交換即可。

//現(xiàn)代寫法1
string& operator=(string s) //編譯器接收右值的時(shí)候自動(dòng)調(diào)用拷貝構(gòu)造函數(shù)
{
	swap(s); //交換這兩個(gè)對(duì)象
	return *this; //返回左值(支持連續(xù)賦值)
}

但這種寫法無(wú)法避免自己給自己賦值,就算是自己給自己賦值這些操作也會(huì)進(jìn)行,雖然操作之后對(duì)象中_str指向的字符串的內(nèi)容不變,但是字符串存儲(chǔ)的地址發(fā)生了改變,為了避免這種操作我們可以采用下面這種寫法:

//現(xiàn)代寫法2
string& operator=(const string& s)
{
	if (this != &s) //防止自己給自己賦值
	{
		string tmp(s); //用s拷貝構(gòu)造出對(duì)象tmp
		swap(tmp); //交換這兩個(gè)對(duì)象
	}
	return *this; //返回左值(支持連續(xù)賦值)
}

但實(shí)際中很少出現(xiàn)自己給自己賦值的情況,所以采用“現(xiàn)代寫法1”就行了。

迭代器相關(guān)函數(shù)

string類中的迭代器可以認(rèn)為是就是字符指針(原生指針),只是給字符指針起了一個(gè)別名叫iterator而已。但并不是說(shuō)所有迭代器都是指針。

typedef char* iterator;
typedef const char* const_iterator;

begin與end

string類中的begin和end函數(shù)的實(shí)現(xiàn)簡(jiǎn)單,begin函數(shù)的作用就是返回字符串中第一個(gè)字符的地址:

iterator begin()
{
	return _str; //返回字符串中第一個(gè)字符的地址
}
const_iterator begin()const
{
	return _str; //返回字符串中第一個(gè)字符的const地址
}

end函數(shù)的作用就是返回字符串中最后一個(gè)字符的后一個(gè)字符的地址(即’\0’的地址):

iterator end()
{
	return _str + _size; //返回字符串中最后一個(gè)字符的后一個(gè)字符的地址
}
const_iterator end()const
{
	return _str + _size; //返回字符串中最后一個(gè)字符的后一個(gè)字符的const地址
}

在明白了string類中迭代器的底層實(shí)現(xiàn),再來(lái)看看我們用迭代器遍歷string的代碼,其實(shí)就是用指針在遍歷字符串而已。

string s("hello world!!!");
string::iterator it = s.begin();
while (it != s.end())
{
	cout << *it << " ";
	it++;
}
cout << endl;

在string介紹中我們還說(shuō)到,可以用范圍for來(lái)遍歷string,可能很多初學(xué)者都會(huì)覺(jué)得范圍for是個(gè)很神奇的東西,只需要一點(diǎn)點(diǎn)代碼就能實(shí)現(xiàn)string的遍歷。

實(shí)際上范圍for并不神奇,因?yàn)樵诖a編譯的時(shí)候,編譯器會(huì)自動(dòng)將范圍for替換為迭代器的形式,也就是說(shuō)范圍for是由迭代器支持的,現(xiàn)在我們已經(jīng)實(shí)現(xiàn)了string類的迭代器,自然也能用范圍for對(duì)string進(jìn)行遍歷:

string s("hello world!!!");
//編譯器將其替換為迭代器形式
for (auto e : s)
{
	cout << e << " ";
}
cout << endl;

容量相關(guān):

size和capacity

因?yàn)閟tring類的成員變量是私有的,我們并不能直接對(duì)其進(jìn)行訪問(wèn),所以string類設(shè)置了size和capacity這兩個(gè)成員函數(shù),用于獲取string對(duì)象的大小和容量。

size函數(shù)用于獲取字符串當(dāng)前的有效長(zhǎng)度(不包括’\0’)。

//大小
size_t size()const
{
	return _size; //返回字符串當(dāng)前的有效長(zhǎng)度
}

capacity函數(shù)用于獲取字符串當(dāng)前的容量。

//容量
size_t capacity()const
{
	return _capacity; //返回字符串當(dāng)前的容量
}

reserve與resize

reserve和resize這兩個(gè)函數(shù)的執(zhí)行規(guī)則一定要區(qū)分清楚。

reserve規(guī)則:
?1、當(dāng)n大于對(duì)象當(dāng)前的capacity時(shí),將capacity擴(kuò)大到n或大于n。
?2、當(dāng)n小于對(duì)象當(dāng)前的capacity時(shí),什么也不做。

//改變?nèi)萘?,大小不?/span>
void reserve(size_t n)
{
	if (n > _capacity) //當(dāng)n大于對(duì)象當(dāng)前容量時(shí)才需執(zhí)行操作
	{
		char* tmp = new char[n + 1]; //多開(kāi)一個(gè)空間用于存放'\0'
		strncpy(tmp, _str, _size + 1); //將對(duì)象原本的C字符串拷貝過(guò)來(lái)(包括'\0')
		delete[] _str; //釋放對(duì)象原本的空間
		_str = tmp; //將新開(kāi)辟的空間交給_str
		_capacity = n; //容量跟著改變
	}
}

注意:代碼中使用strncpy進(jìn)行拷貝對(duì)象C字符串而不是strcpy,是為了防止對(duì)象的C字符串中含有有效字符’\0’而無(wú)法拷貝(strcpy拷貝到第一個(gè)’\0’就結(jié)束拷貝了)。
【C++初階】STL詳解(二)string類的模擬實(shí)現(xiàn),c++,c++,java,開(kāi)發(fā)語(yǔ)言,string
resize規(guī)則:
?1、當(dāng)n大于當(dāng)前的size時(shí),將size擴(kuò)大到n,擴(kuò)大的字符為ch,若ch未給出,則默認(rèn)為’\0’。
?2、當(dāng)n小于當(dāng)前的size時(shí),將size縮小到n。

//改變大小
void resize(size_t n, char ch = '\0')
{
	if (n <= _size) //n小于當(dāng)前size
	{
		_size = n; //將size調(diào)整為n
		_str[_size] = '\0'; //在size個(gè)字符后放上'\0'
	}
	else //n大于當(dāng)前的size
	{
		if (n > _capacity) //判斷是否需要擴(kuò)容
		{
			reserve(n); //擴(kuò)容
		}
		for (size_t i = _size; i < n; i++) //將size擴(kuò)大到n,擴(kuò)大的字符為ch
		{
			_str[i] = ch;
		}
		_size = n; //size更新
		_str[_size] = '\0'; //字符串后面放上'\0'
	}
}

empty

empty是string的判空函數(shù),我們可以調(diào)用strcmp函數(shù)來(lái)實(shí)現(xiàn),strcmp函數(shù)是用于比較兩個(gè)字符串大小的函數(shù),當(dāng)兩個(gè)字符串相等時(shí)返回0。

//判空
bool empty()
{
	return strcmp(_str, "") == 0;
}

注意:兩個(gè)字符串相比較千萬(wàn)不能用 == 。

訪問(wèn)字符串相關(guān)函數(shù)

operator[ ]

[ ]運(yùn)算符的重載是為了讓string對(duì)象能像C字符串一樣,通過(guò)[ ] +下標(biāo)的方式獲取字符串對(duì)應(yīng)位置的字符。

在C字符串中我們通過(guò)[ ] +下標(biāo)的方式可以獲取字符串對(duì)應(yīng)位置的字符,并可以對(duì)其進(jìn)行修改,實(shí)現(xiàn)[ ] 運(yùn)算符的重載時(shí)只需返回對(duì)象C字符串對(duì)應(yīng)位置字符的引用即可,這樣便能實(shí)現(xiàn)對(duì)該位置的字符進(jìn)行讀取和修改操作了,但需要注意在此之前檢測(cè)所給下標(biāo)的合法性。

//[]運(yùn)算符重載(可讀可寫)
char& operator[](size_t i)
{
	assert(i < _size); //檢測(cè)下標(biāo)的合法性
	return _str[i]; //返回對(duì)應(yīng)字符
}

在某些場(chǎng)景下,我們可能只能用[ ] +下標(biāo)的方式讀取字符而不能對(duì)其進(jìn)行修改。

例如,對(duì)一個(gè)const的string類對(duì)象進(jìn)行[ ] +下標(biāo)的操作,我們只能讀取所得到的字符,而不能對(duì)其進(jìn)行修改。所以我們需要再重載一個(gè)[ ] 運(yùn)算符,用于只讀操作。

//[]運(yùn)算符重載(只讀)
const char& operator[](size_t i)const
{
	assert(i < _size); //檢測(cè)下標(biāo)的合法性
	return _str[i]; //返回對(duì)應(yīng)字符
}

find和rfind

find函數(shù)和rfind函數(shù)都是用于在字符串中查找一個(gè)字符或是字符串,find函數(shù)和rfind函數(shù)分別用于正向查找和反向查找,即從字符串開(kāi)頭開(kāi)始向后查找和從字符串末尾開(kāi)始向前查找。

find函數(shù):
1、正向查找第一個(gè)匹配的字符。

首先判斷所給pos的合法性,然后通過(guò)遍歷的方式從pos位置開(kāi)始向后尋找目標(biāo)字符,若找到,則返回其下標(biāo);若沒(méi)有找到,則返回npos。(npos是string類的一個(gè)靜態(tài)成員變量,其值為整型最大值)

//正向查找第一個(gè)匹配的字符
size_t find(char ch, size_t pos = 0)
{
	assert(pos < _size); //檢測(cè)下標(biāo)的合法性
	for (size_t i = pos; i < _size; i++) //從pos位置開(kāi)始向后尋找目標(biāo)字符
	{
		if (_str[i] == ch)
		{
			return i; //找到目標(biāo)字符,返回其下標(biāo)
		}
	}
	return npos; //沒(méi)有找到目標(biāo)字符,返回npos
}

2、正向查找第一個(gè)匹配的字符串。

首先也是先判斷所給pos的合法性,然后我們可以通過(guò)調(diào)用strstr函數(shù)進(jìn)行查找。strstr函數(shù)若是找到了目標(biāo)字符串會(huì)返回字符串的起始位置,若是沒(méi)有找到會(huì)返回一個(gè)空指針。若是找到了目標(biāo)字符串,我們可以通過(guò)計(jì)算目標(biāo)字符串的起始位置和對(duì)象C字符串的起始位置的差值,進(jìn)而得到目標(biāo)字符串起始位置的下標(biāo)。

//正向查找第一個(gè)匹配的字符串
size_t find(const char* str, size_t pos = 0)
{
	assert(pos < _size); //檢測(cè)下標(biāo)的合法性
	const char* ret = strstr(_str + pos, str); //調(diào)用strstr進(jìn)行查找
	if (ret) //ret不為空指針,說(shuō)明找到了
	{
		return ret - _str; //返回字符串第一個(gè)字符的下標(biāo)
	}
	else //沒(méi)有找到
	{
		return npos; //返回npos
	}
}

rfind函數(shù):

實(shí)現(xiàn)rfind函數(shù)時(shí),我們可以考慮復(fù)用已經(jīng)寫好了的兩個(gè)find函數(shù),但rfind函數(shù)是從后先前找,所以我們需要將對(duì)象的C字符串逆置一下,若是查找字符串,還需將待查找的字符串逆置一下,然后調(diào)用find函數(shù)進(jìn)行查找,但注意傳入find函數(shù)的pos以及從find函數(shù)接收到的pos都需要鏡像對(duì)稱一下。

1、反向查找第一個(gè)匹配的字符。
首先我們需要用對(duì)象拷貝構(gòu)造一個(gè)臨時(shí)對(duì)象tmp,因?yàn)槲覀儾⒉幌M{(diào)用rfind函數(shù)后對(duì)象的C字符串就被逆置了。我們將tmp對(duì)象的C字符串逆置,然后將所給pos鏡像對(duì)稱一下再調(diào)用find函數(shù),再將從find函數(shù)接收到的返回值鏡像對(duì)稱一下作為rfind函數(shù)的返回值返回即可。

//反向查找第一個(gè)匹配的字符
size_t rfind(char ch, size_t pos = npos)
{
	string tmp(*this); //拷貝構(gòu)造對(duì)象tmp
	reverse(tmp.begin(), tmp.end()); //調(diào)用reverse逆置對(duì)象tmp的C字符串
	if (pos >= _size) //所給pos大于字符串有效長(zhǎng)度
	{
		pos = _size - 1; //重新設(shè)置pos為字符串最后一個(gè)字符的下標(biāo)
	}
	pos = _size - 1 - pos; //將pos改為鏡像對(duì)稱后的位置
	size_t ret = tmp.find(ch, pos); //復(fù)用find函數(shù)
	if (ret != npos)
		return _size - 1 - ret; //找到了,返回ret鏡像對(duì)稱后的位置
	else
		return npos; //沒(méi)找到,返回npos
}

注:rfind函數(shù)規(guī)定,當(dāng)所給的pos大于等于字符串的有效長(zhǎng)度時(shí),看作所給pos為字符串最后一個(gè)字符的下標(biāo)。

2、反向查找第一個(gè)匹配的字符串。

首先我們還是需要用對(duì)象拷貝構(gòu)造一個(gè)臨時(shí)對(duì)象tmp,然后將tmp對(duì)象的C字符串逆置,同時(shí)我們還需要拷貝一份待查找的字符串,也將其逆置。然后將所給pos鏡像對(duì)稱一下再調(diào)用find函數(shù)。注意:此時(shí)我們將從find函數(shù)接收到的值鏡面對(duì)稱后,得到的是待查找字符串的最后一個(gè)字符在對(duì)象C字符串中的位置,而我們需要返回的是待查找字符串在對(duì)象C字符串中的第一個(gè)字符的位置,所以還需做進(jìn)一步調(diào)整后才能作為rfind函數(shù)的返回值返回。
【C++初階】STL詳解(二)string類的模擬實(shí)現(xiàn),c++,c++,java,開(kāi)發(fā)語(yǔ)言,string

//反向查找第一個(gè)匹配的字符串
size_t rfind(const char* str, size_t pos = npos)
{
	string tmp(*this); //拷貝構(gòu)造對(duì)象tmp
	reverse(tmp.begin(), tmp.end()); //調(diào)用reverse逆置對(duì)象tmp的C字符串
	size_t len = strlen(str); //待查找的字符串的長(zhǎng)度
	char* arr = new char[len + 1]; //開(kāi)辟arr字符串(用于拷貝str字符串)
	strcpy(arr, str); //拷貝str給arr
	size_t left = 0, right = len - 1; //設(shè)置左右指針
	//逆置字符串a(chǎn)rr
	while (left < right)
	{
		::swap(arr[left], arr[right]);
		left++;
		right--;
	}
	if (pos >= _size) //所給pos大于字符串有效長(zhǎng)度
	{
		pos = _size - 1; //重新設(shè)置pos為字符串最后一個(gè)字符的下標(biāo)
	}
	pos = _size - 1 - pos; //將pos改為鏡像對(duì)稱后的位置
	size_t ret = tmp.find(arr, pos); //復(fù)用find函數(shù)
	delete[] arr; //銷毀arr指向的空間,避免內(nèi)存泄漏
	if (ret != npos)
		return _size - ret - len; //找到了,返回ret鏡像對(duì)稱后再調(diào)整的位置
	else
		return npos; //沒(méi)找到,返回npos
}

修改字符串相關(guān)函數(shù)

push_back

push_back函數(shù)的作用就是在當(dāng)前字符串的后面尾插上一個(gè)字符,尾插之前首先需要判斷是否需要增容,若需要,則調(diào)用reserve函數(shù)進(jìn)行增容,然后再尾插字符,注意尾插完字符后需要在該字符的后方設(shè)置上’\0’,否則打印字符串的時(shí)候會(huì)出現(xiàn)非法訪問(wèn),因?yàn)槲膊宓淖址蠓讲灰欢ň褪恰痋0’

//尾插字符
void push_back(char ch)
{
	if (_size == _capacity) //判斷是否需要增容
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2); //將容量擴(kuò)大為原來(lái)的兩倍
	}
	_str[_size] = ch; //將字符尾插到字符串
	_str[_size + 1] = '\0'; //字符串后面放上'\0'
	_size++; //字符串的大小加一
}

實(shí)現(xiàn)push_back還可以直接復(fù)用下面即將實(shí)現(xiàn)的insert函數(shù)。

//尾插字符
void push_back(char ch)
{
	insert(_size, ch); //在字符串末尾插入字符ch
}

append

append函數(shù)的作用是在當(dāng)前字符串的后面尾插一個(gè)字符串,尾插前需要判斷當(dāng)前字符串的空間能否容納下尾插后的字符串,若不能,則需要先進(jìn)行增容,然后再將待尾插的字符串尾插到對(duì)象的后方,因?yàn)榇膊宓淖址蠓阶陨韼в小痋0’,所以我們無(wú)需再在后方設(shè)置’\0’。

//尾插字符串
void append(const char* str)
{
	size_t len = _size + strlen(str); //尾插str后字符串的大?。ú话?\0')
	if (len > _capacity) //判斷是否需要增容
	{
		reserve(len); //增容
	}
	strcpy(_str + _size, str); //將str尾插到字符串后面
	_size = len; //字符串大小改變
}

實(shí)現(xiàn)append函數(shù)也可以直接復(fù)用下面即將實(shí)現(xiàn)的insert函數(shù)。

//尾插字符串
void append(const char* str)
{
	insert(_size, str); //在字符串末尾插入字符串str
}

operator+=

+=運(yùn)算符的重載是為了實(shí)現(xiàn)字符串與字符、字符串與字符串之間能夠直接使用+=運(yùn)算符進(jìn)行尾插。
+=運(yùn)算符實(shí)現(xiàn)字符串與字符之間的尾插直接調(diào)用push_back函數(shù)即可。

//+=運(yùn)算符重載
string& operator+=(char ch)
{
	push_back(ch); //尾插字符串
	return *this; //返回左值(支持連續(xù)+=)
}

+=運(yùn)算符實(shí)現(xiàn)字符串與字符串之間的尾插直接調(diào)用append函數(shù)即可。

//+=運(yùn)算符重載
string& operator+=(const char* str)
{
	append(str); //尾插字符串
	return *this; //返回左值(支持連續(xù)+=)
}

insert

insert函數(shù)的作用是在字符串的任意位置插入字符或是字符串。

insert函數(shù)用于插入字符時(shí),首先需要判斷pos的合法性,若不合法則無(wú)法進(jìn)行操作,緊接著還需判斷當(dāng)前對(duì)象能否容納插入字符后的字符串,若不能則還需調(diào)用reserve函數(shù)進(jìn)行擴(kuò)容。插入字符的過(guò)程也是比較簡(jiǎn)單的,先將pos位置及其后面的字符統(tǒng)一向后挪動(dòng)一位,給待插入的字符留出位置,然后將字符插入字符串即可。

//在pos位置插入字符
string& insert(size_t pos, char ch)
{
	assert(pos <= _size); //檢測(cè)下標(biāo)的合法性
	if (_size == _capacity) //判斷是否需要增容
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2); //將容量擴(kuò)大為原來(lái)的兩倍
	}
	char* end = _str + _size;
	//將pos位置及其之后的字符向后挪動(dòng)一位
	while (end >= _str + pos)
	{
		*(end + 1) = *(end);
		end--;
	}
	_str[pos] = ch; //pos位置放上指定字符
	_size++; //size更新
	return *this;
}

insert函數(shù)用于插入字符串時(shí),首先也是判斷pos的合法性,若不合法則無(wú)法進(jìn)行操作,再判斷當(dāng)前對(duì)象能否容納插入該字符串后的字符串,若不能則還需調(diào)用reserve函數(shù)進(jìn)行擴(kuò)容。插入字符串時(shí),先將pos位置及其后面的字符統(tǒng)一向后挪動(dòng)len位(len為待插入字符串的長(zhǎng)度),給待插入的字符串留出位置,然后將其插入字符串即可。

//在pos位置插入字符串
string& insert(size_t pos, const char* str)
{
	assert(pos <= _size); //檢測(cè)下標(biāo)的合法性
	size_t len = strlen(str); //計(jì)算需要插入的字符串的長(zhǎng)度(不含'\0')
	if (len + _size > _capacity) //判斷是否需要增容
	{
		reserve(len + _size); //增容
	}
	char* end = _str + _size;
	//將pos位置及其之后的字符向后挪動(dòng)len位
	while (end >= _str + pos)
	{
		*(end + len) = *(end);
		end--;
	}
	strncpy(_str + pos, str, len); //pos位置開(kāi)始放上指定字符串
	_size += len; //size更新
	return *this;
}

注意:插入字符串的時(shí)候使用strncpy,不能使用strcpy,否則會(huì)將待插入的字符串后面的’\0’也插入到字符串中。

erase

erase函數(shù)的作用是刪除字符串任意位置開(kāi)始的n個(gè)字符。刪除字符前也需要判斷pos的合法性,進(jìn)行刪除操作的時(shí)候分兩種情況:

1、pos位置及其之后的有效字符都需要被刪除。
這時(shí)我們只需在pos位置放上’\0’,然后將對(duì)象的size更新即可。

2、pos位置及其之后的有效字符只需刪除一部分。
這時(shí)我們可以用后方需要保留的有效字符覆蓋前方需要?jiǎng)h除的有效字符,此時(shí)不用在字符串后方加’\0’,因?yàn)樵诖酥白址┪簿陀小痋0’了。

//刪除pos位置開(kāi)始的len個(gè)字符
string& erase(size_t pos, size_t len = npos)
{
	assert(pos < _size); //檢測(cè)下標(biāo)的合法性
	size_t n = _size - pos; //pos位置及其后面的有效字符總數(shù)
	if (len >= n) //說(shuō)明pos位置及其后面的字符都被刪除
	{
		_size = pos; //size更新
		_str[_size] = '\0'; //字符串后面放上'\0'
	}
	else //說(shuō)明pos位置及其后方的有效字符需要保留一部分
	{
		strcpy(_str + pos, _str + pos + len); //用需要保留的有效字符覆蓋需要?jiǎng)h除的有效字符
		_size -= len; //size更新
	}
	return *this;
}

clear函數(shù)用于將對(duì)象中存儲(chǔ)的字符串置空,實(shí)現(xiàn)時(shí)直接將對(duì)象的_size置空,然后在字符串后面放上’\0’即可。

//清空字符串
void clear()
{
	_size = 0; //size置空
	_str[_size] = '\0'; //字符串后面放上'\0'
}

swap

swap函數(shù)用于交換兩個(gè)對(duì)象的數(shù)據(jù),直接調(diào)用庫(kù)里的swap模板函數(shù)將對(duì)象的各個(gè)成員變量進(jìn)行交換即可。

但我們?nèi)羰窍朐谶@里調(diào)用庫(kù)里的swap模板函數(shù),需要在swap函數(shù)之前加上“::”(作用域限定符),告訴編譯器優(yōu)先在全局范圍尋找swap函數(shù),否則編譯器編譯時(shí)會(huì)認(rèn)為你調(diào)用的是正在實(shí)現(xiàn)的swap函數(shù)(就近原則)。

void swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

c_str

c_str函數(shù)用于獲取對(duì)象C類型的字符串,實(shí)現(xiàn)時(shí)直接返回對(duì)象的成員變量_str即可。

//返回C類型的字符串
const char* c_str()const
{
	return _str;
}

關(guān)系運(yùn)算符重載函數(shù)

關(guān)系運(yùn)算符有 >、>=、<、<=、==、!= 這六個(gè),但是對(duì)于C++中任意一個(gè)類的關(guān)系運(yùn)算符重載,我們均只需重載其中的兩個(gè),剩下的四個(gè)關(guān)系運(yùn)算符可以通過(guò)復(fù)用已經(jīng)重載好了的兩個(gè)關(guān)系運(yùn)算符來(lái)實(shí)現(xiàn)。

例如,對(duì)于string類,我們可以選擇只重載 > 和 == 這兩個(gè)關(guān)系運(yùn)算符

//>運(yùn)算符重載
bool operator>(const string& s)const
{
	return strcmp(_str, s._str) > 0;
}
//==運(yùn)算符重載
bool operator==(const string& s)const
{
	return strcmp(_str, s._str) == 0;
}

剩下的四個(gè)關(guān)系運(yùn)算符的重載,就可以通過(guò)復(fù)用這兩個(gè)已經(jīng)重載好了的關(guān)系運(yùn)算符來(lái)實(shí)現(xiàn)了。

//>=運(yùn)算符重載
bool operator>=(const string& s)const
{
	return (*this > s) || (*this == s);
}
//<運(yùn)算符重載
bool operator<(const string& s)const
{
	return !(*this >= s);
}
//<=運(yùn)算符重載
bool operator<=(const string& s)const
{
	return !(*this > s);
}
//!=運(yùn)算符重載
bool operator!=(const string& s)const
{
	return !(*this == s);
}

>>和<<運(yùn)算符的重載以及getline函數(shù)

>>運(yùn)算符的重載

重載>>運(yùn)算符是為了讓string對(duì)象能夠像內(nèi)置類型一樣使用>>運(yùn)算符直接輸入。輸入前我們需要先將對(duì)象的C字符串置空,然后從標(biāo)準(zhǔn)輸入流讀取字符,直到讀取到’ ‘或是’\n’便停止讀取。

//>>運(yùn)算符的重載
istream& operator>>(istream& in, string& s)
{
	s.clear(); //清空字符串
	char ch = in.get(); //讀取一個(gè)字符
	while (ch != ' '&&ch != '\n') //當(dāng)讀取到的字符不是空格或'\n'的時(shí)候繼續(xù)讀取
	{
		s += ch; //將讀取到的字符尾插到字符串后面
		ch = in.get(); //繼續(xù)讀取字符
	}
	return in; //支持連續(xù)輸入
}

<<運(yùn)算符的重載

重載<<運(yùn)算符是為了讓string對(duì)象能夠像內(nèi)置類型一樣使用<<運(yùn)算符直接輸出打印。實(shí)現(xiàn)時(shí)我們可以直接使用范圍for對(duì)對(duì)象進(jìn)行遍歷即可。

//<<運(yùn)算符的重載
ostream& operator<<(ostream& out, const string& s)
{
	//使用范圍for遍歷字符串并輸出
	for (auto e : s)
	{
		cout << e;
	}
	return out; //支持連續(xù)輸出
}

getline

getline函數(shù)用于讀取一行含有空格的字符串。實(shí)現(xiàn)時(shí)于>>運(yùn)算符的重載基本相同,只是當(dāng)讀取到’\n’的時(shí)候才停止讀取字符。

//讀取一行含有空格的字符串
istream& getline(istream& in, string& s)
{
	s.clear(); //清空字符串
	char ch = in.get(); //讀取一個(gè)字符
	while (ch != '\n') //當(dāng)讀取到的字符不是'\n'的時(shí)候繼續(xù)讀取
	{
		s += ch; //將讀取到的字符尾插到字符串后面
		ch = in.get(); //繼續(xù)讀取字符
	}
	return in;
}

測(cè)試相關(guān)接口函數(shù):

測(cè)試1.對(duì)元素的訪問(wèn):迭代器.c_str.與運(yùn)算符重載[]

示例:
將字符串s1按照[]訪問(wèn),按照迭代器訪問(wèn),按照范圍for訪問(wèn)。

void test_string1()
{

	string s1("hello world");
	cout << s1.c_str() << endl;

	string s2;
	cout << s2.c_str() << endl;

	//訪問(wèn)字符串
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i] ;
	}
	cout << endl;

	//迭代器
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		//寫
		(*it)++;
		//讀
		cout << *it << " ";
		++it;
	}
	cout << endl;
	//范圍for
	for (auto& ch : s1)
	{
		ch++;
		cout << ch << " ";
	}
	cout << endl;
	cout << s1.c_str() << endl;
}

測(cè)試結(jié)果:
【C++初階】STL詳解(二)string類的模擬實(shí)現(xiàn),c++,c++,java,開(kāi)發(fā)語(yǔ)言,string

測(cè)試2.修改字符串

示例:
給s1字符串插入其他字符串,用push_back與+=

void test_string2()
{
	string s1("hello world");
	cout << s1.c_str() << endl;
	s1.push_back(' ');
	s1.append("hello bit hello bit");

	cout << s1.c_str() << endl;

	s1 += '#';
	s1 += "*********************";
	cout << s1.c_str() << endl;

	string s2;
	s2 += '#';
	s2 += "*********************";
	cout << s2.c_str() << endl;
}

測(cè)試結(jié)果:
【C++初階】STL詳解(二)string類的模擬實(shí)現(xiàn),c++,c++,java,開(kāi)發(fā)語(yǔ)言,string

測(cè)試3.insert

示例:
測(cè)試insert

void test_string3()
{
	string s1("hello world");
	cout << s1.c_str() << endl;

	s1.insert(5, '%');
	cout << s1.c_str() << endl;

	s1.insert(s1.size(), '%');
	cout << s1.c_str() << endl;

	s1.insert(0, '%');
	cout << s1.c_str() << endl;
}

測(cè)試結(jié)果:
【C++初階】STL詳解(二)string類的模擬實(shí)現(xiàn),c++,c++,java,開(kāi)發(fā)語(yǔ)言,string

測(cè)試4.關(guān)系運(yùn)算符與流插入流提取

示例:
比較s1與s2,輸入字符串并打印

void test_string4()
{
	string s1("hello world");
	string s2("hello world");

	cout << (s1 >= s2) << endl;

	s1[0] = 'z';
	cout << (s1 >= s2) << endl;

	cout << s1 << endl;
	cin >> s1;
	cout << s1 << endl;
}

測(cè)試結(jié)果:
【C++初階】STL詳解(二)string類的模擬實(shí)現(xiàn),c++,c++,java,開(kāi)發(fā)語(yǔ)言,string

測(cè)試5.erase

示例:
測(cè)試rease

void test_string5()
{
	string s1("hello world");
	s1.insert(5, "abc");
	cout << s1 << endl;

	s1.insert(0, "xxx");
	cout << s1 << endl;

	s1.erase(0, 3);
	cout << s1 << endl;

	s1.erase(5, 100);
	cout << s1 << endl;

	s1.erase(2);
	cout << s1 << endl;
}

測(cè)試結(jié)果:
【C++初階】STL詳解(二)string類的模擬實(shí)現(xiàn),c++,c++,java,開(kāi)發(fā)語(yǔ)言,string

測(cè)試6.resize

示例:
測(cè)試resize

void test_string6()
{
	string s1("hello world");
	cout << s1 << endl;

	s1.resize(5);
	cout << s1 << endl;

	s1.resize(25, 'x');
	cout << s1 << endl;
}

測(cè)試結(jié)果:
【C++初階】STL詳解(二)string類的模擬實(shí)現(xiàn),c++,c++,java,開(kāi)發(fā)語(yǔ)言,string

測(cè)試7.find

示例:
將網(wǎng)址按照協(xié)議,域名,資源名三部分找出來(lái)。

void test_string7()
{
	string s1("test.cpp.tar.zip");
	//size_t i = s1.find('.');
	//size_t i = s1.rfind('.');

	//string s2 = s1.substr(i);
	//cout << s2 << endl;

	string s3("https://legacy.cplusplus.com/reference/string/string/rfind/");
	//string s3("ftp://www.baidu.com/?tn=65081411_1_oem_dg");
	// 協(xié)議
	// 域名
	// 資源名

	string sub1, sub2, sub3;
	size_t i1 = s3.find(':');
	if (i1 != string::npos)
		sub1 = s3.substr(0, i1);
	else
		cout << "沒(méi)有找到i1" << endl;

	size_t i2 = s3.find('/', i1 + 3);
	if (i2 != string::npos)
		sub2 = s3.substr(i1 + 3, i2 - (i1 + 3));
	else
		cout << "沒(méi)有找到i2" << endl;

	sub3 = s3.substr(i2 + 1);

	//域名
	cout << "協(xié)議" << endl;
	cout << sub1 << endl;
	cout << "域名" << endl;
	cout << sub2 << endl;
	cout << "資源名" << endl;
	cout << sub3 << endl;
}

測(cè)試結(jié)果:
【C++初階】STL詳解(二)string類的模擬實(shí)現(xiàn),c++,c++,java,開(kāi)發(fā)語(yǔ)言,string

測(cè)試8:拷貝構(gòu)造

示例:
將s3拷貝給s2

void test_string8()
{
	string s1("hello world");
	string s2 = s1;
	cout << s1 << endl;
	cout << s2 << endl;

	string s3("xxxxxxxxxxxxxxxxxxx");
	s2 = s3;

	cout << s2 << endl;
	cout << s3 << endl;
}

結(jié)果:
【C++初階】STL詳解(二)string類的模擬實(shí)現(xiàn),c++,c++,java,開(kāi)發(fā)語(yǔ)言,string

測(cè)試9.容量與大小

示例:
計(jì)算字符串“hello world”的長(zhǎng)度與容量

void test_string9()
{
	string s1("hello world");

	cout << s1 << endl;
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
}

結(jié)果:

【C++初階】STL詳解(二)string類的模擬實(shí)現(xiàn),c++,c++,java,開(kāi)發(fā)語(yǔ)言,string

string模擬實(shí)現(xiàn)源碼

模擬實(shí)現(xiàn)中,我們沒(méi)有進(jìn)行對(duì)其定義與聲明分離,將測(cè)試函數(shù)寫成成員函數(shù)包裝在命名空間里。

string.h

#include<assert.h>

namespace NIC
{
	class string
	{
	public:

		//迭代器相關(guān)
		typedef char* iterator;
		typedef const char* const_iterator;


		iterator begin()
		{
			return _str;//返回字符串中第一個(gè)字符的地址
		}

		iterator end()
		{
			return _str + _size; //返回字符串中最后一個(gè)字符的后一個(gè)字符的地址
		}

		const_iterator begin() const
		{
			return _str;//返回字符串中第一個(gè)字符的const地址
		}

		const_iterator end() const
		{
			return _str + _size; //返回字符串中最后一個(gè)字符的后一個(gè)字符的地址
		}

		/*string()
			:_str(new char[1]{'\0'})
			,_size(0)
			,_capacity(0)
		{}*/
        
		//構(gòu)造函數(shù)
		string(const char* str = "")
			:_size(strlen(str))//初始時(shí),字符串大小設(shè)置為字符串長(zhǎng)度
			, _capacity(_size)///初始時(shí),字符串容量設(shè)置為字符串長(zhǎng)度
		{
			_str = new char[_capacity + 1];//為存儲(chǔ)字符開(kāi)辟空間(多開(kāi)一個(gè)用于存放'\0')
			strcpy(_str, str);//將字符串拷貝到已經(jīng)開(kāi)好的空間
		}


		 傳統(tǒng)寫法
		 s2(s1)
		//string(const string& s)
		//{
		//	_str = new char[s._capacity+1];
		//	strcpy(_str, s._str);//將s._str拷貝一份到_str
		//	_size = s._size;//_size賦值
		//	_capacity = s._capacity;//_capacity賦值
		//}


		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}


		//現(xiàn)代寫法
		// s2(s1)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);//調(diào)用構(gòu)造函數(shù)
			swap(tmp);//交換這兩個(gè)對(duì)象
		}


		//賦值運(yùn)算符重載函數(shù)
		 s2 = s3
		// 寫法一:
		//string& operator=(const string& s)
		//{
		//	if (this != &s)//防止自己給自己賦值
		//	{
		//		char* tmp = new char[s._capacity + 1];
		//		strcpy(tmp, s._str);

		//		delete[] _str;
		//		_str = tmp;
		//		_size = s._size;
		//		_capacity = s._capacity;
		//	}

		//	return *this;
		//}


		//寫法二:
		// s2 = s3
		//string& operator=(const string& s)
		//{
		//	if (this != &s)
		//	{
		//		string tmp(s);
		//		//this->swap(tmp);
		//		swap(tmp);
		//	}

		//	return *this;
		//}

		//寫法一:
		// s2 = s3
		string& operator=(string tmp)
		{
			swap(tmp);

			return *this;
		}

		//析構(gòu)函數(shù)
		~string()
		{
			delete[] _str;//釋放_(tái)str指向的空間
			_str = nullptr;//及時(shí)置空,防止非法訪問(wèn)
			_size = _capacity = 0;
		}

		//容量相關(guān)
		//大小
		size_t size() const
		{
			return _size;
		}

		//容量
		size_t capacity() const
		{
			return _capacity;
		}
		
		//reserve
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;

				_capacity = n;
			}
		}
		
		//resize
		void resize(size_t n, char ch = '\0')
		{
			if (n <= _size)//n小于當(dāng)前size
			{
				_str[n] = '\0';//在size個(gè)字符后放上'\0'
				_size = n;//將size調(diào)整為n
			}
			else//n大于當(dāng)前的size
			{
				reserve(n);
				while (_size < n)
				{
					_str[_size] = ch;
					++_size;
				}

				_str[_size] = '\0';
			}
		}


		char& operator[](size_t pos)
		{
			assert(pos < _size);

			return _str[pos];
		}

		const char& operator[](size_t pos) const
		{
			assert(pos < _size);

			return _str[pos];
		}

		

		
		const char* c_str() const
		{
			return _str;
		}


		

		size_t find(char ch, size_t pos = 0)
		{
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
				{
					return i;
				}
			}

			return npos;
		}

		size_t find(const char* sub, size_t pos = 0)
		{
			const char* p = strstr(_str + pos, sub);
			if (p)
			{
				return p - _str;
			}
			else
			{
				return npos;
			}
		}

		string substr(size_t pos, size_t len = npos)
		{
			string s;
			size_t end = pos + len;
			if (len == npos || pos + len >= _size) // 有多少取多少
			{
				len = _size - pos;
				end = _size;
			}

			s.reserve(len);
			for (size_t i = pos; i < end; i++)
			{
				s += _str[i];
			}

			return s;
		}

		void push_back(char ch)
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}

		void append(const char* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			strcpy(_str + _size, str);
			_size += len;
		}

		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		// insert(0, 'x')
		void insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			// 17:17
			size_t end = _size + 1;
			while (end > pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			_size++;
		}

		void insert(size_t pos, const char* str)
		{
			assert(pos <= _size);

			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			// 挪動(dòng)數(shù)據(jù)
			int end = _size;
			while (end >= (int)pos)
			{
				_str[end + len] = _str[end];
				--end;
			}

			strncpy(_str + pos, str, len);
			_size += len;
		}

		void erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);

			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				size_t begin = pos + len;
				while (begin <= _size)
				{
					_str[begin - len] = _str[begin];
					++begin;
				}
				_size -= len;
			}
		}

		bool operator<(const string& s) const
		{
			return strcmp(_str, s._str) < 0;
		}

		bool operator==(const string& s) const
		{
			return strcmp(_str, s._str) == 0;
		}

		bool operator<=(const string& s) const
		{
			return *this < s || *this == s;
		}

		bool operator>(const string& s) const
		{
			return !(*this <= s);
		}

		bool operator>=(const string& s) const
		{
			return !(*this < s);
		}

		bool operator!=(const string& s) const
		{
			return !(*this == s);
		}

		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;

		//const static size_t npos = -1;  // 特例
		//const static double npos = 1.1;  // 不支持
	public:
		const static size_t npos;
	};

	const size_t string::npos = -1;

	ostream& operator<<(ostream& out, const string& s)
	{
		/*for (size_t i = 0; i < s.size(); i++)
		{
			out << s[i];
		}*/
		for (auto ch : s)
			out << ch;

		return out;
	}

	istream& operator>>(istream& in, string& s)
	{

		s.clear();
		//s.reserve(128);

		char buff[129];
		size_t i = 0;

		char ch;
		ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 128)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}

			//s += ch;

			ch = in.get();
		}

		if (i != 0)
		{
			buff[i] = '\0';
			s += buff;
		}

		return in;
	}

	void test_string1()
	{

		string s1("hello world");
		cout << s1.c_str() << endl;

		string s2;
		cout << s2.c_str() << endl;

		//訪問(wèn)字符串
		for (size_t i = 0; i < s1.size(); i++)
		{
			cout << s1[i] ;
		}
		cout << endl;

		//迭代器
		string::iterator it = s1.begin();
		while (it != s1.end())
		{
			//寫
			(*it)++;
			//讀
			cout << *it << " ";
			++it;
		}
		cout << endl;
		//范圍for
		for (auto& ch : s1)
		{
			ch++;
			cout << ch << " ";
		}
		cout << endl;

		cout << s1.c_str() << endl;
	}

	void test_string2()
	{
		string s1("hello world");
		cout << s1.c_str() << endl;
		s1.push_back(' ');
		s1.append("hello bit hello bit");

		cout << s1.c_str() << endl;

		s1 += '#';
		s1 += "*********************";
		cout << s1.c_str() << endl;

		string s2;
		s2 += '#';
		s2 += "*********************";
		cout << s2.c_str() << endl;
	}

	void test_string3()
	{
		string s1("hello world");
		cout << s1.c_str() << endl;

		s1.insert(5, '%');
		cout << s1.c_str() << endl;

		s1.insert(s1.size(), '%');
		cout << s1.c_str() << endl;

		s1.insert(0, '%');
		cout << s1.c_str() << endl;
	}

	void test_string4()
	{
		string s1("hello world");
		string s2("hello world");

		cout << (s1 >= s2) << endl;

		s1[0] = 'z';
		cout << (s1 >= s2) << endl;

		cout << s1 << endl;
		cin >> s1;
		cout << s1 << endl;

		/*char ch1, ch2;
		cin >> ch1 >> ch2;*/
	}

	void test_string5()
	{
		string s1("hello world");
		s1.insert(5, "abc");
		cout << s1 << endl;

		s1.insert(0, "xxx");
		cout << s1 << endl;

		s1.erase(0, 3);
		cout << s1 << endl;

		s1.erase(5, 100);
		cout << s1 << endl;

		s1.erase(2);
		cout << s1 << endl;
	}

	void test_string6()
	{
		string s1("hello world");
		cout << s1 << endl;

		s1.resize(5);
		cout << s1 << endl;

		s1.resize(25, 'x');
		cout << s1 << endl;
	}

	void test_string7()
	{
		string s1("test.cpp.tar.zip");
		//size_t i = s1.find('.');
		//size_t i = s1.rfind('.');

		//string s2 = s1.substr(i);
		//cout << s2 << endl;

		string s3("https://legacy.cplusplus.com/reference/string/string/rfind/");
		//string s3("ftp://www.baidu.com/?tn=65081411_1_oem_dg");
		// 協(xié)議
		// 域名
		// 資源名

		string sub1, sub2, sub3;
		size_t i1 = s3.find(':');
		if (i1 != string::npos)
			sub1 = s3.substr(0, i1);
		else
			cout << "沒(méi)有找到i1" << endl;

		size_t i2 = s3.find('/', i1 + 3);
		if (i2 != string::npos)
			sub2 = s3.substr(i1 + 3, i2 - (i1 + 3));
		else
			cout << "沒(méi)有找到i2" << endl;

		sub3 = s3.substr(i2 + 1);

		//域名
		cout << "協(xié)議" << endl;
		cout << sub1 << endl;
		cout << "域名" << endl;
		cout << sub2 << endl;
		cout << "資源名" << endl;
		cout << sub3 << endl;
	}

	void test_string8()
	{
		string s1("hello world");
		string s2 = s1;
		cout << s1 << endl;
		cout << s2 << endl;

		string s3("xxxxxxxxxxxxxxxxxxx");
		s2 = s3;

		cout << s2 << endl;
		cout << s3 << endl;
	}

	void test_string9()
	{
		string s1("hello world");
		
		cout << s1 << endl;
		cout << s1.size() << endl;
		cout << s1.capacity() << endl;
	}
}

test.c

test.c用于測(cè)試接口函數(shù):文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-753301.html

#include<string>
using namespace std;
#include"string.h"
int main()
{
	NIC::test_string9();
	return 0;
}

到了這里,關(guān)于【C++初階】STL詳解(二)string類的模擬實(shí)現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【C++練級(jí)之路】【Lv.6】【STL】string類的模擬實(shí)現(xiàn)

    歡迎各位小伙伴關(guān)注我的專欄,和我一起系統(tǒng)學(xué)習(xí)C語(yǔ)言,共同探討和進(jìn)步哦! 學(xué)習(xí)專欄 : 《進(jìn)擊的C++》 關(guān)于 STL容器 的學(xué)習(xí),我會(huì)采用 模擬實(shí)現(xiàn) 的方式,以此來(lái)更加清楚地了解其 底層原理和整體架構(gòu) 。而string類更是有100多個(gè)接口函數(shù),所以模擬實(shí)現(xiàn)的時(shí)候只會(huì)調(diào)重點(diǎn)和

    2024年01月18日
    瀏覽(30)
  • 【C++】:STL中的string類的增刪查改的底層模擬實(shí)現(xiàn)

    【C++】:STL中的string類的增刪查改的底層模擬實(shí)現(xiàn)

    本篇博客僅僅實(shí)現(xiàn)存儲(chǔ)字符(串)的string 同時(shí)由于C++string庫(kù)設(shè)計(jì)的不合理,我僅實(shí)現(xiàn)一些最常見(jiàn)的增刪查改接口 接下來(lái)給出的接口都是基于以下框架: C++string標(biāo)準(zhǔn)庫(kù)中,無(wú)參構(gòu)造并不是空間為0,直接置為空指針 而是開(kāi)一個(gè)字節(jié),并存放‘\\0’ C++中支持無(wú)參構(gòu)造一個(gè)對(duì)象后,直

    2024年02月05日
    瀏覽(24)
  • 【C++初階】STL詳解(四)vector的模擬實(shí)現(xiàn)

    【C++初階】STL詳解(四)vector的模擬實(shí)現(xiàn)

    本專欄內(nèi)容為:C++學(xué)習(xí)專欄,分為初階和進(jìn)階兩部分。 通過(guò)本專欄的深入學(xué)習(xí),你可以了解并掌握C++。 ??博主csdn個(gè)人主頁(yè):小小unicorn ?專欄分類:C++ ??代碼倉(cāng)庫(kù):小小unicorn的代碼倉(cāng)庫(kù)?? ??????關(guān)注我?guī)銓W(xué)習(xí)編程知識(shí) 注:為了防止與標(biāo)準(zhǔn)庫(kù)當(dāng)中的vector產(chǎn)生命名沖突

    2024年02月05日
    瀏覽(25)
  • 【C++初階】第七站:string類的初識(shí)(萬(wàn)字詳解、細(xì)節(jié)拉滿)

    【C++初階】第七站:string類的初識(shí)(萬(wàn)字詳解、細(xì)節(jié)拉滿)

    前言: ?? 本文知識(shí)點(diǎn):string的初識(shí) ?? 個(gè)人博客: Dream_Chaser~-CSDN博客 ??本專欄: C++ 目錄 一、什么是STL 二、STL的六大組件 三、STL的缺陷 四、為什么學(xué)習(xí)string類? 五、標(biāo)準(zhǔn)庫(kù)中的string類 1、string類(了解) 2、string類的常用接口說(shuō)明(最常用的接口) A. string類對(duì)象的常見(jiàn)構(gòu)

    2024年03月20日
    瀏覽(27)
  • C++初階-vector類的模擬實(shí)現(xiàn)

    C++初階-vector類的模擬實(shí)現(xiàn)

    C++ STL中的vector就類似于C語(yǔ)言當(dāng)中的數(shù)組,但是vector又擁有很多數(shù)組沒(méi)有的接口,使用起來(lái)更加方便。 相比于STL中的string,vector可以定義不同的數(shù)據(jù)類型。 迭代器的本質(zhì)可以暫時(shí)看作是指針,模擬實(shí)現(xiàn)vector,需要定義三個(gè)指針:指向起始位置_start,指向最后一個(gè)有效元素的下

    2024年02月04日
    瀏覽(19)
  • 【C++】string類的模擬實(shí)現(xiàn)

    【C++】string類的模擬實(shí)現(xiàn)

    前言:在上一篇中我們講到了string類的使用方法,今天我們將進(jìn)一步的去學(xué)習(xí)string類,去底層看看它順帶模擬實(shí)現(xiàn)部分的內(nèi)容。 ?? 博主CSDN主頁(yè):衛(wèi)衛(wèi)衛(wèi)的個(gè)人主頁(yè) ?? ?? 專欄分類:高質(zhì)量C++學(xué)習(xí) ?? ??代碼倉(cāng)庫(kù):衛(wèi)衛(wèi)周大胖的學(xué)習(xí)日記?? ??關(guān)注博主和博主一起學(xué)習(xí)!一起努

    2024年03月21日
    瀏覽(22)
  • 【C++】String類的模擬實(shí)現(xiàn)。

    ??博客主頁(yè):小智_x0___0x_ ??歡迎關(guān)注:??點(diǎn)贊??收藏??留言 ??系列專欄:C++初階 ??代碼倉(cāng)庫(kù):小智的代碼倉(cāng)庫(kù) string類中需要三個(gè)成員變量分別記錄元素個(gè)數(shù)、容量和內(nèi)容。還需要一個(gè) size_t 類型npos-1表示整型的最大值。 這段代碼是 string 類的構(gòu)造函數(shù)。構(gòu)造函數(shù)是在

    2024年02月13日
    瀏覽(20)
  • C++ 之 string類的模擬實(shí)現(xiàn)

    C++ 之 string類的模擬實(shí)現(xiàn)

    這學(xué)習(xí)我有三不學(xué) 昨天不學(xué),因?yàn)樽蛱焓莻€(gè)過(guò)去 明天不學(xué),因?yàn)槊魈爝€是個(gè)未知數(shù) 今天不學(xué),因?yàn)槲覀円钤诋?dāng)下,我就是玩嘿嘿~ –?–?–?–?–?–?–?–?–?–?–?–?–?–?–?–?–?–?–?-正文開(kāi)始-?–?–?–?–?–?–?–?–?–?–

    2024年04月27日
    瀏覽(21)
  • 【c++】string類的使用及模擬實(shí)現(xiàn)

    【c++】string類的使用及模擬實(shí)現(xiàn)

    我們先了解一下什么是OOP思想 OOP思想,即面向?qū)ο缶幊蹋∣bject-Oriented Programming)的核心思想,主要包括“抽象”、“封裝”、“繼承”和“多態(tài)”四個(gè)方面。 抽象:抽象是忽略一個(gè)主題中與當(dāng)前目標(biāo)無(wú)關(guān)的那些方面,以便充分地注意與當(dāng)前目標(biāo)有關(guān)的方面。抽象并不打算了

    2024年04月11日
    瀏覽(24)
  • 【C++】——string類的介紹及模擬實(shí)現(xiàn)

    【C++】——string類的介紹及模擬實(shí)現(xiàn)

    C語(yǔ)言中,字符串是以’\\0’結(jié)尾的一些字符的集合,為了操作方便,C標(biāo)準(zhǔn)庫(kù)中提供了一些str系列的庫(kù)函數(shù),但是這些庫(kù)函數(shù)與字符串是分離開(kāi)的,不太符合OOP的思想,而且底層空間需要用戶自己管理,稍不留神可能還會(huì)越界訪問(wèn)。所以我們今天來(lái)學(xué)習(xí)C++標(biāo)準(zhǔn)庫(kù)中的string類。

    2024年02月07日
    瀏覽(31)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包