欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++中String的語法及常用接口的底層實(shí)現(xiàn)詳解

 更新時(shí)間:2023年06月21日 10:16:52   作者:Ggggggtm  
在C語言中,string是一個(gè)標(biāo)準(zhǔn)庫類(class),用于處理字符串,它提供了一種更高級(jí)、更便捷的字符串操作方式,string 類提供了一系列成員函數(shù)和重載運(yùn)算符,以便于對(duì)字符串進(jìn)行操作和處理,本編文章會(huì)對(duì)C++中的 string 進(jìn)行詳解,希望本篇文章會(huì)對(duì)你有所幫助

一、string類

在學(xué)習(xí) string 前,我們不妨先來了解一下 string 類到底是什么,有什么用呢?下圖是C++標(biāo)準(zhǔn)庫中的對(duì) string 內(nèi)容:

what???沒錯(cuò),C++標(biāo)準(zhǔn)庫都是英語解釋。我們也應(yīng)該試著去適應(yīng),不懂的可以查閱。當(dāng)然,在這里我就直接給出翻譯,主要是以下內(nèi)容:

字符串是表示字符序列的類;
  • 標(biāo)準(zhǔn)的字符串類提供了對(duì)此類對(duì)象的支持,其接口類似于標(biāo)準(zhǔn)字符容器的接口,但添加了專門用于操作單字節(jié)字符字符串的設(shè)計(jì)特性。
  • string類是使用char(即作為它的字符類型,使用它的默認(rèn)char_traits和分配器類型(關(guān)于模板的更多信息,請(qǐng)參閱basic_string)。
  • string類是basic_string模板類的一個(gè)實(shí)例,它使用char來實(shí)例化basic_string模板類,并用char_traits和allocator作為basic_string的默認(rèn)參數(shù)(根于更多的模板信息請(qǐng)參考basic_string)。
  • 注意,這個(gè)類獨(dú)立于所使用的編碼來處理字節(jié):如果用來處理多字節(jié)或變長字符(如UTF-8)的序列,這個(gè)類的所有成員(如長度或大小)以及它的迭代器,將仍然按照字節(jié)(而不是實(shí)際編碼的字符)來操作。

了解到上面的內(nèi)容后,我們要開始真正的學(xué)習(xí) string 的用法了。

二、string的常用見用法

2、1 string對(duì)象的構(gòu)造

2、1、1 string對(duì)象的構(gòu)造的使用方法

最為常用的無非就是我們用串string來構(gòu)造一個(gè)對(duì)象,也就是存儲(chǔ)一個(gè)字符,常用的方法有如下幾點(diǎn):

  • string()——構(gòu)造空的 string 類對(duì)象,即空字符串;
  • string(const char* s)——用 char* 來構(gòu)造 string 類對(duì)象;
  • string(size_t n, char c)——string類對(duì)象中包含n個(gè)字符c;
  • string(const string&s)——拷貝構(gòu)造函數(shù)。

下面是使用方法所對(duì)應(yīng)的實(shí)例,幫助更好的理解其用法。

根據(jù)上面的實(shí)例和對(duì)應(yīng)的輸出結(jié)果,我們可以更好的理解。 

2、1、2 string()的底層實(shí)現(xiàn)

構(gòu)造空串,其有效字符長度為0,但是實(shí)際上是開辟了一個(gè)字節(jié)的空間存儲(chǔ) ‘\0’ 的。具體如下:

string()
		:_str(new char[1])
		, _size(0)
		,_capacity(0)
	{
	}

2、1、3 string(const char* s)的底層實(shí)現(xiàn)

我們這里就給出以上兩個(gè)底層的構(gòu)造實(shí)現(xiàn),其余兩個(gè)類似,就不再給出。具體實(shí)現(xiàn)如下:

string(const char* str = "") //默認(rèn)空串。注意:空串是以 \0 結(jié)尾
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_size + 1];
			strcpy(_str, str);
		}

2、2 string對(duì)象的修改操作

