欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

string(四)————底層實(shí)現(xiàn)-創(chuàng)新互聯(lián)

目錄

成都網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)建站!專注于網(wǎng)頁(yè)設(shè)計(jì)、重慶網(wǎng)站建設(shè)、微信開(kāi)發(fā)、小程序設(shè)計(jì)、集團(tuán)企業(yè)網(wǎng)站設(shè)計(jì)等服務(wù)項(xiàng)目。核心團(tuán)隊(duì)均擁有互聯(lián)網(wǎng)行業(yè)多年經(jīng)驗(yàn),服務(wù)眾多知名企業(yè)客戶;涵蓋的客戶類型包括:資質(zhì)代辦等眾多領(lǐng)域,積累了大量豐富的經(jīng)驗(yàn),同時(shí)也獲得了客戶的一致贊譽(yù)!

引言

外層包裝

成員變量設(shè)計(jì)

接口實(shí)現(xiàn)


引言

在之前的博客中我簡(jiǎn)單介紹了string的相關(guān)使用方法和接口,現(xiàn)在我們自己來(lái)模擬實(shí)現(xiàn)一下它的底層(注:不同編譯器底層實(shí)現(xiàn)不同,這里只是其中一種的實(shí)現(xiàn))。

外層包裝

本來(lái)應(yīng)該是在外層套個(gè)basic_string的模板的,但因?yàn)槟0宀恢С致暶鞫x分離所以這里不用模板了,直接將string類放到命名空間sak中,測(cè)試函數(shù)放在頭文件中,在命名空間里面,分兩個(gè)文件鏈接。

namespace sak
{
    class string
    {
        public:
           //...
        private:
           //...
    };
    Test();
};

成員變量設(shè)計(jì)

我的 string類中成員變量設(shè)置三個(gè):char* _str , size_t size(有效數(shù)據(jù)大小) , size_t capacity(容量)

因?yàn)樽址婕暗?'\0' 問(wèn)題,要多給一個(gè)空間儲(chǔ)存 '\0',所以在開(kāi)辟空間時(shí)給_str 多開(kāi)一個(gè)字節(jié)空間,capacity不包含 '\0' 的大小,表示有效數(shù)據(jù)的容量。

class string
    {
        public:
           //...
        private:
           size_t capacity;
           size_t size;
           char*_str;
    };

接口實(shí)現(xiàn)

1、構(gòu)造函數(shù)接口

構(gòu)造可以用字符串構(gòu)造,也可以用string類型對(duì)象構(gòu)造。注意成員變量聲明的順序,為了盡可能減少strlen函數(shù)的調(diào)用,先對(duì)_capacity初始化,再對(duì)_size和_str初始化,因?yàn)槌蓡T變量在類中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后 次序無(wú)關(guān),所以要先聲明_capacity,再聲明其他的。

string(const char* str)
	:_capacity(strlen(str))
	,_size(_capacity)
	,_str(new char[_capacity+1])
{
	strcpy(_str, str);
}

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

這里不能直接將str 指針賦值給_str,雖然確實(shí)將str指向的內(nèi)容賦給了_str,但是如果參數(shù)str是常量字符串,那么后面調(diào)用push_back接口需要擴(kuò)容的話,無(wú)法對(duì)常量字符串操作,所以這里先給_str開(kāi)辟空間(上面說(shuō)了因?yàn)?'\0'的原因多開(kāi)一個(gè)空間)。

還有一種情況:無(wú)參類型的構(gòu)造

string()
		{
			_str = new char[1];
			_str[0] = '\0';
			_size = _capacity = 0;
		}

或者這里可以通過(guò)字符串類型參數(shù)的缺省來(lái)實(shí)現(xiàn)無(wú)參類型的構(gòu)造:

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

這里為什么是"",需要先辨析清楚 '\0' , " \0" , "" 三者之間的不同。

'\0' :字符\0,ASCLL碼值是0,即就代表了0

" \0" :字符串都以 '\0' 結(jié)尾,所以是\0\0,有2個(gè)?

"" : 空字符串,里面有一個(gè) \0

size_t strlen ( const char * str );

strlen函數(shù)的參數(shù)部分是const char* 類型的,會(huì)計(jì)算 '\0'之前有幾個(gè)字符。因此這里不能是

string(const char* str = '\0'或者nullptr)? 而是? string(const char* str = "")

2、析構(gòu)函數(shù)接口

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

3、c_str 接口

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

在尚未實(shí)現(xiàn)? 流輸出<< 和?? 流提取>>操作符時(shí),可以用c_str打印string類型對(duì)象。

4、size 接口

size_t size() const
		{
			return _size;
		}

5、capacity 接口

size_t capacity() const
		{
			return _capacity;
		}

這里capacity實(shí)現(xiàn)的和VS編譯器下不同,所以表現(xiàn)結(jié)果也不同

6、operator[] 接口(重載[ ])

//可讀可寫(xiě)
        char& operator[](size_t pos)
		{
			assert(pos< _size);
			return _str[pos];
		}
        
        //只讀
        const char& operator[](size_t pos)const
		{
			assert(pos< _size);
			return _str[pos];
		}

