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

C++初階之一篇文章讓你掌握string類(模擬實(shí)現(xiàn))

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

C++初階之一篇文章讓你掌握string類(模擬實(shí)現(xiàn)),C++初階,c++,開發(fā)語言,vscode

1.為什么要模擬實(shí)現(xiàn)string

C++初階之一篇文章讓你掌握string類(模擬實(shí)現(xiàn)),C++初階,c++,開發(fā)語言,vscode
模擬實(shí)現(xiàn) std::string 是一個(gè)有挑戰(zhàn)性的練習(xí),它可以帶來多方面的收益,尤其對(duì)于學(xué)習(xí) C++ 和深入了解字符串操作以及動(dòng)態(tài)內(nèi)存管理的機(jī)制。以下是模擬實(shí)現(xiàn) std::string 的一些好處和重要意義:

  1. 學(xué)習(xí) C++ 內(nèi)存管理:std::string 是一個(gè)動(dòng)態(tài)分配內(nèi)存的容器,模擬實(shí)現(xiàn)需要手動(dòng)處理內(nèi)存的分配和釋放。這可以讓你更深入地理解動(dòng)態(tài)內(nèi)存管理的原理和機(jī)制,如何正確地使用 new 和 delete 運(yùn)算符,以及如何避免內(nèi)存泄漏和懸空指針。
  2. 字符串操作的練習(xí):在模擬實(shí)現(xiàn)過程中,您需要實(shí)現(xiàn)字符串的拼接、插入、刪除、查找等操作,以及其他與字符串處理相關(guān)的函數(shù)。這可以幫助您熟悉 C++ 中字符串的操作和處理方式。
  3. 深入理解類和對(duì)象:std::string 是一個(gè)類模板,模擬實(shí)現(xiàn)它需要深入理解類和對(duì)象的概念,包括構(gòu)造函數(shù)、析構(gòu)函數(shù)、成員函數(shù)、成員變量等。通過實(shí)現(xiàn)一個(gè)類似 std::string 的類,你可以更好地理解類的設(shè)計(jì)和使用。
  4. 提高編程技能:模擬實(shí)現(xiàn) std::string 是一項(xiàng)挑戰(zhàn)性的任務(wù),它可以鍛煉你的編程技能,讓你更加熟練地使用 C++ 的語法和特性。
  5. 深入學(xué)習(xí)模板編程:std::string 是一個(gè)類模板,模擬實(shí)現(xiàn)它可以幫助你深入了解模板編程的機(jī)制和技巧。
  6. 實(shí)現(xiàn)自定義容器:std::string 是 C++ 標(biāo)準(zhǔn)庫中的一個(gè)容器類,模擬實(shí)現(xiàn)它是實(shí)現(xiàn)自定義容器的練習(xí)。自定義容器可以幫助您更好地理解容器的設(shè)計(jì)和實(shí)現(xiàn)。

2.string的模擬實(shí)現(xiàn)需要注意哪些問題

模擬實(shí)現(xiàn) std::string 類是一個(gè)有挑戰(zhàn)性的任務(wù),因?yàn)?std::string 是 C++ 標(biāo)準(zhǔn)庫中的一個(gè)復(fù)雜數(shù)據(jù)類型,它有很多功能和特性,而其實(shí)現(xiàn)涉及到動(dòng)態(tài)內(nèi)存管理、字符串操作、復(fù)制語義等方面。在進(jìn)行模擬實(shí)現(xiàn)時(shí),需要注意以下一些關(guān)鍵問題:

  1. 內(nèi)存管理:std::string 類是一個(gè)動(dòng)態(tài)分配內(nèi)存的容器,模擬實(shí)現(xiàn)需要正確地處理內(nèi)存的分配和釋放。你可以使用動(dòng)態(tài)數(shù)組、指針或其他數(shù)據(jù)結(jié)構(gòu)來模擬動(dòng)態(tài)內(nèi)存的管理。
  2. 字符串操作:模擬實(shí)現(xiàn)需要支持字符串的拼接、插入、刪除、查找等操作,以及其他字符串處理的函數(shù)(如 size()、substr()、find() 等)。
  3. 異常處理:std::string 在一些情況下可能會(huì)引發(fā)異常,例如內(nèi)存分配失敗或訪問越界等。模擬實(shí)現(xiàn)需要考慮如何正確處理異常情況,以確保程序的穩(wěn)定性和安全性。
  4. 內(nèi)存拷貝:std::string 采用了深拷貝(deep copy)語義,即在復(fù)制時(shí)會(huì)復(fù)制整個(gè)字符串的內(nèi)容。模擬實(shí)現(xiàn)需要正確地處理內(nèi)存的拷貝,以避免懸空指針和資源泄漏等問題。
  5. 迭代器支持:std::string 支持迭代器用于訪問字符串的內(nèi)容,模擬實(shí)現(xiàn)需要提供相應(yīng)的迭代器支持。
  6. 性能優(yōu)化:std::string 的標(biāo)準(zhǔn)實(shí)現(xiàn)通常會(huì)對(duì)性能進(jìn)行優(yōu)化,例如采用了擴(kuò)容策略來減少頻繁的內(nèi)存分配。模擬實(shí)現(xiàn)可以考慮一些優(yōu)化策略,提高性能和效率。
  7. 邊界條件:在進(jìn)行模擬實(shí)現(xiàn)時(shí),需要特別注意邊界條件和特殊情況,確保實(shí)現(xiàn)的正確性和魯棒性。
  8. 完整性:std::string 類是一個(gè)非常復(fù)雜的數(shù)據(jù)類型,模擬實(shí)現(xiàn)時(shí)需要盡可能完整地實(shí)現(xiàn)其功能和接口。

雖然模擬實(shí)現(xiàn) std::string 是一個(gè)復(fù)雜的任務(wù),但它也是一個(gè)很好的學(xué)習(xí)練習(xí),可以加深對(duì) C++ 內(nèi)存管理、字符串處理等方面的理解。

3.經(jīng)典的string類問題

上一篇文章已經(jīng)對(duì)string類進(jìn)行了簡單的介紹,大家只要能夠正常使用即可。在面試中,面試官總喜歡讓學(xué)生自己來模擬實(shí)現(xiàn)string類,最主要是實(shí)現(xiàn)string類的構(gòu)造、拷貝構(gòu)造、賦值運(yùn)算符重載以及析構(gòu)函數(shù)。大家看下以下string類的實(shí)現(xiàn)是否有問題?

