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

【C++ ? STL】探究string的源碼

這篇具有很好參考價值的文章主要介紹了【C++ ? STL】探究string的源碼。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。


ヾ(????)?" 人總要為過去的懶惰而付出代價ヾ(????)?"
【C++ ? STL】探究string的源碼,C++,筆記,c++,開發(fā)語言


一、深淺拷貝

淺拷貝:也稱位拷貝,編譯器只是將對象中的值拷貝過來。如果對象中管理資源,最后就會導致多個對象共享同一份資源,當一個對象銷毀時就會將該資源釋放掉,而此時另一些對象不知道該資源已經被釋放,以為還有效,所以當繼續(xù)對資源進項操作時,就會發(fā)生發(fā)生了訪問違規(guī)。
淺拷貝:(1)析構兩次,造成程序崩潰(2)一個對象修改影響另外一個

如果一個類中涉及到資源的管理,其拷貝構造函數(shù)、賦值運算符重載以及析構函數(shù)必須要顯式給出。一般情況都是按照深拷貝方式提供。

編譯器默認生成的拷貝構造,是淺拷貝,會是兩個對象指向同一塊空間,當程序結束的時候,那么兩個對象都會進行銷毀,那么一塊空間就會進行多次釋放,從而引起崩潰。

深拷貝:給每一個對象分配資源,保證多個對象之間不會因為共享資源而導致多次釋放造成程序崩潰。

二、傳統(tǒng)版寫法的string類(簡單)

#pragma once
#include <iostream>
using namespace std;
#include <assert.h>

namespace yyqx//為了與庫里面的string進行區(qū)分
{
	//僅僅實現(xiàn)一個簡單的string,僅僅考慮資源管理深淺拷貝問題
	class string
	{
	public:
		//構造函數(shù)
		string(const char* str)
			:_str(new char[strlen(str) + 1])//這里的+1,是為了'\0'開辟空間
		{
			strcpy(_str, str);//拷貝的時候'\0'也拷貝了
		}

		//拷貝構造(深拷貝)
		//s2(s1)
		string(const string& s)
			:_str(new char[strlen(s._str) + 1])
		{
			strcpy(_str, s._str);
		}

		//賦值,也會有深淺拷貝的問題
		string& operator=(const string& s)
		{
			if (this != &s)//避免自己給自己賦值,會導致值被釋放,就會變成隨機值
			{
				//delete[] _str;//首先進行釋放
				//_str = new char[strlen(s._str) + 1];//C++的new是不需要檢查是否開辟空間
				會拋異常
				//strcpy(_str, s._str);

				//為了避免開辟空間失敗,而本來的空間也被我們釋放,可以先開啟空間,
				//進行拷貝,然后再釋放
				char* tmp = new char[strlen(s._str) + 1];
				strcpy(tmp, s._str);
				delete[] _str;
				_str = tmp;
			}
			return *this;
		}


		//析構函數(shù)
		~string()
		{
			if (_str)
			{
				delete[] _str;
			}
		}
		
		//目的為了輸出字符串
		const char* c_str() const
		{
			return _str;
		}//返回c格式的字符串

		//重載[]
		char& operator[](size_t pos)
		{
			assert(pos < strlen(_str));//注意這里的范圍
			return _str[pos];
		}

		size_t size()
		{
			return strlen(_str);
		}
	private:
		char* _str;
	};
}

賦值運算符重載也會有深淺拷貝的問題。賦值,對象本身是有值的【拷貝的時候,如果空間小,就會不夠,空間大,就會造成資源浪費】

三、string類的模擬實現(xiàn)

string的增刪查改以及使用string【傳統(tǒng)】
基本框架

#pragma once
#include <iostream>
using namespace std;
#include <assert.h>

namespace yyqx//為了與庫里面的string進行區(qū)分
{
	class string
	{
	public:

構造函數(shù)+析構函數(shù)
寫法1

//構造函數(shù)
		string(const char* str)
			:_size(strlen(str))
			,_capacity(_size)
		{
			_str = new char[strlen(str) + 1];//這里的+1,是為了'\0'開辟空間
			strcpy(_str, str);//拷貝的時候'\0'也拷貝了
		}

