模拟实现string超详解(C++)

在这里插入图片描述

😇 😇 各位小伙伴们,大家好!我是bug。今天我们来谈谈STL中容器部分的string
(代码可能会有一点问题,请各位老铁指正 😘 😘 )

一、string的概念

string :C++中的字符串
字符串对象是一种特殊类型的容器,专门设计来操作的字符序列。 不像传统的c-strings,只是在数组中的一个字符序列,我们称之为字符数组,而C + +字符串对象属于一个类,这个类有很多内置的特点,在操作方式,更直观,另外还有很多有用的成员函数。


二、string的用法

string的常用接口

construct(构造函数)用法
string()构造空的string类对象,即空字符串
string(const char* s)用C-string来构造string类对象
string(const string&s)拷贝构造函数
操作空间/容量用法
size(length)返回字符串有效字符长度
capacity返回字符串空间总大小
empty判断字符串是否为空
clear清空有效字符
reserve为字符串预留空间
resize调整字符串大小
遍历操作用法
operator[]返回pos位置的字符,包括了const和非const的重载
beginbegin获取一个字符的迭代器
endend获取最后一个字符下一个位置的迭代器
修改操作用法
push_back尾插字符
append尾插字符串
operator+=尾插字符/字符串
c_str返回C语言形式的字符串
pop_back尾删字符
find从pos开始查找字符,找到返回下标,找不到返回npos
rfind(逆置)从pos开始查找字符,找到返回下标,找不到返回npos
比较操作用法
operator>>输入
operator<<输出
getline获取一行字符串
relational operators比较大小

🌳 (1)string对象的创建

string创建对象的方式有很多,比如:
🌵 使用默认构造函数
🌵 使用带参构造函数
🌵 使用拷贝构造函数
🌵 使用赋值运算符重载
🌵 使用迭代器区间进行构造
🌵 创建匿名对象

🍒 🍒 代码⬇️ ⬇️ :

//创建字符串对象的方式:

	//默认构造函数
	string s1;
	//带参构造函数
	string s2("hello world");
	//拷贝构造函数
	string s3(s2);
	//s4.operator=("hello world")
	string s4 = "hello world";
	//迭代器区间进行构造
	string s5(s4.begin(), s4.end());
	//匿名对象(生命周期很短)
	string();

🌳 (2)string的遍历

string有三种遍历的方式:
🌵 通过[]的重载像数组一样遍历。
🌵 通过迭代器进行遍历。
🌵 通过范围for进行的遍历

🍒 🍒 代码⬇️ ⬇️ (可改变原有数据):

//遍历字符串方式(可以改变原数据):

	string s("hello world");
	
	//通过[]重载进行遍历
	for (size_t i = 0; i < s.size(); i++)
	{
		cout << s[i] << " ";
	}
	cout << endl;
	
	//通过迭代器进行遍历
	string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	
	//通过范围for进行遍历
	for (auto &e : s)
	{
		cout << e << " ";
	}
	cout << endl;

🍒 🍒 代码⬇️ ⬇️ (不可改变原有数据):

//遍历字符串的方式(不会改变原数据):

	//通过[]重载遍历(返回值带上const)
	const string s("hello world");
	for (size_t i = 0; i < s.size(); i++)
	{
		cout << s[i] << " ";
	}
	cout << endl;
	
	//通过迭代器进行遍历(调用返回值const修饰的迭代器)
	string::const_iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	
	//通过范围for遍历(不使用引用时,将s中的数据赋值给e)
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

注意❗️ ❗️

🌱 (1)[]的重载有const和非const版本的。
🌱 (2)迭代器有const和非const版本的,C98中直接将begin、end进行重载。在C11中为了区别const和非const迭代器,引入了cbegin和cend。(和begin、end的const版本就名字不同)
🌱 (3)范围for的本质是迭代器,程序运行过程中,编译器会把范围for替换成迭代器进行操作。同时范围for是通过赋值的方式进行遍历(将数据赋给变量),如果要修改,就要使用引用。


🌳 (3)string的reserve和resize

🍒 🍒 reserve测试代码⬇️ ⬇️ :

	string s1("hello world");//容量为11
	cout << s1.capacity() << endl;
	
	//容量没有发生变化
	s1.reserve(5);
	cout << s1.capacity() << endl;

	for (auto e : s1)
	{
		cout << e << " ";
	}
	cout << endl;
	
	//扩容
	s1.reserve(20);
	cout << s1.capacity() << endl;

	for (auto e : s1)
	{
		cout << e << " ";
	}
	cout << endl;