// 為了和標(biāo)準(zhǔn)庫區(qū)分,此處使用String
class String
{
public:
	/*String()
:_str(new char[1])
{*_str = '\0';}
*/
//String(const char* str = "\0") 錯(cuò)誤示范
//String(const char* str = nullptr) 錯(cuò)誤示范
	String(const char* str = "")
	{
		// 構(gòu)造String類對(duì)象時(shí),如果傳遞nullptr指針,可以認(rèn)為程序非法
		if (nullptr == str)
		{
			assert(false);
			return;
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
private:
	char* _str;
};
// 測試
void TestString()
{
	String s1("hello bit!!!");
	String s2(s1);
}

C++初階之一篇文章讓你掌握string類(模擬實(shí)現(xiàn)),C++初階,c++,開發(fā)語言,vscode
上述String類沒有顯式定義其拷貝構(gòu)造函數(shù)與賦值運(yùn)算符重載,此時(shí)編譯器會(huì)合成默認(rèn)的,當(dāng)用s1構(gòu)造s2時(shí),編譯器會(huì)調(diào)用默認(rèn)的拷貝構(gòu)造。最終導(dǎo)致的問題是,s1、s2共用同一塊內(nèi)存空間,在釋放時(shí)同一塊空間被釋放多次而引起程序崩潰,這種拷貝方式,稱為淺拷貝。

什么是淺拷貝

淺拷貝:也稱位拷貝,編譯器只是將對(duì)象中的值拷貝過來。如果對(duì)象中管理資源,最后就會(huì)導(dǎo)致多個(gè)對(duì)象共享同一份資源,當(dāng)一個(gè)對(duì)象銷毀時(shí)就會(huì)將該資源釋放掉,而此時(shí)另一些對(duì)象不知道該資源已經(jīng)被釋放,以為還有效,所以當(dāng)繼續(xù)對(duì)資源進(jìn)項(xiàng)操作時(shí),就會(huì)發(fā)生發(fā)生了訪問違規(guī)。其實(shí)我們可以采用深拷貝解決淺拷貝問題,即:每個(gè)對(duì)象都有一份獨(dú)立的資源,不要和其他對(duì)象共享。

什么是深拷貝

深拷貝是指在進(jìn)行對(duì)象拷貝時(shí),不僅復(fù)制對(duì)象本身的成員變量,還復(fù)制對(duì)象所指向的動(dòng)態(tài)分配的資源(例如堆內(nèi)存)到新的對(duì)象中。這意味著拷貝后的對(duì)象和原對(duì)象擁有獨(dú)立的資源副本,彼此之間不會(huì)相互影響。

當(dāng)對(duì)象中含有動(dòng)態(tài)分配的資源,如指針指向的內(nèi)存塊,或者其他動(dòng)態(tài)分配的資源(文件句柄、網(wǎng)絡(luò)連接等),進(jìn)行深拷貝是非常重要的,以避免多個(gè)對(duì)象共享同一塊資源導(dǎo)致釋放重復(fù)、懸掛指針等問題。

如果一個(gè)類中涉及到資源的管理,其拷貝構(gòu)造函數(shù)、賦值運(yùn)算符重載以及析構(gòu)函數(shù)必須要顯式給出。一般情況都是按照深拷貝方式提供。
C++初階之一篇文章讓你掌握string類(模擬實(shí)現(xiàn)),C++初階,c++,開發(fā)語言,vscode

4.寫時(shí)拷貝

“寫時(shí)拷貝”(Copy on Write,簡稱為 COW)是一種優(yōu)化技術(shù),通常應(yīng)用于操作系統(tǒng)的內(nèi)存管理或數(shù)據(jù)結(jié)構(gòu)中,目的是節(jié)省內(nèi)存和提高性能。在 COW 中,當(dāng)多個(gè)對(duì)象共享同一份資源時(shí),只有在某個(gè)對(duì)象試圖修改資源內(nèi)容時(shí),才會(huì)進(jìn)行實(shí)際的拷貝操作,否則所有對(duì)象共享相同的原始資源。這樣可以避免在修改前對(duì)整個(gè)資源進(jìn)行拷貝,節(jié)省了內(nèi)存和執(zhí)行時(shí)間。

COW 最常見的應(yīng)用是在操作系統(tǒng)中的進(jìn)程管理和內(nèi)存分配。當(dāng)一個(gè)進(jìn)程 fork(復(fù)制)自身時(shí),通常會(huì)采用 COW 機(jī)制。在 fork 時(shí),子進(jìn)程會(huì)與父進(jìn)程共享相同的內(nèi)存空間,即物理頁框。只有當(dāng)子進(jìn)程或父進(jìn)程中的一個(gè)試圖修改其中的內(nèi)容時(shí),操作系統(tǒng)才會(huì)執(zhí)行實(shí)際的拷貝,將要修改的頁框內(nèi)容復(fù)制到新的頁框中,使得兩個(gè)進(jìn)程的內(nèi)存空間獨(dú)立開來。這樣,父子進(jìn)程可以共享大部分資源,而無需進(jìn)行大規(guī)模的內(nèi)存拷貝,從而提高了 fork 操作的效率。

在編程語言或數(shù)據(jù)結(jié)構(gòu)中,寫實(shí)拷貝也可以用于優(yōu)化數(shù)據(jù)結(jié)構(gòu)的復(fù)制操作。例如,在某些容器類(如字符串、數(shù)組、向量等)中,當(dāng)多個(gè)對(duì)象共享相同的數(shù)據(jù)時(shí),只有在其中一個(gè)對(duì)象試圖修改數(shù)據(jù)時(shí),才會(huì)進(jìn)行實(shí)際的拷貝操作,確保各個(gè)對(duì)象之間的數(shù)據(jù)相互獨(dú)立。

需要注意的是,COW 并不是適用于所有情況的通用優(yōu)化技術(shù),它的有效性取決于具體的應(yīng)用場景。在某些情況下,COW 可以帶來顯著的性能優(yōu)勢,但在其他情況下,可能會(huì)增加復(fù)雜性和開銷。因此,在實(shí)現(xiàn)時(shí)需要仔細(xì)權(quán)衡利弊,根據(jù)實(shí)際需求選擇合適的優(yōu)化策略。

其實(shí)寫時(shí)拷貝就是一種拖延癥,是在淺拷貝的基礎(chǔ)之上增加了引用計(jì)數(shù)的方式來實(shí)現(xiàn)的。

引用計(jì)數(shù):用來記錄資源使用者的個(gè)數(shù)。在構(gòu)造時(shí),將資源的計(jì)數(shù)給成1,每增加一個(gè)對(duì)象使用該資源,就給計(jì)數(shù)增加1,當(dāng)某個(gè)對(duì)象被銷毀時(shí),先給該計(jì)數(shù)減1,然后再檢查是否需要釋放資源,如果計(jì)數(shù)為1,說明該對(duì)象時(shí)資源的最后一個(gè)使用者,將該資源釋放;否則就不能釋放,因?yàn)檫€有其他對(duì)象在使用該資源。

一個(gè)常見的例子是字符串的寫實(shí)拷貝。

在許多編程語言中,字符串通常是不可變的(immutable),即一旦創(chuàng)建后,就無法修改其內(nèi)容。在這種情況下,當(dāng)多個(gè)變量或?qū)ο笠猛粋€(gè)字符串時(shí),如果其中一個(gè)變量試圖修改字符串的內(nèi)容,就需要?jiǎng)?chuàng)建一個(gè)新的字符串對(duì)象,而不是直接在原始字符串上進(jìn)行修改。

假設(shè)有兩個(gè)變量 str1 和 str2 都指向相同的字符串 “Hello”:

std::string str1 = "Hello";
std::string str2 = str1;

在這里,str2 是通過拷貝構(gòu)造函數(shù)從 str1 創(chuàng)建的。在傳統(tǒng)的拷貝情況下,這將導(dǎo)致整個(gè)字符串 “Hello” 的拷貝,即兩個(gè)變量 str1 和 str2 都指向不同的內(nèi)存地址,但其內(nèi)容是相同的。

但是,寫時(shí)拷貝可以優(yōu)化這種情況。在寫時(shí)拷貝中,當(dāng) str2 拷貝 str1 時(shí),并不會(huì)立即創(chuàng)建一個(gè)新的字符串副本。而是讓 str2 和 str1 共享同一個(gè)底層的字符串?dāng)?shù)據(jù)。只有當(dāng)其中一個(gè)字符串試圖修改其內(nèi)容時(shí),才會(huì)觸發(fā)實(shí)際的拷貝操作。

例如,如果現(xiàn)在對(duì) str2 進(jìn)行修改操作:

str2[0] = 'h'; // 修改第一個(gè)字符為小寫 'h'

在寫時(shí)拷貝機(jī)制下,會(huì)創(chuàng)建一個(gè)新的字符串 “hello”,然后 str2 的內(nèi)容指向新的字符串,而 str1 的內(nèi)容保持不變。這樣,兩個(gè)變量 str1 和 str2 仍然共享相同的底層數(shù)據(jù),但它們的內(nèi)容已經(jīng)不再相同。

寫時(shí)拷貝可以有效地節(jié)省內(nèi)存,尤其在字符串長期共享的情況下,避免了不必要的內(nèi)存復(fù)制。但在其他情況下,可能會(huì)增加復(fù)雜性和開銷。因此,在實(shí)現(xiàn)時(shí)需要仔細(xì)權(quán)衡利弊,根據(jù)實(shí)際需求選擇合適的優(yōu)化策略。

5.傳統(tǒng)版寫法的String類(參考)