		string()//注意,這里不是給的空,而是給了一個空的字符串//標準庫里的就是給了一個""
			:_size(0)
			,_capacity(0)
		{
			_str = new char[1];
			_str[0] = '\0';
		}
  • 構造函數(shù):初始化列表,初始化的順序并不是初始化列表的順序,而是成員變量在類中的聲明次序。
  • 構造函數(shù):注意默認的構造函數(shù)【編譯器自動生成、缺省、函數(shù)重載】,默認的構造函數(shù)這里選擇寫一個同名函數(shù),注意這里并不是給一個空指針,而是給了一個空字符串。
    寫法2:(最優(yōu)寫法)
		string(const char* str = "")//這里默認值不能給nullptr,strlen以及拷貝strcpy會崩潰
			:_size(strlen(str))
			,_capacity(_size)
		{
			_str = new char[strlen(str) + 1];//這里的+1,是為了'\0'開辟空間
			strcpy(_str, str);//拷貝的時候'\0'也拷貝了
		}
		
		//析構函數(shù)
		~string()
		{
			if (_str)
			{
				delete[] _str;
				_str = nullptr;//好習慣
				_size = 0;
				_capacity = 0;
			}
		}
  • 缺省值這不能給nullptr,strlen以及拷貝strcpy時程序會崩潰
  • 注意初始化列表
  • strcpy注意,拷貝的時候’\0’也拷貝了
  • new開空間的時候,一定要多開一個給’\0’

拷貝構造+賦值重載函數(shù)+其他

		//拷貝構造(深拷貝)
		//s2(s1)
		string(const string& s)
			:_size(strlen(s._str))
			,_capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}

		//賦值,也會有深淺拷貝的問題
		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;
		}
		//目的為了輸出字符串
		const char* c_str() const
		{
			return _str;
		}//返回c格式的字符串
		
		char& operator[](size_t pos)//這里僅僅可以傳入對象,不能傳入const對象,如果是const對象,就會報錯
		{
			assert(pos < _size);//注意這里的范圍
			return _str[pos];
		}
		const char& operator[](size_t pos) const//這里就可以傳入const對象
		{
			assert(pos < _size);
			return _str[pos];
		}

		//這里的const修飾的是this指針指向的對象const string s;
		size_t size() const//寫const,普通對象以及const對象都可以調用,如果不加const對象就不可以調用
		{
			return _size;
		}
		size_t capacity() const//寫const,普通對象以及const對象都可以調用
		{
			return _capacity;
		}

添加

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

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

		void reverse(size_t n)//一個擴容的作用
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;//注意這里的釋放不是free
				_str = tmp;
				_capacity = n;
			}
		}

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

		void push_back(char ch)
		{
			if (_size == _capacity)
			{
				reverse(_capacity == 0 ? 4 : _capacity * 2);//如果是一個空字符串,就會導致并沒有擴容,
				//擴容要注意剛開始沒有容量的情況下
			}
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';//注意\0,容易遺漏
		}

		//append插入的字符個數(shù)是未知的,擴容二倍也不一定足夠
		void append(const char* str)
		{
			size_t len = _size + strlen(str);
			if (len > _capacity)
			{
				reverse(len);
			}
			strcpy(_str + _size, str);
			_size = len;
		}//但是我們一般用+=
  • 判斷容量是否滿,如果 _size= _ capacity,容量擴2倍,new一個新容量的空間,釋放舊空間,最后指針指向新的空間。
  • append (append插入的字符個數(shù)是未知的,擴容二倍也不一定足夠:解決辦法:reverse預留空間【一個擴容的作用】)
  • reverse 為string預留空間,避免多次擴容(提高效率)
  • resize用處:擴空間+初始化;刪除數(shù)據(jù)保留前n個
    插入