當(dāng)我們構(gòu)建好對(duì)象后,我們接下來就要往對(duì)應(yīng)的字符串對(duì)象進(jìn)行修改操作了。常用的修改操作無非就是插入和查找,具體有如下幾種常見用法:

  • push_back——在字符串后尾插字符c;
  • insert——在pos位置插入n個(gè)字符或者插入一個(gè)字符串;
  • append ——在字符串后追加一個(gè)字符串 ;
  • operator+=——在字符串后追加字符或者字符串str; c_str——返回C格式字符串;
  • find——從字符串pos位置開始往后找字符c,返回該字符在字符串中的位置。

我們?cè)倏雌渚唧w的使用方法實(shí)例,如下圖:

上圖為常用的修改用法。當(dāng)然,其他的用法還有很多。如果想了解的過可去C++官網(wǎng)(cppreference)或者 cplusplus 的標(biāo)準(zhǔn)庫中查詢。具體也可看下圖:

insert

operator+= 

find 

2、3 string對(duì)象的容量操作

在平常對(duì)字符串的操作中,我們也經(jīng)常需要去了解到字符串的實(shí)際長度為多少,或者改數(shù)組到底能夠存下多長的字符串,又或是修改字符串的長度和空間大小。C++的string類中,這些操作都提供了相應(yīng)的接口,具體如下:

  • size——返回字符串有效字符長度;
  • length——返回字符串有效字符長度 ;
  • empty——檢測(cè)字符串釋放為空串,是返回true,否則返回false;
  • reserve——為字符串預(yù)留空間;
  • resize——將有效字符的個(gè)數(shù)該成n個(gè),多出的空間用字符c填充;
  • capacity——返回空間總大?。?/strong>
  • clear——清空有效字符;

我們發(fā)現(xiàn)。size和length的功能一樣的。確實(shí)都是求字符串的有效長度。那我們接著看其具體的實(shí)例:

注意:capacity返回的是空間的總大小,size和length返回的是字符串的實(shí)際有效長度。 兩者是有所區(qū)別的。一個(gè)字符串的capacity是有底層的具體實(shí)現(xiàn)決定的,不同的編譯器可能實(shí)現(xiàn)的是不同的。

2、4 string對(duì)象的訪問和遍歷操作

string對(duì)象的訪問,支持像數(shù)組一樣使用 [] 進(jìn)行訪問,也可通過迭代器進(jìn)行訪問,具體有如下用法:

  • operator[]——返回pos位置的字符,const string類對(duì)象調(diào)用;
  • begin+ end——begin獲取一個(gè)字符的迭代器 + end獲取最后一個(gè)字符下一個(gè)位置的迭 代器;
  • 范圍for——C++11支持更簡潔的范圍for的新遍歷方式。

其具體的用法實(shí)例如下:

范圍for的底層實(shí)現(xiàn)就是用迭代器來實(shí)現(xiàn)的。寫起來更加的便捷和方便。 

以上為string中較為常用的接口。以上完全足夠我們平常的使用了,如果想要了解的更多,可參考C++的標(biāo)準(zhǔn)庫。不過string容器一共實(shí)現(xiàn)了106個(gè)接口?。。∑渲写蟛糠侄际侨哂嗟?。這也是很多人吐槽string類實(shí)現(xiàn)的過于復(fù)雜和冗余的一個(gè)重要原因。所以在查看時(shí),我們只需要看自己想要了解的接口即可。 

三、string常用結(jié)構(gòu)的底層實(shí)現(xiàn)

3、1 初建結(jié)構(gòu)

我們通過上述的構(gòu)造,不難發(fā)現(xiàn)也不難理解string的底層其實(shí)就是一個(gè)字符指針,該指針指向一個(gè)數(shù)組。當(dāng)然,我們還需要兩個(gè)變量來維護(hù)其有效長度(_size)數(shù)組容量(_capacity)

其次,我們自己實(shí)現(xiàn)的string類為了區(qū)分std命名空間,我們可自己設(shè)置一個(gè)命名空間。處型的模擬實(shí)現(xiàn)如下:

namespace gtm
{
	class string
	{
     public:
        //string()
		//	:_str(new char[1])
		//	, _size(0)
		//	,_capacity(0)
		//{
		//}
		//string(const char* str)
		//	:_str(new char[strlen(str) + 1])  //三次strlen函數(shù),效率低。
		//	,_size(strlen(str))
		//	,_capacity(strlen(str))
		//{
		//	strcpy(_str, str);
		//}
		// 不再使用strlen函數(shù),初始化列表與變量聲明順序固定
		string(const char* str = "") //默認(rèn)空串。注意:空串是以 \0 結(jié)尾
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_size + 1];
			strcpy(_str, str);
		}
        ~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}
     private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};

注意,我們上述省略了無參的構(gòu)造。原因是我們?cè)谧址臉?gòu)造中有缺省參數(shù),即為空串。

3、2 返回大小和容量

這兩個(gè)部分,是比較容易實(shí)現(xiàn)的兩部分。同時(shí)也是較為常用的兩部分。具體如下:

size_t size() const
		{
			return _size;
		}
		size_t capacity() const
		{
			return _capacity;
		}

3、3 拷貝構(gòu)造和賦值重載

這兩部分較為復(fù)雜的兩部分。其中均需要深拷貝去實(shí)現(xiàn)完成,而淺拷貝是不可以的。注意:拷貝構(gòu)造使用一個(gè)已定義變量去初始化另一個(gè)變量,賦值重載是兩個(gè)已定義變量進(jìn)行賦值。

具體實(shí)現(xiàn)如下:

//深拷貝
		//string(const string& s)
		//	:_str(new char[s._capacity+1])
		//	,_size(s._size)
		//	,_capacity(s._capacity)
		//{
		//	strcpy(_str, s._str);
		//}
		void swap(string& tmp)
		{
			//調(diào)用全局的swap
			::swap(_str, tmp._str);
			::swap(_size, tmp._size);
			::swap(_capacity, tmp._capacity);
		}
		//借助變量tmp
		string(const string& s)
			:_str(nullptr) 
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			swap(tmp);
		}
		//賦值
		//string& operator=(const string& s)
		//{
		//	if(this == &s)
		//	{
		//		return *this;
		//	}
		//	//先開空間拷貝數(shù)據(jù),以防new失敗銷毀原來的空間
		//	char* tmp = new char[s._capacity + 1];
		//	strcpy(tmp, s._str);
		//	delete[] _str;
		//	_str = tmp;
		//	_size = s._size;
		//	_capacity = s._capacity;
		//	return *this;
		//	//delete[] _str;
		//	//_str = new char[s._capacity + 1];
		//	//strcpy(_str, s._str);
		//	//_size = s._size;
		//	//_capacity = s._capacity;
		//	return *this;
		//}
		//string& operator=(const string& s)
		//{
		//	if(this == &s)
		//	{
		//		return *this;
		//	}
		//	string tmp(s._str);
		//	swap(tmp);
		//  return *this;
		//}
		string& operator=(string s)
		{
			if (this == &s)
			{
				return *this;
			}
			swap(s);
			return *this;
		}

上述的輔助重載我們巧妙地借助了臨時(shí)變量s。當(dāng)賦值完成后,出了作用域s會(huì)自動(dòng)調(diào)用戲后進(jìn)行銷毀,這里是需要反復(fù)理解的。

3、4 擴(kuò)容(reserve)

我們可簡單的理解reserve為擴(kuò)容(擴(kuò)容的前提為要求的容量比原來的大),但是我們要記得把字符數(shù)組中原有的內(nèi)容拷貝過來,并且釋放之前所動(dòng)態(tài)開辟的空間。 具體實(shí)現(xiàn)如下:

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

3、5 插入(push_back、append、operator+=、insert)