class String
{
public:
	String(const char* str = "")
	{
		// 構(gòu)造String類對(duì)象時(shí),如果傳遞nullptr指針,可以認(rèn)為程序非法
		if (nullptr == str)
		{
			assert(false);
			return;
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	String(const String& s)
		: _str(new char[strlen(s._str) + 1])
	{
		strcpy(_str, s._str);
	}
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			char* pStr = new char[strlen(s._str) + 1];
			strcpy(pStr, s._str);
			delete[] _str;
			_str = pStr;
		}
		return *this;
	}
	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
private:
	char* _str;
};

6.現(xiàn)代版寫法的String類(參考)

class String
{
public:
	String(const char* str = "")
	{
		if (nullptr == str)
		{
			assert(false);
			return;
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	String(const String& s)
		: _str(nullptr)
	{
		String strTmp(s._str);
		swap(_str, strTmp._str);
	}

	String& operator=(String s)
	{
		swap(_str, s._str);
		return *this;
	}
	/*
	String& operator=(const String& s)
	{
	if(this != &s)
	{
	String strTmp(s);
	swap(_str, strTmp._str);
	}
	return *this;
}
*/
	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
private:
	char* _str;
};

7.string類的模擬實(shí)現(xiàn)(講解)

根據(jù)上面提到的內(nèi)容和知識(shí),我們可以來實(shí)現(xiàn)string類框架和大部分的接口函數(shù),但在實(shí)際面試中,我們可能需要實(shí)現(xiàn)的功能并不多,所以我們這里只把最常見和常用的那些部分模擬實(shí)現(xiàn)。

7.1 命名空間string類的成員變量定義

namespace mystring
{
	class string
	{
	public:
		//...

	private:
		size_t _capacity;
		size_t _size;
		char* _str;
	public:
		// const static 語法特殊處理
		// 直接可以當(dāng)成定義初始化
		const static size_t npos = -1;
}

首先我們重新定義一個(gè)命名空間,防止和庫中的string類重定義,或者重新寫一個(gè)別的名字的string類也可以,類成員包括capacity,size和字符串str,npos定義成公有并初始化。

7.2 string類構(gòu)造函數(shù)

string(const char* str = "")
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];

			strcpy(_str, str);
		}

const char* str = “” 是構(gòu)造函數(shù)的默認(rèn)參數(shù)。默認(rèn)參數(shù)是在函數(shù)聲明中為函數(shù)參數(shù)提供默認(rèn)值的一種特性,它允許在調(diào)用函數(shù)時(shí),如果沒有提供相應(yīng)的參數(shù)值,就會(huì)使用默認(rèn)值作為參數(shù)的值,實(shí)際包含一個(gè)’\0’,分配足夠存儲(chǔ)字符串的內(nèi)存空間(_size + 1,其中 _size 是輸入字符串的長度),然后通過 strcpy 函數(shù)將輸入的 C 風(fēng)格字符串復(fù)制到 _str 成員變量中。

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

傳統(tǒng)寫法:

string(const string& s)
		:_str(new char[s._capacity+1])
		, _size(s._size)
		, _capacity(s._capacity)
	{
		strcpy(_str, s._str);
	}

現(xiàn)代寫法:

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


string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	string tmp(s._str);
	swap(tmp); //this->swap(tmp);
}

第一段代碼中,拷貝構(gòu)造函數(shù)采用傳統(tǒng)的深拷貝方式。它首先分配了與源對(duì)象(s)相同大小的內(nèi)存空間(包括結(jié)尾的空字符),然后將源對(duì)象的內(nèi)容復(fù)制到新分配的內(nèi)存空間中。

這種實(shí)現(xiàn)方式確保了新創(chuàng)建的對(duì)象和源對(duì)象具有獨(dú)立的內(nèi)存空間,即它們不共享資源。這樣,當(dāng)一個(gè)對(duì)象修改其內(nèi)容時(shí),不會(huì)影響到另一個(gè)對(duì)象,從而保證了對(duì)象之間的數(shù)據(jù)隔離。

而在第二段代碼中,拷貝構(gòu)造函數(shù)使用了 C++11 引入的移動(dòng)語義。它先創(chuàng)建了一個(gè)名為 tmp 的臨時(shí)對(duì)象,并使用 s._str 初始化了這個(gè)臨時(shí)對(duì)象。接著,通過調(diào)用成員函數(shù) swap(tmp) 將當(dāng)前對(duì)象的成員和臨時(shí)對(duì)象的成員進(jìn)行交換。

swap 函數(shù)的實(shí)現(xiàn)會(huì)使當(dāng)前對(duì)象的成員指向臨時(shí)對(duì)象的內(nèi)存空間,而臨時(shí)對(duì)象的成員指向了當(dāng)前對(duì)象之前的內(nèi)存空間。這樣一來,原來的資源被交換了,臨時(shí)對(duì)象會(huì)在析構(gòu)時(shí)釋放當(dāng)前對(duì)象原來的資源,而當(dāng)前對(duì)象則擁有了 s 對(duì)象的資源。

這種實(shí)現(xiàn)方式通過避免了不必要的內(nèi)存拷貝,從而提高了拷貝構(gòu)造函數(shù)的性能。在 tmp 作為臨時(shí)對(duì)象被析構(gòu)時(shí),它會(huì)自動(dòng)釋放原來 s 對(duì)象的資源,因此沒有內(nèi)存泄漏。

兩種實(shí)現(xiàn)方式都是有效的拷貝構(gòu)造函數(shù),但第二種實(shí)現(xiàn)利用了移動(dòng)語義,可以在拷貝對(duì)象時(shí)避免不必要的內(nèi)存復(fù)制,提高性能。在 C++11 及以上版本中,推薦使用第二種實(shí)現(xiàn)方式。

7.4 string類賦值運(yùn)算符重載

傳統(tǒng)寫法:

string& operator=(const string& s)
{
	if (this != &s)
	{
	string tmp(s);
	swap(tmp); 
	}
	return *this;
}

現(xiàn)代寫法:

string& operator=(string s)
{
	swap(s);
	return *this;
}

第一個(gè)函數(shù)中,賦值運(yùn)算符采用了傳統(tǒng)的深拷貝方式。它首先檢查目標(biāo)對(duì)象與源對(duì)象是否是同一個(gè)對(duì)象(地址比較),如果是同一個(gè)對(duì)象則不執(zhí)行賦值操作,避免了自賦值的情況。

然后,它創(chuàng)建一個(gè)臨時(shí)的 string 對(duì)象 tmp,并將源對(duì)象 s 的內(nèi)容復(fù)制到 tmp 中。接著,通過調(diào)用 swap(tmp),將當(dāng)前對(duì)象的成員和臨時(shí)對(duì)象的成員進(jìn)行交換。

這樣,原來的資源被交換了,當(dāng)前對(duì)象擁有了 s 對(duì)象的資源,而臨時(shí)對(duì)象在析構(gòu)時(shí)會(huì)自動(dòng)釋放當(dāng)前對(duì)象原來的資源。這樣實(shí)現(xiàn)了賦值操作,并在賦值時(shí)避免了不必要的內(nèi)存拷貝。

第二個(gè)函數(shù)中,賦值運(yùn)算符使用了 C++11 引入的移動(dòng)語義。它的參數(shù)是一個(gè) string 對(duì)象 s,這里采用了按值傳遞,即通過值拷貝的方式傳遞參數(shù)。

在函數(shù)內(nèi)部,它直接通過 swap(s) 將當(dāng)前對(duì)象的成員和參數(shù) s 對(duì)象的成員進(jìn)行交換。由于參數(shù) s 是按值傳遞的,意味著在調(diào)用函數(shù)時(shí)會(huì)執(zhí)行一次拷貝構(gòu)造函數(shù)來創(chuàng)建 s 對(duì)象的副本,因此在 swap(s) 中,將 s 對(duì)象的資源交換給了當(dāng)前對(duì)象,同時(shí)臨時(shí)對(duì)象 s 會(huì)在函數(shù)結(jié)束時(shí)自動(dòng)析構(gòu)并釋放當(dāng)前對(duì)象原來的資源。

這樣,通過移動(dòng)語義實(shí)現(xiàn)了賦值操作,并在賦值時(shí)避免了不必要的內(nèi)存復(fù)制。

區(qū)別總結(jié):

