string类

文章详细介绍了STL中的string类,包括构造方法、迭代器、容量管理、元素访问、修改操作以及字符串操作。还提到了深拷贝和浅拷贝的概念,并给出了模拟实现string类的部分代码,强调了在实现过程中需要注意的问题,如内存管理和迭代器的使用。
摘要由CSDN通过智能技术生成

STL六大组件:容器、算法、迭代器、仿函数、空间配置器、配接器
在这里插入图片描述
stl可以参考微软的文档:
https://learn.microsoft.com/zh-cn/cpp/standard-library/map-class?view=msvc-170
这是容器map文档

stl中的线性结构容器
string —— 动态顺序表,专门用来存储和管理字符串
vector —— 动态顺序表,可以存储任意类型数据
list —— 带头结点的双向循环链表
deque —— 动态二维数组结构
array —— 静态类型的顺序表
forward_list —— 带头结点的循环单链表

string类

一提起字符串,首先想到的是C语言的字符串。
C语言是没有字符串类型的,所有的字符串都是用char[]数组结尾加 ‘\0’,来表示,操作起来较为不便。
在stl标准模版库中有实现string类。那么就可以使用类去构造对象然后进行操作,在C++语言中用内置类型定义一个变量,和用类创建一个类对象的代码是一样的:

int num;
string str;//感觉就好像string也是自带类型一样

string类的方法:

构造:

在这里插入图片描述

总结一下一共7个方法:
空,string拷贝,子串构造,C串构造,C子串构造,字符填充,迭代器构造

常用的就是前四种。
C串构造:string( const char* s) 是挺重要的,这样在传参时传递C串给string也不会出问题,因为会进行C串构造成string ;注意子串构造是:从pos拷贝len长度构造

迭代器:

begin() , end()

容量

在这里插入图片描述
常用的就是size,resize ,clear,empty

for(int i = 0; i < str.size(); i++ )

str.clear();

str.resize(10);

if( str.empty() )

对于string容量,要理解字符串需要的是一块连续的内存,所以对于字符串的扩容,增删,插入操作的效率其实不高,因为要涉及元素的移动。
对字符串操作,insert或者+=,会使原串长超过reserve容量,此时就需要扩容,扩容一般来讲是1.5倍或两倍扩容,这样就不会造成频繁的扩容,元素复制,提高效率。

元素访问:

在这里插入图片描述

可以用下标运算符 [ ] 来获取某一位置字符,如果越界访问会assert,程序中断崩溃
str.at(i) 使用效果和【】是一样的,越界会抛异常out of range

修改:

修改一般来讲就是:增、删、替换、交换、
在这里插入图片描述
增:+=,append(添加整串),push_back(尾插字符),insert(任意位置插入任意数量字符)
删:erase(全部删除),pop_back(尾删)
改:assign(重新分配),replace(替换),swap

str1+=str2;
//string& append (const string& str, size_t subpos, size_t sublen);
str.append( str2, 0 ,10);
str.puck_bash(c);
//string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);
str.insert( pos, str2, 0, 10);//把str2串的0到10字符插入pos位置之前
//string& erase (size_t pos = 0, size_t len = npos);
str.earse();//这样就全删了,因为默认参数是0到,npos--可以理解为字符串结尾,
//一般用i != string::npos来判断限制,进行循环终止条件
str.earse(c);//也可以删除单个字符

在这里插入图片描述

str.assign( str, 9)//实现substr相同功能,当然substr重载的形式更丰富

string& replace (size_t pos, size_t len, const string& str);
要替换的区间位置,[pos , len),替换的新串str

看文档中这些方法,一般都有很多重载形式:关注不同重载的返回值类型、参数的类型和作用,便于掌握这些函数的用法。
比如用const string&修饰的参数,那必然是作为输入参数;不带const有可能只是作为输出参数,也用可能是输入输出参数;insert方法参数较多,首先明确插入肯定要先指明插入位置pos,之后就是要操作的str和范围;

字符串操作:

种类比较多,一般就是用个find,substr,compare,c_str
在这里插入图片描述

str.c_str();

在这里插入图片描述
find方法要找的可以是字符串,也可以是字符,并且可以设置从某一位置开始找,返回值是找到的元素的下标
当返回值为string::npos时说明要找的不存在

成员常量:string::npos 用来表示字符串末尾

static const size_t npos = -1;

实际是
4294967295
这个值,当用作字符串成员函数中的len(或sublen)参数的值时,表示“直到字符串的末尾”。

非成员函数重载:

这里只列出我常用的

istream& getline (istream& is, string& str);
//不是成员函数,不用str.getline()
getline(cin,str);//获取一行输入
istream& is 可以不仅仅是cin,也可以是文件流等等

还有这些转换函数,要注意转换时有数据范围限制的
在这里插入图片描述

stoi("123")
to_string(123);

这里为什么string转其他内置类型有这么多方法,而转成stirng只有to_string一个。