插入的實(shí)現(xiàn),主要的點(diǎn)就是是否要進(jìn)行擴(kuò)容。其次,當(dāng)我們實(shí)現(xiàn)push_back和append后,其他的均可復(fù)用這兩個(gè)結(jié)構(gòu)進(jìn)行實(shí)現(xiàn)。具體實(shí)現(xiàn)如下:

        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 (len + _size > _capacity)
			{
				reserve(len + _size >= _capacity * 2 ? len + _size : _capacity * 2);
			}
			strcpy(_str + _size, str);
			_size += len;
		}
		void append(const string& s)
		{
			append(s._str);
		}
		void append(int n, char ch)
		{
			reserve(_size + n);
			for (int i = 0; i < n; i++)
			{
				push_back(ch);
			}
		}
		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);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			//注意,當(dāng)運(yùn)算數(shù)一個(gè)是有符號(hào),另一個(gè)是無符號(hào)時(shí),有符號(hào)的運(yùn)算數(shù)會(huì)強(qiáng)制類型轉(zhuǎn)換為無符號(hào)數(shù)。pos等于0的位置插入,end--后為超大數(shù)據(jù),會(huì)出錯(cuò)。
			//int end = _size;
			//while (end >= (int)pos)
			//{
			//	_str[end + 1] = _str[end];
			//	end--;
			//}
			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 (len + _size > _capacity)
			{
				reserve(len + _size >= _capacity * 2 ? len + _size : _capacity * 2);
			}
			size_t end = _size + len;
			while (end >= pos+len)
			{
				_str[end] = _str[end - len];
				end--;
			}
			for (int i = pos,j=0; j < len;j++, i++)
			{
				_str[i] = str[j];
			}
			_size += len;
			return *this;
		}

3、6 刪除(erase)

我們這里實(shí)現(xiàn)的從某個(gè)位置開始刪除,刪除長度為 len 的字符。len有一個(gè)缺省參數(shù),為npos(npos是一個(gè)很大的數(shù),也就是不傳參給 len 的話,默認(rèn)刪除到最后)。如果 len 本就很大,刪除的長度超過從pos開始所剩余的長度,那么默認(rèn)也是pos后的刪除完。那么我們看其具體的實(shí)現(xiàn)。

void erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);
			if (len == npos || _size - pos <= len)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
		}

3、7 查找(find)

查找的話,主要常用的就兩個(gè):從pos位置開始查找,查找的內(nèi)容可能是一個(gè)字符,也可能是一個(gè)子串。如果找到,則返回其下標(biāo)。沒找到就返回npos。具體實(shí)現(xiàn)如下:

size_t find(char ch, size_t pos = 0)const
		{
			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
		{
			const char* ret=strstr(_str + pos, sub);
			if (ret == nullptr)
			{
				return npos;
			}
			else
			{
				return ret - _str;
			}
		}

3、8 返回子串(substr)

返子串也是我們經(jīng)常需要的一個(gè)接口。返回子串就一個(gè)接口,從某個(gè)位置開始查找,查找長度為 len 的字符。len有一個(gè)缺省參數(shù),為npos(npos是一個(gè)很大的數(shù),也就是不傳參給 len 的話,默認(rèn)返回到最后)。如果 len 本就很大,返回子串的長度超過從pos開始所剩余的長度,那么默認(rèn)也是pos后的子串全部返回。我們看其具體實(shí)現(xiàn):

        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 s;
			for (size_t i = 0; i < realLen; i++)
			{
				s += _str[pos + i];
			}
			return s;
		}

3、9 迭代器(iterator)

string中的迭代器底層就是指針,但是并不是所有的迭代器底層實(shí)現(xiàn)都是指針!我們直接看起底層實(shí)現(xiàn):

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;
		}

這里的begin()就是返回的字符串的首元素地址,end()返回的是字符串最后一個(gè)元素的后一個(gè)地址!

3、10 比較(>、<、>=、<=、==、!=)

字符串的比較并非比較其長度,而是與其相同位置字符的大小有關(guān),也就是我們所說的字典序。我們這里只需要實(shí)現(xiàn)其中的兩個(gè),其他均可復(fù)用。具體如下:

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);
		}

四、總結(jié)

string 在C++中算是比較重要的了,也是入門時(shí)必須所學(xué)的容器。在平常中使用的頻率較高,所以我們不僅要掌握其簡單的用法,更應(yīng)該去了解其底層的實(shí)現(xiàn)。這有助于我們后續(xù)的使用和理解。本篇文章列舉出了string中常用的語法和接口底層的底層實(shí)現(xiàn),這些都是我們應(yīng)該熟練掌握的內(nèi)容。

本篇文章講解就到這里,感謝觀看ovo~