🍒 🍒 resize测试代码⬇️ ⬇️ :

	string s1("hello world");
	cout << s1.capacity() << endl;

	//发生扩容
	s1.reserve(18);
	cout << s1.capacity() << endl;
	
	for (auto e : s1)
	{
		cout << e << " ";
	}
	cout << endl;

	//容量没有发生变化
	s1.resize(5);
	cout << s1.capacity() << endl;
	
	for (auto e : s1)
	{
		cout << e << " ";
	}
	cout << endl;

	//空位补充'!'
	s1.resize(16,'!');
	cout << s1.capacity() << endl;
	
	for (auto e : s1)
	{
		cout << e << " ";
	}
	cout << endl;

三、string的模拟实现

🍒 🍒 这里我们来模拟实现一个简易的string类⬇️ ⬇️ :

//函数
//构造函数
string(const char* ch = "", size_t size = 0, size_t capacity = 0)
string(InputIterator first, InputIterator last)
string(const string& str, size_t pos, size_t len = npos)
string(const string& str)

//交换函数
void swap(string& str)
string& operator=(const string& str)
string& operator+=(const string& str)

//比较函数
friend bool operator > (const lz::string&, const lz::string&);
friend bool operator >= (const lz::string&, const lz::string&);
friend bool operator != (const lz::string&, const lz::string&);
friend bool operator <= (const lz::string&, const lz::string&);
friend bool operator < (const lz::string&, const lz::string&);
friend bool operator == (const lz::string&, const lz::string&);

//查找修改
void reserve(size_t n)
void resize(size_t n, const char& ch = '\0')
void push_back(const char& ch)
void append(const string& str)
string& insert(size_t pos, const char& ch)
string& insert(size_t pos, const string& str)
void pop_back()
string& erase(size_t pos, size_t len = npos)
bool empty()const
size_t find(const char& ch, size_t pos = 0)const
size_t find(const char* str, size_t pos = 0)const

//遍历
char& operator[](size_t i)
const char& operator[](size_t i)const
const size_t& size()const
const size_t& capacity()const
iterator begin()
iterator end()
const_iterator begin()const
const_iterator end()const
const char* c_str()

//标准输入输出
friend istream& operator>> (istream&, lz::string&);
friend ostream& operator<< (ostream&, const lz::string&);

//变量
		char* _str;
		size_t _size;
		size_t _capacity;

🍒 🍒 完整代码⬇️ ⬇️ :

#include<iostream>
#include<assert.h>

using std::endl;
using std::cin;
using std::cout;
using std::ostream;
using std::istream;

namespace lz
{
	class string
	{
		//比较运算符重载
		friend bool operator > (const lz::string&, const lz::string&);
		friend bool operator >= (const lz::string&, const lz::string&);
		friend bool operator != (const lz::string&, const lz::string&);
		friend bool operator <= (const lz::string&, const lz::string&);
		friend bool operator < (const lz::string&, const lz::string&);
		friend bool operator == (const lz::string&, const lz::string&);
		//标准输入输出流重载
		friend istream& operator>> (istream&, lz::string&);
		friend ostream& operator<< (ostream&, const lz::string&);

	public:

		//无符号整型最大值
		static size_t npos;
		//迭代器(指针)
		typedef char* iterator;
		typedef const char* const_iterator;

		//构造函数
		string(const char* ch = "", size_t size = 0, size_t capacity = 0)
			:_str(new char[strlen(ch) + 1])
			, _size(strlen(ch))
			, _capacity(strlen(ch))
		{
			strcpy(_str, ch);
		}

		//迭代器区间构造函数
		template<class InputIterator>
		string(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				first++;
			}
			_str[_size] = '\0';
		}

		//析构函数
		~string()
		{
			delete[]_str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		//交换函数(注意!!优先使用类中的交换函数)
		void swap(string& str)
		{
			std::swap(str._str, _str);
			std::swap(str._size, _size);
			std::swap(str._capacity, _capacity);
		}

		//拷贝构造函数
		string(const string& str)
		{
			string tmp(str._str);
			swap(tmp);
		}

		//构造函数(取另一个对象的部分进行构造)
		string(const string& str, size_t pos, size_t len = npos)
		{
			//pos只能比字符串的len小
			assert(pos < str._size);
			//无符号整形的加减法问题,当len = npos时,巨坑:len + pos == 3 <str._size
			if (len > str._size - pos)//*********
			{
				len = str._size - pos;
			}

			_str = new char[len + 1];
			_size = len;
			_capacity = _size;

			for (size_t i = 0; i < len; i++)
			{
				_str[i] = str._str[pos + i];
			}

			_str[_size] = '\0';
		}

		//赋值运算符重载
		string& operator=(const string& str)
		{
			string tmp(str);
			swap(tmp);
			return *this;
		}

		//扩容
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];