string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);//這里的=_size相當于尾插
			//注意,這里容易忘記,size_t就已經大于等于0了,所以在這里我們主要保證pos是小于_size即可
			if (_size == _capacity)
			{
				reverse(_capacity == 0 ? 4 : 2 * _capacity);
			}
			//不可以用strcpy,這里不可以是同一塊地址,對導致內容不是我們想要的
			//最后一個未知的字符移到_size然后就是倒數(shù)第二位移動,從后向前移動
			size_t end = _size + 1;
			//注意這里如果end=_size,當頭插的時候,進入循環(huán)end會變成-1,因為是size_t所以又會進入循環(huán),導致錯誤
			while (end > pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}
			_str[pos] = ch;
			_size++;
			return *this;
		}

		//插入\0,用c_str(遇到\0停止打印)打印顯示在屏幕的字符串長度會減小或者不變,但是_size會變大
		//用范圍for或者迭代器可以打印出來

		string& insert(size_t pos, const char* str)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reverse(_size + len);
			}
			size_t end = _size + len;
			while (end > pos + len - 1)//這里注意
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, str, len);//防止為了遇見\0就不拷貝了(strcpy遇見\0就不拷貝了)
			_size += len;
			return *this;
		}

插入字符:

  • 不可以用strcpy,在字符進行向后移的時候,不可以是同一塊地址,對導致內容不是我們想要的,最后一個未知的字符移到_size然后就是倒數(shù)第二位移動,從后向前移動
  • end=_size,當頭插的時候,進入循環(huán)end會變成-1,因為是size_t,又是大于0所以又會進入循環(huán),導致代碼錯誤

插入字符串:

  • 防止為了遇見\0就不拷貝了,所以用的是strncpy(strcpy遇見\0就不拷貝了)

刪除

//刪除
		string& erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);
			//刪除的數(shù)據(jù)大于等于_size
			if (len == npos || pos + len >= npos)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				size_t begin = pos + len;
				while (begin <= _size)
				{
					_str[begin - len] = _str[begin];
					++begin;
				}
				_size -= len;
			}
			return *this;
		}

注意:npos類中靜態(tài)成員的初始化,必須在類外,類和對象(下)本篇文章中有詳細說明?!綾onst在定義的時候必須初始化,但是靜態(tài)成員的變量初始化又在外面】

查找

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

		size_t find(const char* str, size_t pos = 0)
		{
			const char* p = strstr(_str + pos, str);
			if (p == nullptr)
			{
				return npos;
			}
			else
			{
				return p - _str;
			}
			
		}
		
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
	private:
		char* _str;
		size_t _size;//有效字符的個數(shù)
		size_t _capacity;//存儲有效字符的空間大小
		const static size_t npos;//正確的寫法是在類外進行初始化
		//const static size_t npos = -1;//這種寫法也可以,但是違背了正確的寫法,要注意
	};
	const size_t string::npos = -1;
  • strstr返回的是指針,沒有找到返回空指針。

流插入和流提取

/流插入和流提取
	//在類外
	//不可以用c_str(),因為遇見\0會停止
	//'\0'是不可以見字符,不會顯示

	//流插入
	ostream& operator<<(ostream& out, const string& s)
	{
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}
	//流提取,字符從面板提取到s
	istream& operator>>(istream& in, string& s)
	{
			s.clear();
		//要把對象里面的字符清理掉,否則當對象不是空的時候,會導致字符直接加到已有對象的后面。
		//但是我們想要的是,對象是我們輸入的字符串
	
		//第一種思路(缺點:頻繁的+=,字符串過大,會導致頻發(fā)的擴容,影響效率)
		/*char ch;
		ch = in.get();
		if (ch != ' ' && ch != '\n')
		{
			s += ch;
			ch = in.get();
		}
		return in*/

		//第二種思路(這種思路比較優(yōu),無論大小都可以避免頻繁擴容)
		char ch;
		ch = in.get();
		char buff[128] = { '\0' };
		size_t i = 0;
		if (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				s += buff;
				memset(buff, '\0', 128);
				i = 0;
			}
			ch = in.get();
		}
		s += buff;
		return in;
	}
  • '\0’是不可以見字符,不會顯示
  • clear()要把對象里面的字符清理掉,否則當對象不是空的時候,會導致字符直接加到已有對象的后面。但是我們想要的是,對象是我們輸入的字符串