以上就是C++中String的語法及常用接口的底層實(shí)現(xiàn)詳解的詳細(xì)內(nèi)容,更多關(guān)于C++ String的語法及接口底層實(shí)現(xiàn)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 利用Matlab實(shí)現(xiàn)繪制中秋山間秋月和皓月當(dāng)空效果

    利用Matlab實(shí)現(xiàn)繪制中秋山間秋月和皓月當(dāng)空效果

    中秋節(jié)還有三天就到了,中秋節(jié)啊,闔家團(tuán)圓的日子。本文將利用Matlab繪制中秋山間秋月和皓月當(dāng)空的動(dòng)態(tài)效果,感興趣的可以了解一下
    2022-09-09
  • 篩選法的C++實(shí)現(xiàn)

    篩選法的C++實(shí)現(xiàn)

    篩選法又稱篩法,是求不超過自然數(shù)N(N>1)的所有質(zhì)數(shù)的一種方法。據(jù)說是古希臘的埃拉托斯特尼(Eratosthenes,約公元前274~194年)發(fā)明的,又稱埃拉托斯特尼篩子
    2013-10-10
  • c++實(shí)現(xiàn)十進(jìn)制轉(zhuǎn)換成16進(jìn)制示例

    c++實(shí)現(xiàn)十進(jìn)制轉(zhuǎn)換成16進(jìn)制示例

    這篇文章主要介紹了c++實(shí)現(xiàn)十進(jìn)制轉(zhuǎn)換成16進(jìn)制示例,需要的朋友可以參考下
    2014-05-05
  • C++示例分析內(nèi)聯(lián)函數(shù)與引用變量及函數(shù)重載的使用

    C++示例分析內(nèi)聯(lián)函數(shù)與引用變量及函數(shù)重載的使用

    為了消除函數(shù)調(diào)用的時(shí)空開銷,C++ 提供一種提高效率的方法,即在編譯時(shí)將函數(shù)調(diào)用處用函數(shù)體替換,類似于C語言中的宏展開。這種在函數(shù)調(diào)用處直接嵌入函數(shù)體的函數(shù)稱為內(nèi)聯(lián)函數(shù)(Inline Function),又稱內(nèi)嵌函數(shù)或者內(nèi)置函數(shù)
    2022-08-08
  • C++實(shí)現(xiàn)對(duì)輸入數(shù)字組進(jìn)行排序

    C++實(shí)現(xiàn)對(duì)輸入數(shù)字組進(jìn)行排序

    這里給大家介紹的是通過某個(gè)方法實(shí)現(xiàn)判斷命令行中輸入的數(shù)字是幾個(gè),這樣再用冒泡法排序的時(shí)候就不用擔(dān)心輸入的是幾個(gè)數(shù)字,用到的知識(shí)主要是冒泡法排序
    2015-11-11
  • C++使用tinyxml庫處理XML文件

    C++使用tinyxml庫處理XML文件

    TinyXML是一個(gè)開源的解析XML的解析庫,能夠用于C++,能夠在Windows或Linux中編譯,這個(gè)解析庫的模型通過解析XML文件,然后在內(nèi)存中生成DOM模型,從而讓我們很方便的遍歷這棵XML樹,本文為大家介紹的是使用tinyxml庫處理XML文件,需要的可以參考一下
    2023-07-07
  • C++實(shí)現(xiàn)通訊錄小功能

    C++實(shí)現(xiàn)通訊錄小功能

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)通訊錄小功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • 詳解C++中函數(shù)模板的定義與使用

    詳解C++中函數(shù)模板的定義與使用

    函數(shù)模板實(shí)質(zhì)就是參數(shù)化數(shù)據(jù)類型,稱這種編程模式為數(shù)據(jù)類型泛化編程。本文將通過示例來和大家一起了解下C++中函數(shù)模板的定義與使用,需要的可以參考一下
    2022-09-09
  • C++小知識(shí):用合適的工具來分析你的代碼

    C++小知識(shí):用合適的工具來分析你的代碼

    今天小編就為大家分享一篇關(guān)于C++小知識(shí):用合適的工具來分析你的代碼,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • C語言版掃雷游戲

    C語言版掃雷游戲

    這篇文章主要為大家詳細(xì)介紹了C語言版掃雷游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-05-05

最新評(píng)論