这就是重载的妙用,重载是同名函数但参数列表不同,返回值是不能区别重载的,这些方法都是用返回值来实现功能

总结

string可以说是使用起来很方便的一个类,也非常常用,较之于C字符串,可以说是string是很强大的,方法有很多,所以日常中药多加练习,才真正能够随心所用,并且string是stl容器其中一只,stl容器中很多类的方法都是比较相似的如:push_back,earse,find,[],begin,end,insert,当然针对不同的容器,还是有一些特有的方法。

模拟实现string

//#include<iostream>
//using namespace std;
//#include<string>
//class String{
//private:
//	char* _str;
//public:
//	String(const char* str = "")//带参构造
//	{
//		if (nullptr == str)
//			str = "";
//		_str = new char[strlen(str) + 1];
//		cout << &_str << endl << &str << endl;
//
//		strcpy(_str, str);
//	}
//	char* c_str(){
//		return _str;
//	}
//
//	String(const String& s)//拷贝构造
//		:_str(new char[strlen(s._str)+1])
//	{
//		strcpy(_str, s._str);//进行深拷贝,申请新的空间,复制
//	}
//
//	String(const String* this, const String& s)//成员函数是有this指针隐式传参的
//	//String(const String& s)//也可以这样实现
//	//	:_str(nullptr)
//	//{
//	//	String temp(s._str);//用s传入的字符串给tmp拷贝构造(这里用的拷贝构造就是上面写的)
//	//		/*在出了作用域后temp就会销毁,则调用delete[]删除其的str,删除的是交换了的之前this的str,而this的_str是未定义的指针,
//	//		所以最好在构造函数的初始化列表中加上:_str(nullptr)*/
//	//	swap(_str, temp._str);//把this的_str,和temp._str交换
//	//}
//
//	/*String& operator=(const String& s)
//	{
//		if (this != &s)
//		{
//			char* temp = new char[strlen(s._str) + 1];
//			strcpy(temp, s._str); 
//			delete[] _str;
//			_str = temp;
//		}
//		return *this;
//	}*/
//	String& operator=(String s)//值传参,生成临时拷贝构造的对象
//	{
//		swap(_str, s._str);
//		return *this;
//	}
//
//	/*
//	对于浅拷贝的问题,主要是释放资源析构实时delete两次,delete释放过的内存就会报错
//	解决:采用浅拷贝+引用计数
//	每次析构只是计数-1,当计数=0时才释放资源
//	若遇到要修改时才进行深拷贝,申请空间,因为这些字符串是共享一块char[]内存
//	*/
//
//	friend ostream& operator<<(ostream& os, const String& s);
//
//	~String(){
//		if (_str){
//			delete[] _str;
//			_str = nullptr;
//		}
//	}
//};
friend
//ostream& operator << (ostream& os, const String& s)//友元函数
//{
//	cout << s._str << endl;
//	return os;
//}
//
//void test01()
//{
//	String s1("sajdh");
//	cout << s1 << endl;
//	//String s2(s1);//实际调用的是编译器的浅拷贝,而没有调用String(const char* str)实现的深拷贝
//	/*
//	解决方式:1.new新空间给this->_str;
//	*/
//	String s3(s1);
//	cout << s3 << endl;
//	String s4 = "120938109283";
//	s4 = s3;
//	cout << s4;
//}
//
//int main()
//{
//	test01();
//	_CrtDumpMemoryLeaks();
//	return 0;
//}

#include<iostream>
#include<cassert>
#include<algorithm>
namespace gyx
{
	class string
	{
	private:
		char*_str;
		size_t _size;
		size_t _capacity;
		
		friend std::ostream& operator<<(std::ostream&, const string&);
	public:
		const static size_t npos = -1;
		//构造
		string(const char* str = "")
		{
			if (nullptr == str)
				str = "";
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		string(const string& str)
			:_str(nullptr)
			/*
			一定要给_str初始化nullptr,这里实现的拷贝构造,是要生成临时对象temp,然后把temp的内容(_str,size,capacity)和this交换,
			而this是没有初始化的,它的_str指向一个未知的地址,可以说是野指针,交换后临时对象出作用域就会被析构,而析构delete[] _str就会释放野指针,程序崩溃
			*/
		{
			string temp(str._str);//调用上面的C串构造
			this->swap(temp);
		}
		string(size_t n, char c)
		{
			_str = new char[n + 1];
			memset(_str, c, n);
			_str[n] = '\0';

			_size = n;
			_capacity = n;
		}

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

		}

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

		//迭代器
		typedef char* iterator;//迭代器不是指针,这里只是简单实现,功能相近
		iterator begin(){ return _str; }
		iterator end(){ return _str + _size; }

		//容量
		size_t size()const{ return _size; }
		size_t capacity()const { return _capacity; }
		bool empty()const{ return 0 == _size; }
		void clear(){ _size = 0; }