運算符重載

	//運算符重載
	//比較大小
	//全局函數(shù).可以類比日期類
	bool operator<(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}

	bool operator>(const string& s1, const string& s2)
	{
		return !(s1 <= s2);
	}

	bool operator>=(const string& s1, const string& s2)
	{
		return s1 > s2 || s1 == s2;
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}
}//這個是yyqx的大括號

這里是在全局變量,沒有在類里面,是在類外

迭代器
string類private里面:

public:
		//迭代器
		typedef char* iterator;
		typedef const char* const_iterator;

		const_iterator begin() const
		{
			return _str;
		}

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

		iterator end() 
		{
			return _str + _size;
		}

四、現(xiàn)代版寫法的string類

拷貝構造和賦值的現(xiàn)代寫法

		//拷貝構造(深拷貝)
		//s2(s1)//現(xiàn)代寫法,剝削行為,要完成深拷貝,
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		string(const string& s)
			:_str(nullptr)
			,_size(0)
			,_capacity(0)//這里要進行初始化,否則交換后,局部變量的銷毀(隨機值銷毀,不可以)
		{
			//構造一個對象tmp,tmp里所有的東西this想要。this和tmp
			string tmp(s._str);//局部變量,出了作用域會銷毀
			swap(tmp);
			//tmp出了作用域會銷毀
		}

		//賦值,也會有深淺拷貝的問題
		//現(xiàn)代寫法
		//第一種
		//string& operator=(const string& s)
		//{
		//	if (this != &s)//避免自己給自己賦值,會導致值被釋放,就會變成隨機值
		//	{
		//		string tmp(s._str);
		//		swap(tmp);//把tmp給this,出了作用域把this給tmp的值進行銷毀
		//	}
		//	return *this;
		//}

		//第二種
		string& operator=(string s)//傳值傳參,拷貝構造,拷貝的值給this,并不會導致s的實參發(fā)生變化
		{
			swap(s);
			return *this;
		}
		//掌握現(xiàn)代寫法

補充知識點
遍歷方式中有一個是范圍for(范圍for的底層實現(xiàn)是迭代器,如果沒有迭代器的程序,代碼會進行報錯)

代碼展示:

	yyqx::string s("hello 12345");
	for (auto ch : s)
	{
		cout << ch << " ";
	}
	cout << endl;

在c語言中,我們用atoi。
【C++ ? STL】探究string的源碼,C++,筆記,c++,開發(fā)語言
string中的兩個常用函數(shù)


五、總結

以上就是今天要講的內容,本文詳細的介紹了淺拷貝、淺拷貝和string的模擬實現(xiàn)。本文以及一文帶你走進string詳細的介紹了string的相關知識,希望給友友們帶來幫助!文章來源地址http://www.zghlxwxcb.cn/news/detail-712951.html

到了這里,關于【C++ ? STL】探究string的源碼的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

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

領支付寶紅包贊助服務器費用

