C++-STL-string

string的使用

在C语言中,没有类这个概念,处理字符串通常是把字符串存在字符数组中处理的,并且库中提供了大量的字符串处理函数,但是其实与C++相比显的并不是很好用,C++中封装了string这个类,底层也是字符数组,在这基础上类内封装了大量的处理字符串的方法,
下面来一一的介绍一下。
首先推荐一个C++文档的网站:cplusplus
在这里插入图片描述
可以看到string类其实是typedef出来的,最初的那个类叫做basic_string这个类,由于编码的形式有很多种,一个字符占用的空间不同有的1个字节的2个字节有的4个字节,所以有不同的string类。
在这里插入图片描述
最常用的还是basic_string< char >这个类。

默认成员函数

构造函数

在这里插入图片描述
可以看到构造函数的重载形式非常多,下面我只演示最常用的几个。
1,使用字符串初始化

int main()
{
    
    
	string str = "hello string";
	cout << str << endl;
	return 0;
}

在这里插入图片描述
2,拷贝构造

int main()
{
    
    
	string str = "hello string";
	cout << str << endl;
	string str1(str);
	cout << str1 << endl;
	return 0;
}

在这里插入图片描述
3,使用string类对象的一段区间构造

int main()
{
    
    
	string str1 = "hello string";
	string str2(str1, 0, 8);
	cout << str2 << endl;
	return 0;
}

在这里插入图片描述

4,使用字符串的前几个字符构造

int main()
{
    
    
	string str1("hello string", 5);
	cout << str1 << endl;
	return 0;
}

在这里插入图片描述

赋值运算符重载

在这里插入图片描述
1,string类对象作参数

int main()
{
    
    
	string str1;
	string str2 = "hello C++";
	str1 = str2;
	cout << str1 << endl;
	return 0;
}

在这里插入图片描述

2,字符串做参数

int main()
{
    
    
	string str1;
	str1 = "hello C++";
	cout << str1 << endl;
	return 0;
}

在这里插入图片描述

3,单个字符作参数

int main()
{
    
    
	string str1;
	str1 = 'C';
	cout << str1 << endl;
	return 0;
}

在这里插入图片描述

析构函数

在这里插入图片描述

capcity相关函数

在这里插入图片描述

  • size与length
int main()
{
    
    
	string str1 = "hello world";
	cout << "size() : " << str1.size() << endl
		<<  "length() : " << str1.length() << endl;
	return 0;
}

在这里插入图片描述
其实这两个函数功能是重叠的,实现是一摸一样的,字符串用length更合理一些,但是后面的容器都是使用的size,所以为了统一string类也提供了size这个接口。

  • max_size
    在这里插入图片描述
    字符串能达到的最大长度,其实返回的是无符号整型的最大值。

在这里插入图片描述

  • capcity
    capcity就是返回其容量

  • resize与reserve
    在这里插入图片描述
    resize 是重新调整string的大小,如果传入的参数大于当前的大小,那么就会扩容加初始化,如果小于当前的大小就会改变其有效长度(容量不变)。