7、迭代器實(shí)現(xiàn)(char*指針?lè)绞剑?/p>

迭代器底層實(shí)現(xiàn)有很多方法,內(nèi)部類、指針.......? 這里我用指針實(shí)現(xiàn)。

typedef char* iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
string::iterator it = s2.begin();
	while (it != s2.end())
	{
		(*it)++;
		it++;
	}
	cout<< s0.c_str()<< endl;

范圍for底層其實(shí)就是迭代器,是直接用迭代器替換的,所以范圍for并沒(méi)有想象的那么神奇。

底層是將對(duì)象賦給 it變量,再將*it 賦給e,其實(shí)很簡(jiǎn)單。

8、reserve接口

當(dāng)要改變的空間大小n< capacity,對(duì)容量沒(méi)有變化,當(dāng)n< capacity ,reserve實(shí)質(zhì)上就是擴(kuò)容的實(shí)現(xiàn)。在C語(yǔ)言中擴(kuò)容有realloc,C++這里我用的new,需要自己實(shí)現(xiàn)一下擴(kuò)容。

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

				_capacity = n;
			}
		}

9、push_back、append、+= 接口

void push_back(char c)
		{
			if (_size == _capacity) {
				size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
				reserve(newcapacity);
			}
			_str[_size] = c;
			_size++;
			_str[_size] = '\0';
		}
		void append(const char* s)
		{
			size_t len = strlen(s);
			if (len + _size >_capacity) {
				reserve(_size + len);
			}
			strcpy(_str + _size, s);
			_size += len;
		}
		string& operator+=(char c)
		{
			push_back(c);
			return *this;
		}
		string& operator+=(const char* s)
		{
			append(s);
			return *this;
		}

10、insert、erase接口

insert函數(shù)方便我們?cè)谌魏挝恢貌迦霐?shù)據(jù)。和上面一樣,分為插入1個(gè)字符或字符串。

在插入之前要先挪動(dòng)數(shù)據(jù)(尾插除外),挪動(dòng)的過(guò)程中要注意邊界問(wèn)題。

string& insert(char ch, size_t pos)
		{
			assert(pos<= _size);
			if (_capacity == _size)
			{
				size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
				reserve(newcapacity);
			}
			size_t end = _size+1;
			while (end >= pos)
			{
				_str[end] = _str[end-1];
				--end;
			}
			_str[pos] = ch;
			_size++;
			_str[_size] = '\0';
			return *this;
		}

string& insert(const char* str, size_t pos)
		{
			size_t len = strlen(str);
			if (len + _size >_capacity)
			{
				reserve(len + _size);
			}
			size_t end = _size+len;
			while (end >= pos + len)
			{
				_str[end] = _str[end-len];
				--end;
			}
			strncpy(_str + pos, str, len);
			_size += len;
			return *this;
		}

erase刪除同樣需要挪動(dòng)數(shù)據(jù),給刪除個(gè)數(shù)一個(gè)缺省值npos,定義為常量-1,對(duì)于size_t來(lái)說(shuō)是2^32,當(dāng)沒(méi)有給定刪除個(gè)數(shù)或要?jiǎng)h除的個(gè)數(shù)超出剩余的字符數(shù)就意味著刪除pos位置之后所有數(shù)據(jù);否則刪除pos位置后面len個(gè)字符,可以用strcpy直接覆蓋。

string& erase(size_t pos,size_t len = npos)
		{
			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
			}
			else
			{
				strcpy(_str + pos,_str+pos+len);
				_size -= len;
			}
			return *this;
		}
		const static int npos = -1;

11、find接口

find接口不僅要支持查找,還要能支持從指定位置開(kāi)始查找。同樣的,有查找單個(gè)字符的功能,還有查找字符串的功能。找到返回下標(biāo),找不到返回npos。

查找單個(gè)字符比較簡(jiǎn)單,直接遍歷即可。

查找字符串可以用C函數(shù)庫(kù)的strstr函數(shù)暴力查找,注意該函數(shù)返回的是指針,所以最后返回下標(biāo)值時(shí)要用返回的指針 - 頭指針。

const static int npos = -1;

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

		size_t find(const char* str, size_t pos = 0)
		{
			assert(pos< _size);
			const char* ptr = strstr(_str + pos, str);
			if (ptr == nullptr)
				return npos;
			else
				return ptr - _str;
		}

12、resize接口

resize是改變?nèi)萘康暮瘮?shù),可以變大也可以變小。

void resize(size_t n,char ch = '\0')
		{
			if (n >_size)
			{
				reserve(n);
				_size = n;
				for (size_t i = _size; i< n; ++i)
				{
					_str[i] = ch;
				}
				_str[n] = '\0';
			}
			else
			{
				_size = n;
				_str[n] = '\0';
			}
		}

13、流插入cout

流插入操作符<<重載,需要注意[ ]的重載,上面只重載了[ ]的非const版本,對(duì)于只讀對(duì)象調(diào)用需要const版本的。我們?cè)僦剌d一個(gè)只讀的[ ]方法。

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

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

