C++STL——string类与模拟实现

22 篇文章 0 订阅

什么是STL

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。
STL的六大组成:仿函数,算法,迭代器,空间配置器,容器,配接器。
注意:
这里我是按照功能归类讲string归类到了STL里面,如果按照发展史其实并不属于STL中的容器。
并且从现在开始我们会更频繁的开始使用这个网站了:
cpulspuls

string类

为什么要学习string呢?C语言中字符串是以‘\0’结尾的,C语言当中提供的str库函数是与字符串分开的,很可能越界访问,也不方便管理,C++主要是OOP思想(抽象”、“封装”、“继承”、“多态),所以出现了string,并且更方便增删查改。

字符串的标准

计算机是二进制,但是如果让我们去理解一堆0和1组成的是什么意思很难理解,所以就有了编码(计算机存值——文字符号,对应关系):
在这里插入图片描述
这些符号就可以组成很多东西了,比如数值,单词等等。
如果你想储存abc,那么在内存中储存的就是

97 98 99

想让计算机流通全世界,那么就要支持各个国家的语言,这个时候就出现了统一码
在UTF-8中将常用的汉字用两个字节去存储,256*256=65536。(但是这里要兼容ASCII码值所以没有这么多)
有些生僻字需要三个或者四个字节去储存。
UTF-16不兼容ASCII码值,起步用2个字节。
UTF-32不兼容ASCII码值,起步用4个字节。

但是中华文化博大精深,国内又针对函数定义了新的标准,GBK

什么是string

string是字符串的类,该类添加了很多接口便于操作string类。
string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
不能操作多字节和变长字节序列。
cplusplus中是这样描述的:
在这里插入图片描述

string常用接口介绍

string的初始化

string()构造空string类的对象
string(const char* s)用字符串初始化新构造string类的的对象
string(const string&s)用string类对象构造新的string类对象

这类接口是调用构造函数。

#include <iostream>
#include <string>
using namespace std;

int main()
{
	string s("abc");//创建string类s,给s初始化abc
	cout << s << endl;
	string s1;//创建string类s1,不进行初始化
	cout << s1 << endl;
	string s2(s);//将s拷贝到s2中
	cout << s2 << endl;
	return 0;
}

在这里插入图片描述

比较大小与赋值

比较的是字符串的ASCII码值。
这些接口都是对于运算符进行重载。
先来看赋值:

string& operator= (const string& str);赋值string类的对象
string& operator= (const char* s);赋值字符串
string& operator= (char c);赋值一个字符

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s1("abc");
	cout << s1 << endl;
	string s2;
	s2 = s1;//赋值string类的对象
	cout << s2 << endl;
	string s3;
	s3 = "adc";//赋值字符串
	cout << s3 << endl;
	s3 = 's';//赋值一个字符
	cout << s3 << endl;
	return 0;
}

在这里插入图片描述
等于,大于,小于等:
返回值为bool,参数是引用类。
正确返回1,错误返回0.

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s1("abc");
	string s2("adc");
	string s3("abc");
	string s4("abb");
	cout << "==:" << endl;
	cout << (s1 == s2) << endl;
	cout << (s1 == s3) << endl;
	cout << "< <=:" << endl;
	cout << (s1 < s2) << endl;
	cout << (s1 <= s2) << endl;
	cout << (s1 < s3) << endl;
	cout << (s1 <= s3) << endl;
	cout << (s1 <= s4) << endl;
	cout << "> >=:" << endl;
	cout << (s1 > s2) << endl;
	cout << (s1 >= s2) << endl;
	cout << (s1 >= s3) << endl;
	cout << (s1 > s4) << endl;
	cout << (s1 >= s4) << endl;
	return 0;
}

在这里插入图片描述

容量

这里要注意容量和有效字符长度的区别。
size返回字符串有效长度
capacity返回有效容量长度

size_t size() const;
size_t capacity() const;

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s("qwert");
	cout << s.size() << endl;
	cout << s.capacity() << endl;
	return 0;
}

这里有一个与size功能相同的接口length。(原因是因为命名的习惯)
在这里插入图片描述
在这里插入图片描述
empty检测对象是否为空字符串,空返回1,非空返回0
clear清空对象中的有效字符串

bool empty() const;
void clear();

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s("qwert");
	cout << s.empty() << endl;
	s.clear();//这里并不改变容量大小
	cout << s.empty() << endl;
	return 0;
}