				if (_size != 0)
				{
					strcpy(tmp, _str);
				}

				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}

		//扩容+初始化
		void resize(size_t n, const char& ch = '\0')
		{
			//(1)n < _size  (2)n < __caoacity && n > _size (3)n > _capacity
			if (n < _size)
			{
				_size = n;
				_str[_size] = '\0';
			}
			else
			{
				//扩容
				if (n > _capacity)
				{
					size_t new_capacity = (_capacity == 0 ? 4 : n);
					reserve(new_capacity);
				}
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_size = n;
				_str[_size] = '\0';
			}
		}

		//尾插字符
		void push_back(const char& ch)
		{
			if (_size == _capacity)
			{
				size_t new_capacity = (_capacity == 0 ? 4 : _capacity * 2);
				reserve(new_capacity);
			}
			_str[_size] = ch;
			_str[_size + 1] = '\0';
			_size++;
		}

		//尾插字符串
		void append(const string& str)
		{
			/*for (size_t i = 0; i < str._size; i++)
			{
				push_back(str._str[i]);
			}*/
			size_t len = strlen(str._str);
			if (len + _size > _capacity)
			{
				reserve(len + _size + 1);
			}
			strcpy(_str + _size, str._str);
			_size = len + _size;
		}

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

		//插入字符
		string& insert(size_t pos, const char& ch)
		{
			//判断pos要小于_size
			assert(pos < _size);
			//扩容
			if (_size == _capacity)
			{
				size_t new_capacity = (_capacity == 0 ? 4 : _capacity * 2);
				reserve(new_capacity);
			}
			//挪动数据
			for (char* i = _str + _size; i >= pos + _str; i--)
				//这里要使用指针,因为i是无符号整形,
				//当我们在头部插入字符时,i的边界是-1,无符号整形的最大值
			{
				//_str[i + 1] = _str[i];错误示范
				*(i + 1) = *(i);
			}
			//插入
			_str[pos] = ch;
			_size++;
			return *this;
		}

		//插入字符串
		string& insert(size_t pos, const string& str)
		{
			//
			for (char* i = str._str + str.size() - 1; i >= str._str; i--)
			{
				this->insert(pos, *(i));
			}
			return *this;
		}

		//尾删
		void pop_back()
		{
			assert(!empty());
			_size--;
		}

		//删除
		string& erase(size_t pos, size_t len = npos)
		{
			//串不是空串
			assert(_str);
			assert(pos < _size);

			if (len + pos > _size)
			{
				len = _size - pos;
			}

			size_t sz = len;

			while (sz)
			{
				//删除一个字符
				for (size_t i = pos; i < _size; i++)
				{
					_str[i] = _str[i + 1];
				}
				sz--;
			}
			_size = _size - len;

			return *this;
		}

		//判断串是否为空
		bool empty()const
		{
			return _size == 0;
		}

		//查找字符
		size_t find(const char& ch, size_t pos = 0)const
		{
			//查找时,不能为空串
			assert(!empty());
			assert(pos < _size);
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
				{
					return i;
				}
			}
			return 0;
		}

		//查找子串
		size_t find(const char* str, size_t pos = 0)const
		{
			assert(!empty());
			size_t len = strlen(str);
			assert(pos + len <= _size);

			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == str[0])
				{
					pos = i;
					break;
				}
			}
			//判断(1)pos(2)pos + len > _size
			//pos + len > _size
			if (pos > _size - len)
			{
				return npos;
			}
			//pos不变
			//<1>刚好找到<2>从头到尾没找到
			if (_str[pos] != str[0])// 从头到尾没找到
			{
				return npos;
			}
			for (size_t i = pos; i < len + pos; i++)
			{
				if (_str[i] != str[i - pos])
				{
					return npos;
				}
			}
			return pos;
		}

		//[]重载
		char& operator[](size_t i)
		{
			assert(i < _size);
			return _str[i];
		}

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

		//字符个数
		const size_t& size()const
		{
			return _size;
		}

		//容量
		const size_t& capacity()const
		{
			return _capacity;
		}

		//迭代器
		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		//无法修改
		const_iterator begin()const
		{
			return _str;
		}

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

		//以C语言的形式进行打印(不打印\0)
		const char* c_str()
		{
			return _str;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};


	//比较运算符重载
	bool operator>(const lz::string& str1, const lz::string& str2)
	{
		//求出较小的下标
		size_t min_index = 0;
		if (str1.size() < str2.size())
		{
			min_index = str1.size() - 1;
		}
		else
		{
			min_index = str2.size() - 1;
		}

		for (size_t i = 0; i <= min_index; i++)
		{
			if (str1[i] > str2[i])
			{
				return true;
			}
			else if (str1[i] < str2[i])
			{
				return false;
			}

		}
		if (min_index = str1.size() - 1)
			return true;
		else
			return false;
	}

	bool operator==(const lz::string& str1, const lz::string& str2)
	{
		//定义下标
		size_t index = 0;
		//字符串相等时,下标相等
		if (str1.size() == str2.size())
		{
			index = str1.size() - 1;
		}
		else
		{
			return false;
		}
		//一个一个字符进行比较
		for (size_t i = 0; i <= index; i++)
		{
			if (str1[i] != str2[i])
			{
				return false;
			}
		}
		return true;
	}

	bool operator>=(const lz::string& str1, const lz::string& str2)
	{
		return (str1 > str2) || (str1 == str2);
	}

	bool operator!=(const lz::string& str1, const lz::string& str2)
	{
		return !(str1 == str2);
	}

	bool operator<=(const lz::string& str1, const lz::string& str2)
	{
		return !(str1 > str2);
	}

	bool operator<(const lz::string& str1, const lz::string& str2)
	{
		return !(str1 >= str2);
	}

	size_t lz::string::npos = -1;//无符号整型的最大值

	//输出运算符重载
	ostream& operator<< (ostream& out, const lz::string& str)
	{
		for (size_t i = 0; i < str.size(); i++)
		{
			out << str[i];
		}
		return out;
	}

	//输入运算符重载
	istream& operator>> (istream& in, lz::string& str)
	{
		char tmp[1024] = {0};
		in >> tmp;

		size_t i = 0;
		while (tmp[i])
		{
			str.push_back(tmp[i]);
			i++;
		}

		return in;
	}
}