有了c_str還不夠,還需要<<流插入。這兩者打印的方式是不一樣的。

c_str是按char*方式打印,字符串中間出現(xiàn) '\0' 不會(huì)打印后面的內(nèi)容;<<碰到 '\0' 依然會(huì)繼續(xù)打印,'\0' 打印顯示的是不可見(jiàn)字符。

14、流提取 >>

與流插入對(duì)應(yīng)。流提取碰到空格或換行就停下,后面部分進(jìn)入緩沖區(qū)。

想要拿到空格之后的內(nèi)容需要用到get函數(shù)。

istream& operator>>(istream& in,string& s)
	{
		char ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			s += ch;
			ch = in.get();
		}
		return in;
	}

這種方式有一個(gè)缺陷,字符串很長(zhǎng)時(shí)會(huì)擴(kuò)容很多次。想解決這個(gè)問(wèn)題也不好先reserve給定capacity大小,因?yàn)閏apacity給大了浪費(fèi),小了沒(méi)有效果。

因此可以借鑒C++標(biāo)準(zhǔn)庫(kù)的實(shí)現(xiàn)方式,先設(shè)定一個(gè)buff數(shù)組,分段將字符串放入。

istream& operator>>(istream& in, string& s)
	{
		char buff[128] = { '\0' };
		size_t i = 0;
		char ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			if (i == 127)//滿了
			{
				s += buff;
				i = 0;
			}
			buff[i++] = ch;
			ch = in.get();
		}
		if (i >0)
		{
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}

15.拷貝構(gòu)造

這里我們必須手動(dòng)寫(xiě)一個(gè)拷貝構(gòu)造函數(shù),否則系統(tǒng)默認(rèn)的是淺拷貝,對(duì)于自定義類型來(lái)說(shuō)會(huì)出現(xiàn)指針指向同一空間以及內(nèi)存泄漏的問(wèn)題。

拷貝構(gòu)造有兩種寫(xiě)法:傳統(tǒng)寫(xiě)法和現(xiàn)代寫(xiě)法。

1、傳統(tǒng)寫(xiě)法

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

傳統(tǒng)寫(xiě)法就是創(chuàng)建一個(gè)和拷貝對(duì)象一樣大小的新空間,再將數(shù)據(jù)拷貝到新空間中。

2、現(xiàn)代寫(xiě)法

void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_capacity, s._capacity);
			std::swap(_size, s._size);
		}
		//現(xiàn)代寫(xiě)法
		string(const string& s)
			:_str(nullptr)
			,_capacity(0)
			,_size(0)
		{
			string tmp(s._str); //構(gòu)造函數(shù)
			swap(tmp);
		}

現(xiàn)代寫(xiě)法就是先用構(gòu)造函數(shù)構(gòu)造一個(gè)tmp,此時(shí)tmp擁有和拷貝對(duì)象s0一樣的空間和數(shù)據(jù),交換tmp和s1,使兩者的空間和數(shù)據(jù)完全交換。因?yàn)閠mp最后要調(diào)用析構(gòu)函數(shù)清理空間,和s1交換后tmp指向的是隨機(jī)值,所以提前讓s1指向空,避免釋放野指針的問(wèn)題。

注意:現(xiàn)代寫(xiě)法并不能提高效率,只是簡(jiǎn)化了代碼(結(jié)合賦值函數(shù)來(lái)看)

16、賦值重載

賦值也有傳統(tǒng)和現(xiàn)代寫(xiě)法,與拷貝構(gòu)造類似。

1、傳統(tǒng)寫(xiě)法

//傳統(tǒng)寫(xiě)法
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				char* tmp = new char[s._capacity + 1];
				strcpy(tmp,s._str);
				delete[]_str;
				_str = tmp;
				_capacity = s._capacity;
				_size = s._size;
			}
			return *this;
		}

2、現(xiàn)代寫(xiě)法

//現(xiàn)代寫(xiě)法1(不推薦)
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				string tmp(s);
				swap(tmp);
			}
			return *this;
		}

		//現(xiàn)代寫(xiě)法2(推薦√)
		string& operator=(string s)
		{
			swap(s);
			return *this;
		}

現(xiàn)代寫(xiě)法1還是先拷貝構(gòu)造tmp,再將tmp與s1交換,這種寫(xiě)法tmp有點(diǎn)多余了。

其實(shí)可以直接傳值傳參,在傳參的過(guò)程中拷貝構(gòu)造s(寫(xiě)了拷貝構(gòu)造這里是深拷貝),直接交換s和s1即可。

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧

當(dāng)前名稱:string(四)————底層實(shí)現(xiàn)-創(chuàng)新互聯(lián)
本文URL:http://chinadenli.net/article18/dggggp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供服務(wù)器托管、網(wǎng)站設(shè)計(jì)、商城網(wǎng)站、手機(jī)網(wǎng)站建設(shè)、ChatGPT、營(yíng)銷型網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化