參數(shù)傳遞方式:第一個(gè)函數(shù)采用了常量引用傳遞,而第二個(gè)函數(shù)采用了按值傳遞。
拷貝控制技術(shù):第一個(gè)函數(shù)使用了深拷貝和交換資源的方式,而第二個(gè)函數(shù)利用了移動(dòng)語義和 swap 操作來避免拷貝。

兩者都能正確實(shí)現(xiàn)賦值操作,并避免了不必要的內(nèi)存拷貝。然而,第二個(gè)函數(shù)在 C++11 及以上版本中更推薦,因?yàn)樗昧艘苿?dòng)語義,性能更高效。如果你的代碼環(huán)境支持 C++11 或更高版本,建議優(yōu)先考慮使用第二種實(shí)現(xiàn)方式。

7.5 string類析構(gòu)函數(shù)和易實(shí)現(xiàn)的成員函數(shù)

~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}

這里的析構(gòu)函數(shù)通過 delete[] 操作釋放 _str 指向的動(dòng)態(tài)分配的字符數(shù)組(字符串內(nèi)存),然后將 _str 置為 nullptr,同時(shí)將 _size 和 _capacity 設(shè)置為 0。這樣確保對(duì)象被銷毀時(shí)內(nèi)存得到正確的釋放,防止內(nèi)存泄漏。

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

c_str()函數(shù)用于返回指向存儲(chǔ)字符串的字符數(shù)組的指針。

size_t size() const
{
	return _size;
}

size()函數(shù)用于返回字符串的大小,即字符串中實(shí)際存儲(chǔ)的字符個(gè)數(shù),返回類型為 size_t。這里的 _size 成員變量表示實(shí)際存儲(chǔ)的字符個(gè)數(shù),因此直接返回 _size 即可。

size_t capacity() const
{
	return _capacity;
}

capacity()函數(shù)用于返回字符串的容量,即字符串中當(dāng)前分配的內(nèi)存空間大小,返回類型為 size_t。這里的 _capacity 成員變量表示當(dāng)前的容量,因此直接返回 _capacity 即可。

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

	return _str[pos];
}

operator[](size_t pos) const是 const 版本的下標(biāo)操作符重載函數(shù),用于訪問字符串中指定位置 pos 處的字符。函數(shù)返回類型為 const char&,表示返回的是常量字符的引用,即不允許通過該引用修改字符內(nèi)容。這是為了確保字符串的不可變性。

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

	return _str[pos];
}

operator[](size_t pos)是非 const 版本的下標(biāo)操作符重載函數(shù),功能與上面的 const 版本類似,但這個(gè)函數(shù)返回類型是 char&,表示返回的是可修改字符的引用,允許通過該引用修改字符內(nèi)容。

void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

clear 函數(shù)用于清空字符串,即將字符串內(nèi)容全部置為空,并將實(shí)際大小 _size 設(shè)為 0,將字符數(shù)組的第一個(gè)字符(即字符串的起始位置)設(shè)置為空字符 ‘\0’,以將字符串內(nèi)容清空。

7.6 string類reserve函數(shù)

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		delete[] _str;

		_str = tmp;
		_capacity = n;
	}
}