在这里插入图片描述
reserve为字符串预留空间
resize将有效字符串变成指定长度,多出的长度用指定字符补上

void reserve (size_t n = 0);
void resize (size_t n);
void resize (size_t n, char c);

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s;
	s.reserve(1000);//内存对齐会多出一些空间,总大小至少是n
	cout << s.capacity() << endl;
	s.reserve(15);//VS编译器下15及以下才会缩容,前提是原来对象中字符串不能超过15个长度
	cout << s.capacity() << endl;
	string s1("qwert");
	cout << s1 << endl;
	int sz = s1.size();
	s1.resize(4);//不到原本字符串长度就将后面的字符串覆盖
	cout << s1 << endl;
	s1 = "qwert";
	s1.resize(4, 'x');//不到原本字符串长度就将后面的字符串覆盖
	cout << s1 << endl;
	s1 = "qwert";
	s1.resize(8, 'x');//不足就用字符x补上
	cout << s1 << endl;
	return 0;
}

在这里插入图片描述

对象的修改

push_back在后面追加一个字符
append在后面追加一个字符串
operator+=在后面追加一个字符串或字符还有对象

void push_back (char c);
string& append (const char* s);
string& operator+= (const string& str);
string& operator+= (const char* s);
string& operator+= (char c);

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s("qwe");
	string s1("123");
	s += 'r';
	cout << s << endl;
	s += "ty";
	cout << s << endl;
	s += s1;
	cout << s << endl;
	string s2("zxc");
	s2.push_back('v');
	cout << s2 << endl;
	s2.append("cpp");
	cout << s2 << endl;
	return 0;
}

在这里插入图片描述
insert在指定位置插入指定内容(不推荐,时间复杂度高)
pop_back尾删
erase从指定位置删除指定长度的字符(不推荐,时间复杂度高)
c_str返回C语言的字符串

string& insert (size_t pos, const string& str);
string& insert (size_t pos, const char* s);
void pop_back();
string& erase (size_t pos = 0, size_t len = npos);
const char* c_str() const;

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s("qwert");
	string s1("zxc");
	s.insert(1, "ccc");
	cout << s << endl;
	s.insert(0, s1);
	cout << s << endl;
	s.pop_back();
	cout << s << endl;
	s.erase(2, 4);
	cout << s << endl;
	cout << s.c_str() << endl;//这里是为了方便做返回值
}

在这里插入图片描述
find + npos从指定位置向后查找指定位置的字符(npos是string类中特殊的静态常量-1)
rfind从指定位置向前查找指定位置的字符
substr从指定位置截取n个字符返回

size_t find (const string& str, size_t pos = 0) const;
size_t find (const char* s, size_t pos = 0) const;
size_t rfind (const string& str, size_t pos = npos) const;
size_t rfind (const char* s, size_t pos = npos) const;
string substr (size_t pos = 0, size_t len = npos) const;

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s("test.cpp");
	int num = s.find("st", 0);
	cout << num << endl;
	num = s.find("st", 5);
	cout << num << endl;
	num = s.rfind("cpp", 7);
	cout << num << endl;
	num = s.rfind("cpp", 4);
	cout << num << endl;
	string s1 = s.substr(0, 4);
	cout << s1 << endl;
	return 0;
}

在这里插入图片描述

访问及遍历操作

operator[]返回对象下标的元素
begin+ end迭代器,begin是字符串最开始的元素,end是字符串末尾的‘\0’
rbegin + rend反向迭代器

char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s("qwert");
	cout << s[2] << endl;
	s = "123456";
	string::iterator left1 = s.begin();//可读可写
	string::const_iterator left2 = s.begin();//可读不可写
	while (left1 != s.end())
	{
		*left1 += 1;
		cout << *left1;
		left1++;
	}
	cout << endl;
	while (left2 != s.end())
	{
		cout << *left2;
		left2++;
	}
	cout << endl;
	string::reverse_iterator left3 = s.rbegin();
	string::const_reverse_iterator left4 = s.rbegin();
	while (left3 != s.rend())
	{
		*left3 += 1;
		cout << *left3;
		left3++;
	}
	cout << endl;
	while (left4 != s.rend())
	{
		cout << *left4;
		left4++;
	}
	cout << endl;
	return 0;
}