int main()
{
    
    
	string str1 = "hello world";
	str1.resize(20);
	str1.resize(5);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

reserve重新调整其容量,只会在传入参数大于当前容量时起作用,传入参数大于当前容量时会扩容不加初始化。

int main()
{
    
    
	string str1 = "hello world";
	cout << str1.capacity() << endl;
	str1.reserve(5);
	cout << str1.capacity() << endl;
	str1.reserve(20);
	cout << str1.capacity() << endl;
	return 0;
}

在这里插入图片描述

  • clear
    在这里插入图片描述
    对string做清空

  • empty
    在这里插入图片描述
    查看string是否为空

访问相关函数

在这里插入图片描述
最常用的就是[ ]访问string的任意元素,at()与其功能类似只是对越界的检查方式不同,operator[ ]采用的是断言方式检查,at采用的是温柔的检查方式。

int main()
{
    
    
	string str1("hello world");
	for (int i = 0; i < str1.size(); i++)
	{
    
    
		cout << str1[i] << " ";
	}
	cout << endl;
	for (int i = 0; i < str1.size(); i++)
	{
    
    
		cout << str1.at(i) << " ";
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

修改string相关函数

在这里插入图片描述
1,append与operator+=
在这里插入图片描述
在这里插入图片描述
两个功能是一样的,只是append的接口更多一些,但是append并没有operator+=更常用一些。
2,尾插和尾删
在这里插入图片描述
在这里插入图片描述
3,任意位置插入删除
在这里插入图片描述
在这里插入图片描述
4,交换两个string对象
在这里插入图片描述

string类其他功能函数

在这里插入图片描述
1,c_str
在这里插入图片描述
返回底层维护字符串的地址。

int main()
{
    
    
	string str1("hello string");
	const char* s = str1.c_str();
	cout << s << endl;
	return 0;
}

在这里插入图片描述
2,find 与 rfind
在这里插入图片描述
在这里插入图片描述

一个是从前往后查找,一个是从后往前查找,查找的内容可以是,字符,字符串,string类对象。返回的是查找到的字串的位置下标。

3.substr
在这里插入图片描述

字串切割函数,将一个string类对象从pos位置开始长度为len的字串返回,这里的nops是无符号整型的最大值。如果长度大于原字符串本身那么就将从pos位置开始直到结尾的字串返回。

在这里插入图片描述

4,compare
在这里插入图片描述
这个函数其实也并不常用,主要因为string类重载了大量的关系运算符。
在这里插入图片描述

非成员函数

在这里插入图片描述
1,重载流提取和流插入操作符

由于string类是一个自定义类型,无法使用cin,cout对其进行输入和输出,所以要对>>和<<操作符进行重载。

2,getline
在这里插入图片描述

使用cin>>读取string的时候,遇到空格就会停止,所以我们想输入一个带有空格的字符串就会出现问题,而getline这个函数就可以避免这个问题,它默认是以读取到’\n’为结束标志的,也给以手动改变其结束标志。

string的模拟实现

1,大体框架

namespace gy
{
    
    
	class string
	{
    
    
	public:
		string(const char* str = "")
			:_size(strlen(str))
		{
    
    
			_capcity = _size == 0 ? 4 : _size;
			_str = new char[_capcity + 1];
			strcpy(_str, str);
		}
	private:
		char* _str;
		size_t _size;
		size_t _capcity;
		static const size_t npos = -1;
	};
}

2,拷贝构造函数

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

3,赋值运算符重载

string& operator=(const string& s)
{
    
    
	_size = s._size;
	_capcity = s._capcity;
	char* tmp = new char[_capcity + 1];
	strcpy(tmp, s._str);
	delete[] _str;
	_str = tmp;
	return *this;
}

4,析构函数

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

5,c_str函数

const char* c_str()
{
    
    
	return _str;
}

6,size与capcity

size_t size() const
{
    
    
	return _size;
}
size_t capcity() const
{
    
    
	return _capcity;
}

由于这样的函数并不会对string的内容进行修改,最好加上const这样就能使普通对象和const对象都能调用。

7,operator[ ]

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

8,reserve

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

9,insert

insert 重载两个版本,一个是插入字符,一个是插入字符串

string& insert(size_t pos, char ch)
{
    
    
	assert(pos <= _size);
	//插入前检查是否需要扩容
	if (_size + 1 > _capcity)
		reserve(_size * 2);
	//挪动数据
	size_t end = _size;
	while (end >= pos)
	{
    
    
		_str[end + 1] = _str[end];
		--end;
	}
	_str[pos] = ch;
	_size++;
	return *this;
}

如果采用这种写法,你在测试的是时候会发现程序一直在跑停不下来。
这是由于我们采用的下标都是size_t的类型的,如果pos为0,最后一次挪动数据的时候,end处于0位置,挪动完后–end,end会减成 -1,而-1对于无符号整数来说就是最大值,所以会造成死循环。所以我们得换种写法。

string& insert(size_t pos, char ch)
{
    
    
	assert(pos <= _size);
	//插入前检查是否需要扩容
	if (_size + 1 > _capcity)
		reserve(_size * 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* s)
{
    
    
	assert(pos <= _size);
	size_t lens = strlen(s);
	if (_size + lens > _capcity)
		reserve(_size + lens);
	//挪动数据
	size_t end = _size + lens;
	while (end > pos + lens - 1)
	{
    
    
		_str[end] = _str[end - lens];
		--end;
	}
	strncpy(_str, s, lens);
	_size += lens;
	return *this;
}

10,erase

string& erase(size_t pos, size_t len = npos)
{
    
    
	assert(pos < _size);
	//挪动数据
	if (len == npos || _size - pos <= len)
	{
    
    
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
    
    
		size_t begin = pos + len;
		while (begin <= _size)
		{
    
    
			_str[begin - len] = _str[begin];
			++begin;
		}
		_size -= len;
	}
	return *this;
}

11,push_back, pop_back, append

这些接口都可以使用上面的insert和erase复用。

void push_back(char ch)
{
    
    
	insert(_size, ch);
}
void pop_back()
{
    
    
	erase(_size - 1);
}
void append(const char* s)
{
    
    
	insert(_size, s);
}

12,operator+=

+=一个字符

void operator+=(char ch)
{
    
    
	push_back(ch);
}

+=字符串

void operator+=(const char* s)
{
    
    
	append(s);
}

这些函数都在复用insert和erase,可见insert和erase一旦实现,就已经完成了许多工作。

13,swap

成员函数的swap非常简单,只需要交换内部为何的_str,_size,_capcity即可。

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

14,find

find 一个字符

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

find 一个字符串

size_t find(const char* s, size_t pos = 0)
{
    
    
	assert(pos < _size);
	char* res = strstr(_str + pos, s);
	if (!res)
		return npos;
	return res - _str;
}

15,substr

string substr(size_t pos, size_t len = npos)
{
    
    
	assert(pos < _size);
	string res;
	size_t curlen = len;
	if (len <= _size - pos || len == npos)
		curlen = _size - pos;
	char* tmp = new char[curlen + 1];
	strncpy(tmp, _str + pos, curlen);
	tmp[curlen] = '\0';
	res += tmp;
	delete[] tmp;
	return res;
}

16,重载关系运算符

bool operator==(const string& str) const
{
    
    
	return strcmp(_str, str._str) == 0;
}
bool operator>(const string& str) const
{
    
    
	return strcmp(_str, str._str) > 0;
}
bool operator>=(const string& str) const
{
    
    
	return (*this > str) || (*this == str);
}
bool operator<(const string& str) const
{
    
    
	return !(*this >= str);
}
bool operator<=(const string& str) const
{
    
    
	return !(*this > str);
}
bool operator!=(const string& str) const
{
    
    
	return !(*this == str);
}

17,重载流插入操作符

friend ostream& operator<<(ostream& _cout, const string& str);//类内
ostream& operator<<(ostream& _cout, const string& str)
{
    
    
	for (int i = 0; i < str.size(); i++)
	{
    
    
		if (str[i] == '\0')
			cout << ' ';
		else
			cout << str[i];
	}
	return _cout;
}

18,重载流插入操作符

friend istream& operator>>(istream& _cin, string& str);//类内
istream& operator>>(istream& _cin, string& str)
{
    
    
	str.clear();
	char ch = cin.get();
	//缓冲区提高效率
	char tmp[128];
	int i = 0;
	while (ch != ' ' && ch != '\n')
	{
    
    
		tmp[i++] = ch;
		if (i == 127)
		{
    
    
			tmp[127] = '\0';
			str += tmp;
			i = 0;
		}
		ch = cin.get();
	}
	if (i != 0)
	{
    
    
		tmp[i] = '\0';
		str += tmp;
	}
	return _cin;
}

18,正向迭代器

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

猜你喜欢

转载自blog.csdn.net/Djsnxbjans/article/details/129907410