void Test1()//测试插入删除
{
	lz::string s1;
	lz::string s2("!!!");

	s1.push_back('a');
	s1.push_back('b');
	s1.push_back('c');
	s1.push_back('d');
	s1.push_back('d');
	s1.append(s2);
	s1.append("!!!");
	s1 += s2;
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}
	cout << endl;

	size_t ret = s1.find('a');
	size_t ret1 = s1.find("abcd");
	s1.erase(ret1,6);
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}
	cout << endl;
}

void Test2()//测试reserve+resize
{
	lz::string s1("hello world");
	cout << s1.capacity();

	s1.reserve(9);
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}
	cout << endl;
	cout << s1.capacity() << endl;

	s1.reserve(15);
	cout << s1.capacity() << endl;
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}
	cout << endl;

	s1.resize(4);
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}
	cout << endl;

	s1.resize(20,'a');
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}
	cout << endl;
}

void Test3()//测试构造函数+"="
{
	lz::string s1("hello world");
	lz::string s2(s1);
	cout << s2.c_str() << endl;

	lz::string s3(s2.begin(), s2.end());
	cout << s3.c_str() << endl;

	lz::string s4(s3, 0, 20);
	cout << s4.c_str() << endl;

	lz::string s5 = "hello world";
	cout << s5.c_str() << endl;

	s5 = "abcd";
	cout << s5.c_str() << endl;

}

void Test4()//测试遍历
{
	lz::string s1("hello world");
	lz::string::iterator it1 = s1.begin();
	while (it1 != s1.end())
	{
		cout << *it1;
		it1++;
	}
	cout << endl;

	cout << s1.c_str() << endl;

	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i];
	}
	cout << endl;

	for (auto e : s1)
	{
		cout << e;
	}
	cout << endl;

	const lz::string s2("hello world");
	lz::string::const_iterator it2 = s2.begin();
	while (it2 != s2.end())
	{
		//*it2 = 'a';
		cout << *it2;
		it2++;
	}
	cout << endl;


	for (size_t i = 0; i < s2.size(); i++)
	{
		//s2[i] = 'a';
		cout << s2[i];
	}
	cout << endl;

	for (auto& e : s2)
	{
		//e = 'a';
		cout << e;
	}
	cout << endl;
}

void Test5()//测试标准输入输出流
{
	lz::string s1;
	lz::string s2;
	cin >> s1 >> s2;
	cout << s1 << " " << s2 << endl;
}

void Test6()//测试比较运算符
{
	lz::string s1("ABCD");
	lz::string s2("ABC");
	cout << (s1 > s2) << endl;
	cout << (s1 >= s2) << endl;
	cout << (s1 < s2) << endl;
	cout << (s1 <= s2) << endl;
	cout << (s1 != s2) << endl;
	cout << (s1 == s2) << endl;
}

int main()
{
	Test1();
	return 0;
}

😎 😎 今天的内容到这里就结束了,希望对各位老铁能有所帮助,我们下期再见!
在这里插入图片描述

  • 24
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 18
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

今天也要写bug

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

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

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

打赏作者

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

抵扣说明:

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

余额充值