		void resize(size_t newsize, char c)
		{
			size_t oldsize = size();
			//resize可以增多,也可减少size
			if (newsize <= oldsize)
			{
				_size = newsize;
				_str[_size] = '\0';
			}
			else
			{
				//增多
				if (capacity() < newsize)
				{
					reserve(newsize);
				}
				memset(_str + _size, c, newsize - _size);
				_size = newsize;
				_str[_size] = '\0';
			}
		}
		void resize(size_t newsize)
		{
			resize(newsize, '\0');
		}
		void reserve(size_t newcapacity)
		{
			size_t oldcapacity = capacity();
			if (newcapacity > oldcapacity)
			{
				char* newstr = new char[newcapacity + 1];
				strncpy(newstr, _str,_size);//只拷贝size个元素,别拷贝超了
				delete[] _str;
				_str = newstr;
				_capacity = newcapacity;
				_str[_size] = '\0';
			}
		}

		//元素访问
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		char& operator[](size_t pos)const
		{
			assert(pos < _size);
			return _str[pos];
		}
		char& at(size_t pos)
		{
			//越界会抛异常out of range
			return _str[pos];
		}
		char& at(size_t pos)const{ return _str[pos]; }

		//字符串操作
		void push_back(char c)
		{
			*this += c;
		}
		string& operator+=(char c)
		{
			if (_size == _capacity)
				reserve((size_t)_capacity*1.5 + 3);//避免capacity=0的情况
			_str[_size] = c;
			++_size;
			_str[_size] = '\0';
			return *this;
		}
		string& operator+=(const char* str)
		{
			size_t needspace = strlen(str);
			size_t nowfreespace = capacity() - size();
			if (needspace > nowfreespace)
				reserve(capacity()+needspace);

			strcat(_str, str);
			_size += needspace;
			_str[_size] = '\0';
			return *this;
		}
		string& operator+=(const string& s)
		{
			*this += s.c_str();
			return *this;
		}
		string& apend(const char* str)
		{
			*this += str;
			return *this;
		}
		string& append(const string& s)
		{
			*this += s;
			return *this;
		}

		string& insert(size_t pos, const string& str)
		{
			size_t needspace =str.size();
			size_t nowfreespace = capacity() - size();
			if (nowfreespace < needspace)
				reserve(capacity()+needspace+1);
			for (size_t i = size(); i>=pos; i--)
			{
				_str[i + needspace] = _str[i];
			}
			for (int j = 0; j < str.size(); j++)
				_str[pos + j] = str[j];
			_size += needspace;
			_capacity += needspace;

			return *this;
		}

		string& earse(size_t pos=0, size_t len=npos)
		{
			if (pos > _size)//不删
				return *this;
			len = std::min(len, len-pos);
			for (size_t i = pos; i <+len+_size; i++)
			{
				_str[i] = _str[i + len];
				delete &_str[i + len];
			}
			_size -= len;
			_capacity -= len;
			return *this;
		}

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

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

		size_t find(char c, size_t pos = 0)const
		{
			if (pos >= _size)
				return npos;
			
			for (size_t i = pos; i < _size; ++i)
			{
				if (_str[i] == c)
					return i;
			}
			return npos;
		}
		string substr(size_t pos = 0, size_t len = npos)const
		{
			if (pos >= _size)
				return string();
			//pos+len的不能越界,最多截取到字符串尾
			len = std::min(len, _size - pos);

			string temp(len+1, '\0');
			for (size_t i = pos,j=0; i<pos+len; ++i)
				temp[j++] = _str[i];
			return temp;
		}
		friend std::ostream& operator<<(std::ostream&, const string&);
	};
	std::ostream& operator<<(std::ostream& os, const string& s)
	{
		for (size_t i = 0; i<s.size(); i++)
			os << s[i];
		return os;
	}
}
using namespace gyx;
void test()
{
	string s1;
	string s2("ni hao");
	string s3(s2);
	s1 = s2;
	std::cout << s2 << '\n';
	std::cout << s1;
	s1.apend("wqjeiwq");
	s1.insert(3, "111");
	std::cout << "s1=" << s1<<'\n';
	string sub = s1.substr(3, 3);
	std::cout << sub;
	sub.reserve(100);
	sub.resize(100);
	std::cout << sub.find('a');
	int pos = sub.find('a');
	if (pos != string::npos)
		std::cout << "找到了!!!\n";

}
int main()
{
	test();
	_CrtDumpMemoryLeaks();
	return 0;
}

拷贝构造的实现要给底层char[ ]数组指针传nullptr初始化
模拟实现其他的方法首先要明确方法的作用,目的,参数设置的顺序,比如:insert()首先肯定要有插入的位置pos,以及要插入的内容,还有就是底层实现是char的顺序表,顺序表在增删是会有大量的元素移动,所以要注意越界问题,以及结尾的\0

在这里插入图片描述

一些练习题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值