`void reserve(size_t n):這是 std::string 類中的 reserve 函數(shù)的聲明,表示該函數(shù)將預(yù)留 n 個(gè)字符的內(nèi)存空間。n 是傳入的參數(shù),表示需要預(yù)留的字符個(gè)數(shù)。

if (n > _capacity):這里通過比較傳入的 n 和當(dāng)前字符串的容量 _capacity,來判斷是否需要增加字符串的容量。只有當(dāng)需要預(yù)留的字符個(gè)數(shù) n 大于當(dāng)前容量 _capacity 時(shí),才需要進(jìn)行內(nèi)存擴(kuò)展操作。
char* tmp = new char[n + 1];:如果需要增加容量,首先創(chuàng)建一個(gè)新的字符數(shù)組 tmp,長度為 n + 1,即預(yù)留的字符個(gè)數(shù)加上結(jié)尾的空字符。這里將字符串的容量設(shè)置為 n,是為了預(yù)留額外的一個(gè)位置來存儲(chǔ)結(jié)尾的空字符。
strcpy(tmp, _str);:將原來的字符串內(nèi)容復(fù)制到新創(chuàng)建的字符數(shù)組 tmp 中。
delete[] _str;:釋放原來字符串 _str 指向的動(dòng)態(tài)分配的字符數(shù)組,即釋放原來的內(nèi)存空間。
_str = tmp;:將原來的指針 _str 指向新的字符數(shù)組 tmp,這樣字符串的內(nèi)存空間得到了擴(kuò)展。
_capacity = n;:將 _capacity 更新為新的容量 n。

這樣,當(dāng)需要預(yù)留更多的內(nèi)存空間時(shí),reserve 函數(shù)會(huì)創(chuàng)建一個(gè)新的字符數(shù)組,并將原來的字符串內(nèi)容復(fù)制到新數(shù)組中,然后釋放原來的內(nèi)存空間,并將 _str 指向新的字符數(shù)組,更新容量 _capacity 為新的預(yù)留值 n。

7.7 string類resize函數(shù)

void resize(size_t n, char ch = '\0')
{
	if (n > _size)
	{
		// 插入數(shù)據(jù)
		reserve(n);
		for (size_t i = _size; i < n; ++i)
		{
			_str[i] = ch;
		}
		_str[n] = '\0';
		_size = n;
	}
	else
	{
		// 刪除數(shù)據(jù)
		_str[n] = '\0';
		_size = n;
	}
}

resize 函數(shù)用于改變字符串的大小,即增加或減少字符串中的字符個(gè)數(shù)。這里簡單解釋一下這個(gè)函數(shù)的實(shí)現(xiàn):

void resize(size_t n, char ch = '\0'):這是 std::string 類中的 resize 函數(shù)的聲明,表示該函數(shù)將改變字符串的大小為 n。n 是傳入的參數(shù),表示新的字符串大小。參數(shù) ch 是可選的,默認(rèn)值為 ‘\0’,用于在擴(kuò)展字符串大小時(shí)填充新增的字符。
if (n > _size):在這個(gè)條件分支中,判斷需要增加字符串大小的情況。如果傳入的新大小 n 大于當(dāng)前字符串大小 _size,表示需要在字符串末尾添加新的字符。
reserve(n);:首先調(diào)用 reserve 函數(shù)來預(yù)留足夠的內(nèi)存空間,確保字符串有足夠的容量來容納新增的字符。
for (size_t i = _size; i < n; ++i):然后在字符串中新增的位置,從當(dāng)前字符串的大小 _size 開始循環(huán)添加字符。這里將新增的字符都設(shè)置為 ch,即傳入的第二個(gè)參數(shù)。
_str[n] = '\0';:在循環(huán)結(jié)束后,將字符串的新末尾字符設(shè)置為空字符 ‘\0’,保證新的字符串正確終止。
_size = n;:最后將字符串的大小 _size 更新為新的大小 n。
else:在這個(gè)條件分支中,處理需要減小字符串大小的情況。如果傳入的新大小 n 小于當(dāng)前字符串大小 _size,表示需要?jiǎng)h除字符串中多余的字符。
_str[n] = '\0';:將字符串的新末尾字符設(shè)置為空字符 ‘\0’,保證新的字符串正確終止。
_size = n;:最后將字符串的大小 _size 更新為新的大小 n。

這樣,resize 函數(shù)可以根據(jù)傳入的大小 n,擴(kuò)展或縮小字符串的大小,并在必要時(shí)添加或刪除字符。

7.8 string類insert函數(shù)、append函數(shù)、push_back函數(shù)、+=重載

insert函數(shù)
insert的模擬實(shí)現(xiàn)主要實(shí)現(xiàn)字符和字符串插入兩種
字符插入

string& insert(size_t pos, char ch)
{
	assert(pos <= _size);

	// 滿了就擴(kuò)容
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

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

	_str[pos] = ch;
	++_size;

	return *this;
}

insert 函數(shù)在字符串中指定位置插入一個(gè)字符。這里簡單解釋一下這個(gè)函數(shù)的實(shí)現(xiàn):

string& insert(size_t pos, char ch):這是 std::string 類中的 insert 函數(shù)的聲明,表示該函數(shù)將在指定位置 pos 插入字符 ch。pos 是傳入的參數(shù),表示插入位置的索引;ch 是要插入的字符。
assert(pos <= _size);:使用 assert 斷言來確保插入位置 pos 不超過字符串的實(shí)際大小 _size。如果斷言失?。╬os 大于 _size),則會(huì)觸發(fā)斷言失敗錯(cuò)誤,幫助調(diào)試找到錯(cuò)誤的位置。
if (_size == _capacity):檢查當(dāng)前字符串是否已滿(即 _size 等于 _capacity)。如果字符串已滿,則需要擴(kuò)容,以確保有足夠的容量來插入新字符。這里使用 reserve 函數(shù)擴(kuò)容,使字符串有足夠的容量來容納新字符。
size_t end = _size + 1;:在插入字符前,先將字符串的末尾位置(實(shí)際字符個(gè)數(shù) _size 后面)向后移動(dòng)一個(gè)位置,為新字符留出空間。這樣做是為了將插入位置 pos 之后的字符后移。
while (end > pos):通過一個(gè)循環(huán),將插入位置 pos 之后的字符依次向后移動(dòng)一個(gè)位置。
_str[pos] = ch;:將字符 ch 插入到指定的插入位置 pos。
++_size;:插入字符后,將字符串的實(shí)際大小 _size 增加 1。
return *this;:返回當(dāng)前 std::string 對(duì)象的引用,以支持鏈?zhǔn)秸{(diào)用。

字符串插入

string& 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ù)
	size_t end = _size + len;
	while (end >= pos + len)
	{
		_str[end] = _str[end - len];
		--end;
	}

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

	return *this;
}

與上一個(gè) insert 函數(shù)相比,這里的參數(shù) str 是一個(gè) C-style 字符串(const char*),而不是一個(gè)單個(gè)字符。函數(shù)的功能是在字符串中指定位置插入一個(gè) C-style 字符串?,F(xiàn)在來解釋這個(gè)函數(shù)的實(shí)現(xiàn):

string& insert(size_t pos, const char* str):這是 std::string 類中的 insert 函數(shù)的聲明,表示該函數(shù)將在指定位置 pos 插入一個(gè) C-style 字符串 str。pos 是傳入的參數(shù),表示插入位置的索引;str 是要插入的 C-style 字符串。
assert(pos <= _size);:使用 assert 斷言來確保插入位置 pos 不超過字符串的實(shí)際大小 _size。如果斷言失?。╬os 大于 _size),則會(huì)觸發(fā)斷言失敗錯(cuò)誤,幫助調(diào)試找到錯(cuò)誤的位置。
size_t len = strlen(str);:計(jì)算要插入的 C-style 字符串 str 的長度,即字符個(gè)數(shù)。
if (_size + len > _capacity):檢查插入后的字符串大小是否超過當(dāng)前的容量 _capacity,如果超過,則需要擴(kuò)容,以確保有足夠的容量來容納插入的字符串。
reserve(_size + len);:調(diào)用 reserve 函數(shù)來擴(kuò)容,保證有足夠的容量來容納插入的字符串。
size_t end = _size + len;:在插入字符串前,先將字符串的末尾位置(實(shí)際字符個(gè)數(shù) _size 后面)向后移動(dòng) len 個(gè)位置,為新字符串留出空間。
while (end >= pos + len):通過一個(gè)循環(huán),將插入位置 pos 之后的字符依次向后移動(dòng) len 個(gè)位置,為新字符串的插入留出空間。
strncpy(_str + pos, str, len);:使用 strncpy 函數(shù)將 C-style 字符串 str 復(fù)制到指定的插入位置 pos,并且只復(fù)制 len 個(gè)字符。
_size += len;:插入字符串后,將字符串的實(shí)際大小 _size 增加 len,以反映插入后的新大小。
return *this;:返回當(dāng)前 std::string 對(duì)象的引用,以支持鏈?zhǔn)秸{(diào)用。

append函數(shù)

void append(const char* str)
{
	size_t len = strlen(str);

	// 滿了就擴(kuò)容
	if (_size + len > _capacity)
	{
		reserve(_size+len);
	}

	strcpy(_str + _size, str);
	//strcat(_str, str); 需要找\0,效率低
	_size += len;
}

append 函數(shù)用于在字符串末尾添加一個(gè) C-style 字符串?,F(xiàn)在來解釋這個(gè)函數(shù)的實(shí)現(xiàn):

void append(const char* str):這是 std::string 類中的 append 函數(shù)的聲明,表示該函數(shù)將在字符串末尾添加一個(gè) C-style 字符串 str。str 是傳入的參數(shù),表示要添加的 C-style 字符串。
size_t len = strlen(str);:計(jì)算要添加的 C-style 字符串 str 的長度,即字符個(gè)數(shù)。
if (_size + len > _capacity):檢查添加后的字符串大小是否超過當(dāng)前的容量 _capacity,如果超過,則需要擴(kuò)容,以確保有足夠的容量來容納添加的字符串。
reserve(_size + len);:調(diào)用 reserve 函數(shù)來擴(kuò)容,保證有足夠的容量來容納添加的字符串。
strcpy(_str + _size, str);:使用 strcpy 函數(shù)將 C-style 字符串 str 復(fù)制到字符串末尾,即從 _str 的實(shí)際字符個(gè)數(shù) _size 處開始復(fù)制。
_size += len;:添加字符串后,將字符串的實(shí)際大小 _size 增加 len,以反映添加后的新大小。

這樣,append 函數(shù)將 C-style 字符串 str 添加到字符串末尾,并且在必要時(shí)進(jìn)行了內(nèi)存擴(kuò)容。
當(dāng)然你可以對(duì)insert函數(shù)復(fù)用

void append(const char* str)
{
	insert(_size, str);
}

push_back函數(shù)

void push_back(char ch)
{
	// 滿了就擴(kuò)容
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

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

push_back 函數(shù)用于在字符串末尾添加一個(gè)字符?,F(xiàn)在來解釋這個(gè)函數(shù)的實(shí)現(xiàn):

void push_back(char ch):這是 std::string 類中的 push_back 函數(shù)的聲明,表示該函數(shù)將在字符串末尾添加一個(gè)字符 ch。ch 是傳入的參數(shù),表示要添加的字符。
if (_size == _capacity):檢查當(dāng)前字符串是否已滿(即 _size 等于 _capacity)。如果字符串已滿,則需要擴(kuò)容,以確保有足夠的容量來容納新增的字符。這里使用 reserve 函數(shù)擴(kuò)容,使字符串有足夠的容量來容納新字符。
_str[_size] = ch;:將字符 ch 添加到字符串末尾,即在 _str 的實(shí)際字符個(gè)數(shù) _size 處添加字符。
++_size;:字符串的實(shí)際大小 _size 增加 1,以反映添加后的新大小。
_str[_size] = '\0';:在字符串末尾添加一個(gè)空字符 ‘\0’,以保證新的字符串正確終止。

這樣,push_back 函數(shù)將字符 ch 添加到字符串末尾,并在必要時(shí)進(jìn)行了內(nèi)存擴(kuò)容。
同樣的,push_back 函數(shù)你也可以對(duì)insert函數(shù)復(fù)用

void push_back(char ch)
{
	insert(_size, ch);
}

+=重載

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

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

operator+= 運(yùn)算符重載用于在現(xiàn)有字符串后追加字符或 C-style 字符串?,F(xiàn)在來解釋這個(gè)函數(shù)的實(shí)現(xiàn):

string& operator+=(char ch):這是 operator+= 運(yùn)算符重載的第一個(gè)版本,表示該運(yùn)算符將在字符串末尾追加一個(gè)字符 ch。在這個(gè)版本中,直接調(diào)用了 push_back 函數(shù),將字符 ch 添加到字符串末尾。
string& operator+=(const char* str):這是 operator+= 運(yùn)算符重載的第二個(gè)版本,表示該運(yùn)算符將在字符串末尾追加一個(gè) C-style 字符串 str。在這個(gè)版本中,直接調(diào)用了 append 函數(shù),將 C-style 字符串 str 添加到字符串末尾。

在兩個(gè)版本的實(shí)現(xiàn)中,都返回當(dāng)前 std::string 對(duì)象的引用,以支持鏈?zhǔn)秸{(diào)用。

7.9 string類erase函數(shù)

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

	if (len == npos || pos + len >= _size)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}
}

erase 函數(shù)用于從字符串中刪除指定位置開始的一定長度的字符?,F(xiàn)在來解釋這個(gè)函數(shù)的實(shí)現(xiàn):

void erase(size_t pos, size_t len = npos):這是 std::string 類中的 erase 函數(shù)的聲明,表示該函數(shù)將從指定位置 pos 開始刪除一定長度 len 的字符。pos 是傳入的參數(shù),表示刪除的起始位置的索引;len 是要?jiǎng)h除的字符個(gè)數(shù),默認(rèn)值為 npos,表示刪除從起始位置開始的所有字符。
assert(pos < _size);:使用 assert 斷言來確保刪除的起始位置 pos 不超過字符串的實(shí)際大小 _size。如果斷言失?。╬os 大于等于 _size),則會(huì)觸發(fā)斷言失敗錯(cuò)誤,幫助調(diào)試找到錯(cuò)誤的位置。
if (len == npos || pos + len >= _size):檢查是否要?jiǎng)h除從起始位置 pos 開始的所有字符(即 len 等于 npos),或者是否要?jiǎng)h除的字符個(gè)數(shù)超過字符串末尾(即 pos + len 大于等于 _size)。如果是其中一種情況,表示要?jiǎng)h除從 pos 開始的所有字符或從 pos 開始直到末尾的所有字符。
_str[pos] = '\0'; 和 _size = pos;:在上述情況下,將字符串從位置 pos 處截?cái)?,即將字符?shù)組的第 pos 個(gè)字符設(shè)置為空字符 ‘\0’,并更新字符串的實(shí)際大小 _size 為 pos,以反映刪除后的新大小。
else:如果要?jiǎng)h除的字符個(gè)數(shù)小于字符串末尾的字符個(gè)數(shù),則需要將后面的字符向前移動(dòng)。
strcpy(_str + pos, _str + pos + len);:將從位置 pos + len 開始的字符復(fù)制到位置 pos,覆蓋掉要?jiǎng)h除的字符。
_size -= len;:刪除字符后,將字符串的實(shí)際大小 _size 減去 len,以反映刪除后的新大小。

7.10 string類erase函數(shù)

size_t find(char ch, size_t pos = 0) const
{
	assert(pos < _size);

	for (size_t i = pos; i < _size; ++i)
	{
		if (ch == _str[i])
		{
			return i;
		}
	}
	return npos;
}

find 函數(shù)用于在字符串中查找指定字符或子串,并返回其位置?,F(xiàn)在來解釋這個(gè)函數(shù)的實(shí)現(xiàn):

size_t find(char ch, size_t pos = 0) const:這是 std::string 類中的 find 函數(shù)的第一個(gè)版本,表示該函數(shù)將在字符串中從位置 pos 開始查找字符 ch。pos 是傳入的參數(shù),表示查找的起始位置的索引,默認(rèn)值為 0,表示從字符串的開頭開始查找。
assert(pos < _size);:使用 assert 斷言來確保查找的起始位置 pos 不超過字符串的實(shí)際大小 _size。如果斷言失?。╬os 大于等于 _size),則會(huì)觸發(fā)斷言失敗錯(cuò)誤,幫助調(diào)試找到錯(cuò)誤的位置。
在這個(gè)版本中,使用了簡單的循環(huán)遍歷,從位置 pos 開始遍歷字符串,查找是否存在字符 ch。如果找到了,就返回該字符的位置索引;如果未找到,則返回 npos。

size_t find(const char* sub, size_t pos = 0) const
{
	assert(sub);
	assert(pos < _size);

	const char* ptr = strstr(_str + pos, sub);
	if (ptr == nullptr)
	{
		return npos;
	}
	else
	{
		return ptr - _str;
	}
}

size_t find(const char* sub, size_t pos = 0) const:這是 std::string 類中的 find 函數(shù)的第二個(gè)版本,表示該函數(shù)將在字符串中從位置 pos 開始查找子串 sub。sub 是傳入的參數(shù),表示要查找的子串;pos 是傳入的參數(shù),表示查找的起始位置的索引,默認(rèn)值為 0,表示從字符串的開頭開始查找。
assert(sub);:使用 assert 斷言來確保傳入的子串 sub 不為空指針。如果斷言失?。╯ub 為空指針),則會(huì)觸發(fā)斷言失敗錯(cuò)誤,幫助調(diào)試找到錯(cuò)誤的位置。
assert(pos < _size);:同樣,使用 assert 斷言來確保查找的起始位置 pos 不超過字符串的實(shí)際大小 _size。
在這個(gè)版本中,使用 strstr 函數(shù)在字符串中查找子串 sub,如果找到了,就返回子串的位置索引;如果未找到,則返回 npos。

7.11 string類substr 函數(shù)

string substr(size_t pos, size_t len = npos) const
{
	assert(pos < _size);
	size_t realLen = len;
	if (len == npos || pos + len > _size)
	{
		realLen = _size - pos;
	}

	string sub;
	for (size_t i = 0; i < realLen; ++i)
	{
		sub += _str[pos + i];
	}

	return sub;
}

substr 函數(shù)用于從字符串中提取子串,從指定位置 pos 開始,并且可選地指定子串的長度 len?,F(xiàn)在來解釋這個(gè)函數(shù)的實(shí)現(xiàn):

string substr(size_t pos, size_t len = npos) const:這是 std::string 類中的 substr 函數(shù)的聲明,表示該函數(shù)將從指定位置 pos 開始提取子串,并且可選地指定子串的長度 len。pos 是傳入的參數(shù),表示提取子串的起始位置的索引;len 是傳入的參數(shù),表示要提取的子串的長度,默認(rèn)值為 npos,表示提取從起始位置 pos 開始的所有字符。
assert(pos < _size);:使用 assert 斷言來確保提取子串的起始位置 pos 不超過字符串的實(shí)際大小 _size。如果斷言失?。╬os 大于等于 _size),則會(huì)觸發(fā)斷言失敗錯(cuò)誤,幫助調(diào)試找到錯(cuò)誤的位置。
size_t realLen = len;:定義一個(gè)變量 realLen,用于存儲(chǔ)實(shí)際要提取的子串的長度。初始值為傳入的參數(shù) len。
if (len == npos || pos + len > _size):檢查是否要提取從起始位置 pos 開始的所有字符(即 len 等于 npos),或者是否要提取的字符個(gè)數(shù)超過字符串末尾(即 pos + len 大于等于 _size)。如果是其中一種情況,表示要提取從 pos 開始的所有字符或從 pos 開始直到末尾的所有字符。此時(shí),將 realLen 更新為從 pos 開始到末尾的字符個(gè)數(shù)。創(chuàng)建一個(gè)名為 sub 的新的 std::string 對(duì)象,用于存儲(chǔ)提取的子串。使用循環(huán)從位置 pos 開始,逐個(gè)字符地將子串添加到 sub 中。返回提取的子串 sub。

7.12 string類比較運(yùn)算符重載

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

這是大于運(yùn)算符 > 的重載版本,表示該運(yùn)算符用于比較當(dāng)前字符串與另一個(gè)字符串 s 的大小關(guān)系。在這個(gè)版本中,使用 strcmp 函數(shù)比較兩個(gè)字符串 _str 和 s._str 的字典序。如果 _str 大于 s._str,則返回 true,否則返回 false。

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

這是等于運(yùn)算符 == 的重載版本,表示該運(yùn)算符用于比較當(dāng)前字符串與另一個(gè)字符串 s 是否相等。同樣,使用 strcmp 函數(shù)比較兩個(gè)字符串 _str 和 s._str 的內(nèi)容是否相同。如果相同,返回 true,否則返回 false。

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

這是大于等于運(yùn)算符 >= 的重載版本,表示該運(yùn)算符用于比較當(dāng)前字符串是否大于或等于另一個(gè)字符串 s。在這個(gè)版本中,直接使用已經(jīng)定義好的大于運(yùn)算符 > 和等于運(yùn)算符 == 進(jìn)行組合,如果當(dāng)前字符串大于 s 或者與 s 相等,則返回 true,否則返回 false。

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

這是小于等于運(yùn)算符 <= 的重載版本,表示該運(yùn)算符用于比較當(dāng)前字符串是否小于或等于另一個(gè)字符串 s。同樣,直接使用已經(jīng)定義好的大于等于運(yùn)算符 >= 進(jìn)行取反,如果當(dāng)前字符串小于 s,則返回 true,否則返回 false。

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

這是小于運(yùn)算符 < 的重載版本,表示該運(yùn)算符用于比較當(dāng)前字符串是否小于另一個(gè)字符串 s。同樣,直接使用已經(jīng)定義好的大于等于運(yùn)算符 >= 進(jìn)行取反,如果當(dāng)前字符串不大于等于 s,則說明當(dāng)前字符串小于 s,返回 true,否則返回 false。

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

這是不等于運(yùn)算符 != 的重載版本,表示該運(yùn)算符用于比較當(dāng)前字符串是否不等于另一個(gè)字符串 s。同樣,直接使用已經(jīng)定義好的等于運(yùn)算符 == 進(jìn)行取反,如果當(dāng)前字符串與 s 不相等,則返回 true,否則返回 false。

其實(shí)和之前類和對(duì)象的文章中講到的日期類比較運(yùn)算符重載一樣,先實(shí)現(xiàn)> ==< ==后面的都可以進(jìn)行復(fù)用。

7.13 string類流插入<<和流提取>>重載

首先這里要注意的是,流插入和流提取在這里定義為全局函數(shù),因此我們不要再類中定義,而是在類外,即全局定義。這樣定義的運(yùn)算符重載函數(shù)不屬于類的成員,因此在其實(shí)現(xiàn)中不能直接訪問類的私有成員,而需要通過類的公有接口進(jìn)行訪問。

運(yùn)算符重載函數(shù)可以作為成員函數(shù)或全局非成員函數(shù)進(jìn)行定義,具體取決于使用場景和設(shè)計(jì)需求。通常情況下,如果運(yùn)算符的操作數(shù)為類對(duì)象本身或需要直接訪問類的私有成員,可以考慮將其定義為成員函數(shù)。而如果運(yùn)算符的操作數(shù)為類對(duì)象外的其他類型,或者運(yùn)算符涉及的操作不僅限于類對(duì)象本身,可以考慮將其定義為全局非成員函數(shù)。

流插入<<

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

這是輸出運(yùn)算符 << 的重載版本,表示將 std::string 類對(duì)象 s 輸出到輸出流 out 中。

使用一個(gè)循環(huán)遍歷 s 中的每個(gè)字符,并將每個(gè)字符依次輸出到輸出流 out 中。最后,將輸出流 out 返回,以支持鏈?zhǔn)捷敵觥?/p>

istream& operator>>(istream& in, string& s)
{
	s.clear();

	char ch;
	ch = in.get();

	const size_t N = 32;
	char buff[N];
	size_t i = 0;

	while (ch != ' ' && ch != '\n')
	{
		buff[i++] = ch;
		if (i == N - 1)
		{
			buff[i] = '\0';
			s += buff;
			i = 0;
		}

		ch = in.get();
	}

	buff[i] = '\0';
	s += buff;

	return in;
}

這是輸入運(yùn)算符 >> 的重載版本,表示將輸入流 in 中的數(shù)據(jù)讀取并存儲(chǔ)到 std::string 類對(duì)象 s 中。

首先調(diào)用 s.clear() 函數(shù),將 s 的內(nèi)容清空,以便接收新的輸入。然后,使用一個(gè)循環(huán)從輸入流 in 中逐個(gè)讀取字符 ch。如果字符 ch 不是空格或換行符,就將字符添加到一個(gè)臨時(shí)字符數(shù)組 buff 中,并增加索引 i。一旦 buff 已滿(i == N - 1),就將 buff 最后一個(gè)元素設(shè)為空字符 ‘\0’,然后將 buff 添加到 s 中,然后將索引 i 重置為 0,以繼續(xù)接收后續(xù)字符。如果字符 ch 是空格或換行符,說明一個(gè)單詞的輸入結(jié)束,將 buff 最后一個(gè)元素設(shè)為空字符 ‘\0’,然后將 buff 添加到 s 中。最后,將輸入流 in 返回,以支持鏈?zhǔn)捷斎搿?mark hidden color="red">文章來源:http://www.zghlxwxcb.cn/news/detail-618443.html

8.string類的模擬實(shí)現(xiàn)(完整代碼)

#include<iostream>
#include<string.h>
#include<assert.h>
using namespace std;
namespace mystring
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}


		const_iterator begin() const
		{
			return _str;
		}

		const_iterator end() const
		{
			return _str + _size;
		}


		string(const char* str = "")
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];

			strcpy(_str, str);
		}

		// 傳統(tǒng)寫法
		//string(const string& s)
		//	:_str(new char[s._capacity+1])
		//	, _size(s._size)
		//	, _capacity(s._capacity)
		//{
		//	strcpy(_str, s._str);
		//}

		// 現(xiàn)代寫法 
		void swap(string& tmp)
		{
			::swap(_str, tmp._str);
			::swap(_size, tmp._size);
			::swap(_capacity, tmp._capacity);
		}


		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			swap(tmp);
		}

		//string& operator=(const string& s)
		//{
		//	if (this != &s)
		//	{
		//		//string tmp(s._str);
		//		string tmp(s);
		//		swap(tmp); // this->swap(tmp);
		//	}

		//	return *this;
		//}


		string& operator=(string s)
		{
			swap(s);
			return *this;
		}

		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

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

		size_t size() const
		{
			return _size;
		}

		size_t capacity() const
		{
			return _capacity;
		}

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

			return _str[pos];
		}

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

			return _str[pos];
		}

		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;

				_str = tmp;
				_capacity = n;
			}
		}

		void resize(size_t n, char ch = '\0')
		{
			if (n > _size)
			{
				// 插入數(shù)據(jù)
				reserve(n);
				for (size_t i = _size; i < n; ++i)
				{
					_str[i] = ch;
				}
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				// 刪除數(shù)據(jù)
				_str[n] = '\0';
				_size = n;
			}
		}

		void push_back(char ch)
		{
			// 滿了就擴(kuò)容
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
			//insert(_size, ch);
		}

		void append(const char* str)
		{
			size_t len = strlen(str);

			// 滿了就擴(kuò)容
			if (_size + len > _capacity)
			{
				reserve(_size+len);
			}

			strcpy(_str + _size, str);
			//strcat(_str, str); 需要找\0,效率低
			_size += len;
			//insert(_size, str);
		}


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

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

		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);

			// 滿了就擴(kuò)容
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

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

			_str[pos] = ch;
			++_size;

			return *this;
		}

		string& 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ù)
			size_t end = _size + len;
			while (end >= pos + len)
			{
				_str[end] = _str[end - len];
				--end;
			}

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

			return *this;
		}

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

			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
		}

		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

		size_t find(char ch, size_t pos = 0) const
		{
			assert(pos < _size);

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

			return npos;
		}

		size_t find(const char* sub, size_t pos = 0) const
		{
			assert(sub);
			assert(pos < _size);

			// kmp/bm
			const char* ptr = strstr(_str + pos, sub);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		string substr(size_t pos, size_t len = npos) const
		{
			assert(pos < _size);
			size_t realLen = len;
			if (len == npos || pos + len > _size)
			{
				realLen = _size - pos;
			}

			string sub;
			for (size_t i = 0; i < realLen; ++i)
			{
				sub += _str[pos + i];
			}

			return sub;
		}

		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);
		}
	private:
		size_t _capacity;
		size_t _size;
		char* _str;
	public:
		const static size_t npos = -1;
	};

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

		return out;
	}


	istream& operator>>(istream& in, string& s)
	{
		s.clear();

		char ch;
		ch = in.get();

		const size_t N = 32;
		char buff[N];
		size_t i = 0;

		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == N - 1)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}

			ch = in.get();
		}

		buff[i] = '\0';
		s += buff;

		return in;
	}
}

結(jié)語

有興趣的小伙伴可以關(guān)注作者,如果覺得內(nèi)容不錯(cuò),請給個(gè)一鍵三連吧,蟹蟹你喲?。?!
制作不易,如有不正之處敬請指出
感謝大家的來訪,UU們的觀看是我堅(jiān)持下去的動(dòng)力
在時(shí)間的催化劑下,讓我們彼此都成為更優(yōu)秀的人吧?。。?span toymoban-style="hidden">文章來源地址http://www.zghlxwxcb.cn/news/detail-618443.html

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

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

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

相關(guān)文章

  • C++初階之一篇文章教會(huì)你list(理解和使用)

    C++初階之一篇文章教會(huì)你list(理解和使用)

    在C++標(biāo)準(zhǔn)庫中, std::list 是一個(gè)雙向鏈表容器,用于存儲(chǔ)一系列元素。與 std::vector 和 std::deque 等容器不同, std::list 使用鏈表的數(shù)據(jù)結(jié)構(gòu)來組織元素,因此在某些操作上具有獨(dú)特的優(yōu)勢和性能特點(diǎn)。以下是關(guān)于 std::list 的詳細(xì)介紹: 雙向鏈表結(jié)構(gòu): std::list 內(nèi)部使用雙向鏈表來

    2024年02月13日
    瀏覽(29)
  • 一篇文章讓你完全掌握使用Git推送代碼到新版GitCode

    一篇文章讓你完全掌握使用Git推送代碼到新版GitCode

    GitCode是一款基于Git的在線代碼托管和協(xié)作工具,提供代碼版本控制、代碼托管、代碼評(píng)審、項(xiàng)目管理等功能。它支持多種編程語言,包括 Java 、 Python 、 C++ 等,可幫助開發(fā)者高效協(xié)作,提高代碼質(zhì)量和開發(fā)效率。GitCode還提供豐富的 API 接口,支持與其他系統(tǒng)集成,方便開發(fā)

    2024年03月25日
    瀏覽(24)
  • 誰說配置難?這篇文章讓你輕松掌握xilinx 7系列FPGA配置技巧

    誰說配置難?這篇文章讓你輕松掌握xilinx 7系列FPGA配置技巧

    ??本文旨在通過講解不同模式的原理圖連接方式,進(jìn)而配置用到引腳的含義(手冊上相關(guān)引腳含義有四、五頁,通過本文理解基本上能夠記住所有引腳含義以及使用場景),熟悉xilinx 7系列配置流程,以及設(shè)計(jì)原理圖時(shí)需要注意的一些事項(xiàng),比如flash與FPGA的上電時(shí)序。 ??x

    2024年02月06日
    瀏覽(125)
  • 【數(shù)據(jù)結(jié)構(gòu)——順序表】線性表很難嘛?這篇文章能讓你輕松掌握順序表

    【數(shù)據(jù)結(jié)構(gòu)——順序表】線性表很難嘛?這篇文章能讓你輕松掌握順序表

    線性表是一種在實(shí)際中廣泛使用的數(shù)據(jù)結(jié)構(gòu),常見的線性表:順序表、鏈表、棧、隊(duì)列、字符串…。線性表在邏輯上是線性結(jié)構(gòu),也就是說是連續(xù)的一條直線。但是在物理結(jié)構(gòu)上并不一定是連續(xù)的,線性表在物理上存儲(chǔ)時(shí),通常以數(shù)組和鏈?zhǔn)降慕Y(jié)構(gòu)的形式存儲(chǔ)。 是n個(gè)具有相

    2024年02月07日
    瀏覽(30)
  • Spring Boot 3 + JWT + Security 聯(lián)手打造安全帝國:一篇文章讓你掌握未來!

    Spring Boot 3 + JWT + Security 聯(lián)手打造安全帝國:一篇文章讓你掌握未來!

    Spring Security 已經(jīng)成為 java 后臺(tái)權(quán)限校驗(yàn)的第一選擇.今天就通過讀代碼的方式帶大家深入了解一下Security,本文主要是基于開源項(xiàng)目spring-boot-3-jwt-security來講解Spring Security + JWT(Json Web Token).實(shí)現(xiàn)用戶鑒權(quán),以及權(quán)限校驗(yàn). 所有代碼基于 jdk17+ 構(gòu)建.現(xiàn)在讓我們開始吧! Springboot 3.0 Spri

    2024年02月07日
    瀏覽(89)
  • 【C++】一篇文章帶你深入了解string

    【C++】一篇文章帶你深入了解string

    C語言中,字符串是以’\\0’結(jié)尾的一些字符的集合,為了操作方便,C標(biāo)準(zhǔn)庫中提供了一些str系列的庫函數(shù),但是這些庫函數(shù)與字符串是分離開的,不太符合OOP的思想,而且底層空間需要用戶自己管理,稍不留神可能還會(huì)越界訪問。 string的文檔介紹 字符串是表示字符序列的類

    2024年04月08日
    瀏覽(27)
  • 【C++算法圖解專欄】一篇文章帶你掌握差分算法

    【C++算法圖解專欄】一篇文章帶你掌握差分算法

    ?個(gè)人博客:https://blog.csdn.net/Newin2020?spm=1011.2415.3001.5343 ??專欄定位:為 0 基礎(chǔ)剛?cè)腴T數(shù)據(jù)結(jié)構(gòu)與算法的小伙伴提供詳細(xì)的講解,也歡迎大佬們一起交流~ ??專欄地址:https://blog.csdn.net/Newin2020/article/details/126445229 ??如果有收獲的話,歡迎點(diǎn)贊??收藏??,您的支持就是我創(chuàng)

    2024年04月11日
    瀏覽(19)
  • 【C++】string的接口從生澀到靈活使用——這篇文章就夠了

    【C++】string的接口從生澀到靈活使用——這篇文章就夠了

    目錄 第一類題目:反轉(zhuǎn)字符串類型? 1. 反轉(zhuǎn)字母(初級(jí)) 正向迭代器 ? 題目講解 ? ?2.反轉(zhuǎn)字母(中級(jí)) reverse和size 題目講解 ? 3.反轉(zhuǎn)字母(高級(jí))? find和resize ? 題目講解 ? 第二類題目:一個(gè)字符串的子字符串 1.模擬實(shí)現(xiàn)strStr() KMP算法 理論講解 ? 代碼實(shí)現(xiàn) ? 使用next數(shù)組

    2024年01月22日
    瀏覽(20)
  • 一篇文章讓你搞懂內(nèi)存函數(shù)

    一篇文章讓你搞懂內(nèi)存函數(shù)

    庫函數(shù)memcmp介紹 函數(shù)memcpy從source的位置開始向后復(fù)制num個(gè)字節(jié)的數(shù)據(jù)到destination的內(nèi)存位置。 這個(gè)函數(shù)在遇到 ‘\\0’ 的時(shí)候并不會(huì)停下來。 如果source和destination有任何的重疊,復(fù)制的結(jié)果都是未定義的。 庫函數(shù)memcmp的代碼形式 看代碼 memcmp將arr1中的內(nèi)容拷貝到arr2中,總共

    2024年02月17日
    瀏覽(29)
  • 讀這篇文章讓你徹底了解Redis

    讀這篇文章讓你徹底了解Redis

    你好,我是Redis,一個(gè)叫Antirez的男人把我?guī)У搅诉@個(gè)世界上。 說起我的誕生,跟關(guān)系數(shù)據(jù)庫MySQL還挺有淵源的。 在我還沒來到這個(gè)世界上的時(shí)候,MySQL過的很辛苦,互聯(lián)網(wǎng)發(fā)展的越來越快,它容納的數(shù)據(jù)也越來越多,用戶請求也隨之暴漲,而每一個(gè)用戶請求都變成了對(duì)它的一

    2024年02月04日
    瀏覽(28)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包