相關文章

  • 深入探究C++中的仿函數(shù)和迭代器——提升你的STL技能

    深入探究C++中的仿函數(shù)和迭代器——提升你的STL技能

    ??作者介紹:22級樹莓人(計算機專業(yè)),熱愛編程<目前在c++階段——目標Windows,MySQL,Qt,數(shù)據(jù)結構與算法,Linux,多線程,會持續(xù)分享學習成果和小項目的 ??作者主頁:熱愛編程的小K ??專欄鏈接:c++ ??歡迎各位→點贊?? + 收藏?? + 留言??? ??總結:希望你看

    2023年04月24日
    瀏覽(29)
  • C++ [STL之string模擬實現(xiàn)]

    C++ [STL之string模擬實現(xiàn)]

    本文已收錄至《C++語言》專欄! 作者:ARMCSKGT 前面我們介紹了STL容器string的部分接口使用,有了string使我們對字符串的操作如魚得水,其實string不止于使用方便,其實現(xiàn)也有許多我們值得學習的地方,本節(jié)將為您介紹string常用接口的代碼實現(xiàn)! 本文接口的實現(xiàn)借助于C++官方

    2024年02月05日
    瀏覽(23)
  • C++STL詳解 string【C++】

    C++STL詳解 string【C++】

    函數(shù)模板是一個藍圖,它本身并不是函數(shù),是編譯器用使用方式產生特定具體類型函數(shù)的模具。所以其實模板就是將本來應該我們做的重復的事情交給了編譯器 在編譯器編譯階段,對于模板函數(shù)的使用,編譯器需要根據(jù)傳入的實參類型來推演生成對應類型的函數(shù)以供調用。比

    2024年02月08日
    瀏覽(22)
  • 【C++】STL之string類(2)

    【C++】STL之string類(2)

    個人主頁:平行線也會相交?? 歡迎 點贊?? 收藏? 留言? 加關注??本文由 平行線也會相交 原創(chuàng) 收錄于專欄【C++之路】?? 本專欄旨在記錄C++的學習路線,望對大家有所幫助??? 希望我們一起努力、成長,共同進步。?? reverse :在不改變字符串內容的前提下,預留一定的

    2024年02月12日
    瀏覽(23)
  • C++ [STL之string的使用]

    C++ [STL之string的使用]

    本文已收錄至《C++語言》專欄! 作者:ARMCSKGT 字符串在程序中經常出現(xiàn),C語言為此提供了很多字符串操作函數(shù),但是這些庫函數(shù)與字符串是分離開的,不太符合OOP的思想,而且底層空間需要用戶自己管理,稍不留神可能還會越界訪問,于是STL單獨為字符串實現(xiàn)了一個容器,

    2024年02月01日
    瀏覽(21)
  • 【C++】STL之string類(1)

    【C++】STL之string類(1)

    個人主頁:平行線也會相交?? 歡迎 點贊?? 收藏? 留言? 加關注??本文由 平行線也會相交 原創(chuàng) 收錄于專欄【C++之路】?? 本專欄旨在記錄C++的學習路線,望對大家有所幫助??? 希望我們一起努力、成長,共同進步。?? 接下來就開始STL部分的學習了,本文先來學習一下

    2024年02月11日
    瀏覽(24)
  • 【C++精華鋪】9.STL string

    【C++精華鋪】9.STL string

    目錄 1. string類的優(yōu)勢 2. string類的常用接口 2.1 常用構造 1. 空串構造:string(); 2. C串構造:string(const char* s); 3. 拷貝構造:string(const string str); 4. 字符填充構造:string(size_t n, char c); 5. 迭代器構造:string(InputIterator first, InputIterator last); 2.2 ?string容量操作 1. size_t size(),size_t length

    2024年02月11日
    瀏覽(16)
  • C++ STL string類模擬實現(xiàn)

    C++ STL string類模擬實現(xiàn)

    目錄 string類成員變量 一.構造函數(shù) 二.析構函數(shù) 三.拷貝構造 四.size(),capacity() 五.operator [ ] 六. operator = ?七.字符串比較 ?八.reserve() 九.push_back(),append() 十.operator+= ?十一.insert() ?十二.迭代器 ?十二.erase() 十三.swap() ?十四.find() 十五.流提取,流輸出 十六

    2024年02月14日
    瀏覽(25)
  • 【C++】STL——string(兩萬字詳解)

    【C++】STL——string(兩萬字詳解)

    ??C++學習歷程:STL——string學習 博客主頁: 一起去看日落嗎 持續(xù)分享博主的C++學習歷程 博主的能力有限,出現(xiàn)錯誤希望大家不吝賜教 分享給大家一句我很喜歡的話: 也許你現(xiàn)在做的事情,暫時看不到成果,但不要忘記,樹??成長之前也要扎根,也要在漫長的時光??中沉

    2024年01月25日
    瀏覽(19)
  • 【C++入門到精通】C++入門 —— string類(STL)

    【C++入門到精通】C++入門 —— string類(STL)

    目錄 一、STL簡介? 1.STL是什么 2.STL的內容 ?3.STL的使用前提 二、string類 1.string類 是什么 2.string類的特點和操做 ?構造和初始化 ?字符串大小和容量 ?字符訪問和修改 ?字符串連接和拼接 ?子串操作 ?字符串比較 ?字符串修改 ?獲取字符的ASCII碼 ?字符串大小寫轉換:

    2024年02月14日
    瀏覽(43)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領取紅包

二維碼2

領紅包