C++11语法中的for循环就是与迭代器有关。
在这里插入图片描述
这里还有一个类似于operator[]的接口是at,找到的就返回,找不到会抛异常。
在这里插入图片描述

string中的swap与C++库中的swap的区别

string::swap

void swap (string& x, string& y);
交换两个同类对象中的成员

#include <iostream>
#include <assert.h>
using namespace std;
int main()
{
	string s1("asdq7777777777777777777");
	string s2("qwe");
	s1.swap(s2);
	cout << s1 << endl;
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	cout << s2 << endl;
	cout << s2.size() << endl;
	cout << s2.capacity() << endl;
	return 0;
}

在这里插入图片描述
C++库swap
这是一个模板

template <class T> void swap ( T& a, T& b )
{
  T c(a); a=b; b=c;//这里其实就相当于调用了构造函数,因为T会有自定义类型
}

平时我们的内置类型都是不需要进行析构和构造的,但是内置类型进入模板之后就需要了。
使用时要注意深浅拷贝问题,自定义类型要调用 = 。

非成员函数

operator>>
operator<<流插入流提取

istream& operator>> (istream& is, string& str);
ostream& operator<< (ostream& os, const string& str);

这个就很熟悉了上面不知道用了多少。

string类的模拟实现

#include <iostream>
#include <assert.h>
using namespace std;
namespace baiye
{
	class string
	{
	public:
		typedef char* iterator;
		string(const char* s = "")//对象初始化,缺省值有一个隐藏的\0
		{
			_arr = new char[strlen(s) + 1];
			strcpy(_arr, s);
			_size = strlen(_arr);
			_capacity = _size;//实际可以储存的大小,不算\0的位置
		}
		~string()//析构
		{
			delete[] _arr;
			_arr = nullptr;
			_size = 0;
			_capacity = 0;
		}
		string(const string& s)//拷贝构造
			:_arr(nullptr)
			,_size(0)
			,_capacity(0)
		{
			string tmp(s._arr);
			swap(tmp);
		}
		string& operator=(string s)//这里不用引用就是临时变量,我们可以让它成为打工人
		{
			swap(s);
			return *this;
		}
		//iterator
		iterator begin()const
		{
			return _arr;
		}
		iterator end()const
		{
			return _arr + _size - 1;
 		}
		// modify
		const char* c_str()const
		{
			return _arr;
		}
		void push_back(char c)
		{
			if (_size == _capacity)
			{
				size_t len = _size == 0 ? 4 : _capacity * 2;//防止空对象扩容失败
				reserve(len);
			}
			_arr[_size] = c;
			_size++;//注意\0
			_arr[_size] = '\0';
		}
		void append(const char* str)
		{
			if (_size + strlen(str) > _capacity)
			{
				reserve(_size + strlen(str) + 1);//不能二倍扩容,万一新加的字符串更大就不行了
			}
			strcpy(_arr + _size, str);
			_size = strlen(_arr);
		}
		string& operator+=(char c)
		{
			push_back(c);
			return *this;
		}
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}
		void clear()
		{
			_arr[0] = '\0';
			_size = 0;
		}
		void swap(string& s)
		{
			std::swap(_arr, s._arr);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		//capacity
		size_t size()const//返回长度
		{
			return _size;
		}
		size_t capacity()const//返回容量
		{
			return _capacity;
		}
		bool empty()const
		{
			return _size;
		}
		void resize(size_t n, char c = '\0')
		{
			if (n <= _size)
			{
				_arr[n] = '\0';
				_size = n;
			}
			else
			{
				reserve(n);
				while (_size < n)
				{
					_arr[_size] = c;
					_size++;
				}
				_arr[n] = '\0';
				_size = n;
			}
		}
		void reserve(size_t n)
		{
			char* tmp = new char[n + 1];//+1是为了算\0
			strcpy(tmp, _arr);
			delete[] _arr;
			_arr = tmp;
			_capacity = n;
		}
		// access
		char& operator[](size_t x)//可读可写
		{
			assert(x < _size);
			return _arr[x];
		}
		const char& operator[](size_t x)const//可读不可写
		{
			assert(x < _size);
			return _arr[x];
		}
		//relational operators
		int judge(const string& s)
		{
			return strcmp(_arr, s._arr);//1大于,0等于,-1小于
		}
		bool operator<(const string& s)
		{
			int a = judge(s);
			if (a < 0)
				return 1;
			return 0;
		}
		bool operator<=(const string& s)
		{
			int a = judge(s);
			if (a > 0)
				return 0;
			return 1;
		}
		bool operator>(const string& s)
		{
			int a = judge(s);
			if (a > 0)
				return 1;
			return 0;
		}
		bool operator>=(const string& s)
		{
			int a = judge(s);
			if (a < 0)
				return 0;
			return 1;
		}
		bool operator==(const string& s)
		{
			return !judge(s);
		}
		bool operator!=(const string& s)
		{
			return judge(s);
		}
		// 返回c在string中第一次出现的位置
		size_t find(char c, size_t pos = 0) const
		{
			assert(pos < _size);
			while (pos < _size)
			{
				if (_arr[pos] == c)
					return pos;
				pos++;
			}
			return npos;
		}
		// 返回子串s在string中第一次出现的位置
		size_t find(const char* s, size_t pos = 0) const
		{
			assert(pos < _size);
			const char * p =strstr(_arr + pos, s);
			if (p == nullptr)
			{
				return npos;
			}
			else
			{
				return p - _arr;
			}
		}
		// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		string& insert(size_t pos, char c)
		{
			if (_size == _capacity)
			{
				size_t len = _size == 0 ? 4 : _capacity * 2;//防止空对象扩容失败
				reserve(len);
			}
			int end = _size + 1;
			while (end > pos)
			{
				_arr[end] = _arr[end - 1];
				end--;
			}
			_arr[pos] = c;
			_size++;
			return *this;
		}
		string& insert(size_t pos, const char* str)
		{
			int len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len + 1);//不能二倍扩容,万一新加的字符串更大就不行了
			}
			int end1 = _size + 1;
			int end2 = _size + len;
			while (end1 > pos)
			{
				_arr[end2] = _arr[end1 - 1];
				end1--;
				end2--;
			}
			strncpy(_arr + pos, str, len);
			_size = strlen(_arr);
			return *this;
		}
		// 删除pos位置上的元素,并返回该元素
		string& erase(size_t pos, size_t len = npos)//nops是默认值
		{
			if (len == npos || pos + len >= _size)//删除pos后面所有的数据
			{
				_arr[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_arr + pos, _arr + pos + len);
				_size -= len;
			}
			return *this;
		}
	private:
		char* _arr;//字符串储存的地方
		int _size;//大小
		int _capacity;//容量
		const static size_t npos = -1;//C++只允许整形的静态成员在类的内部进行初始化
	};
	ostream& operator<<(ostream& out, const string& s)//不一定非要是友元
	{
		for (int i = 0; i < s.size(); i++)
		{
			out << s[i];
		}
		return  out;
	}
	istream& operator >> (istream& in, string& s)
	{
		s.clear();//清空原来s中的内容
		char buff[128] = { '\0' };//创建一个容纳输入数据的数组
		char c = in.get();
		int i = 0;
		while (c != '\n')
		{
			if (i == 127)
			{
				i = 0;
				s += buff;//数组满了放入s中,防止s不断扩容
			}
			buff[i++] = c;
			c = in.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}
}

深浅拷贝与现代写法

这里最重要的是深浅拷贝的现代写法,之前说过类的默认拷贝函数只能实现浅拷贝无法实现深拷贝,需要自定义拷贝函数,类与对象二
现代写法是让一个新创建的对象帮你拷贝,然后与你进行交换,相当于你有了一个打工人。

string(const string& s)//拷贝构造
	:_arr(nullptr)//将被拷贝的对象进行初始化
	,_size(0)
	,_capacity(0)
{
	string tmp(s._arr);//打工人tmp
	swap(tmp);//这里虽然只传参了一个tmp,但是还有一个隐藏的this指针
}
string& operator=(string s)//这里不用引用就是临时变量,我们可以让它成为打工人
{
	swap(s);
	return *this;
}
void swap(string& s)
{
	std::swap(_arr, s._arr);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

在这里插入图片描述
之后tmp的成员 _arr指向的就是空指针,进行析构也不会报错,其他成员全都变成0,而需要被拷贝的*this就成功的得到了想要的数据。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ℳℓ白ℳℓ夜ℳℓ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值