C++STL【string】下模拟实现string

string类的接口总览

namespace cl
{
	//模拟实现string类
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		//默认成员函数
		string(const char* str = "");         //构造函数
		string(const string& str);              //拷贝构造函数
		string& operator=(const string& str);   //赋值运算符重载函数
		~string();                            //析构函数

		//容量操作函数
		size_t size();
		size_t capacity();
		void reserve(size_t n);
		void resize(size_t n, char ch = '\0');
		bool empty()const;

		//迭代器相关函数
		iterator begin();
		iterator end();
		const_iterator cbegin();
		const_iterator cend();
		const_iterator begin()const;
		const_iterator end()const;

		//修改字符串相关函数
		string& operator+=(char ch);
		string& operator+=(const char* str);
		string& insert(size_t pos, char ch);
		string& insert(size_t pos, const char* str);
		string& erase(size_t pos, size_t len);
		void push_back(char ch);
		void append(const char* str);
		void swap(string& str);
		const char* c_str()const;
		void clear();

		//访问字符串相关函数
		char& operator[](size_t i);
		const char& operator[](size_t i)const;
		size_t find(char ch, size_t pos = 0)const;
		size_t find(const char* str, size_t pos = 0)const;
		size_t rfind(char ch, size_t pos = npos)const;
		size_t rfind(const char* str, size_t pos = 0)const;

		//关系运算符重载函数
		bool operator>(const string& str)const;
		bool operator<(const string& str)const;
		bool operator>=(const string& str)const;
		bool operator<=(const string& str)const;
		bool operator==(const string& str)const;
		bool operator!=(const string& str)const;

	private:
		char* _str;       //存储字符串
		size_t _size;     //记录字符串当前的有效长度,不包含\0
		size_t _capacity; //记录字符串当前的容量,不包含\0的大小,但是开辟空间时要包含\0在内
		static const size_t npos; //静态成员变量(整型最大值)
	};
	const size_t string::npos = -1;

	//<<和>>运算符重载函数
	istream& operator>>(istream& in, string& str);
	ostream& operator<<(ostream& out, const string& str);
	istream& getline(istream& in, string& str);
}

默认成员函数

构造函数

构造函数就是开辟空间,给成员变量进行初始化。缺省参数就可以少写一个无参构造函数,若不传参默认生成空字符串。空字符串是有大小的包含了’\0’。

		string(const char* str = "")
			:_size(strlen(str))//strlen会检查空指针
			,_capacity(_size)
		{

			_str=new char[_capacity + 1];
			strcpy(_str, str);
		}

拷贝构造函数

传统写法:

		string(const string& str)
			:_str(new char[strlen(str._str) + 1]) //_str申请一块刚好可以容纳str._str的空间
			, _size(0)
			, _capacity(0)
		{
			strcpy(_str, str._str);    
			_size = str._size;        
			_capacity = str._capacity; 
		}

现代写法:

		//现代写法
		string(const string& str)
			:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{
			string tmp(str._str);//出了作用域对象调用析构
			swap(tmp);//交换对象
		}

赋值运算符重载函数

传统写法:

		string& operator=(const string& str)
		{
			// 没有处理异常情况,错误情况
			/*if (this != &str)//防止自己给自己赋值
			{
				delete[] _str;
				_str = nullptr;
				_str = new char[_capacity + 1];
				strcpy(_str, str._str);
				_size=str.size;
				_capacity=str._capacity;

			}*/
			if (this != &str)//防止自己给自己赋值
			{
				char* tmp = _str = new char[_capacity + 1];
				strcpy(tmp, str._str);
				delete[] _str;
				_str = tmp;
				_size = str._size;
				_capacity = str._capacity;
			}

			return *this;
		}

注:这里需要先申请空间,如果申请空间失败会抛异常跳到处理异常,不会执行后面的代码,所以先申请空间,发生错误时就不会影响到原有的对象。
现代写法:
通过编译器自动调用拷贝构造一个对象str,然后交换对象。

		//现代写法一、
	string& operator=( string str)
		{
			swap(str);
			return *this;
		}

现代写法一、不能判断自己给自己赋值,自己给自己赋值影响也不是很大,就是最后的空间地址发生了变化。

//现代写法2
string& operator=(const string& str)
{
	if (this != &str) //防止自己给自己赋值
	{
		string tmp(str); //用s拷贝构造出对象tmp
		swap(tmp); //交换这两个对象
	}
	return *this; //返回左值(支持连续赋值)
}

析构函数

析构函数需要我们写,因为string类对象都需要开辟动态内存的资源,动态开辟的空间需要我们手动清理。

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

迭代器相关函数

string 的迭代器实际就是char*的别名。

		typedef char* iterator;
		typedef const char* const_iterator;
		// 非const对象调用
		iterator begin()
		{
			return _str;
		}
		// 非const对象调用
		iterator end()
		{
			return _str + _size;
		}
		// const对象调用
		const_iterator begin()const
		{
			return _str;
		}
		// const对象调用
		const_iterator end()const
		{
			return _str + _size;
		}
		const_iterator cbegin()
		{
			return _str;
		}
		const_iterator cend()
		{
			return _str + _size;
		}

注意:end()返回的迭代器执行末尾’\0’。
范围for的原理:把范围for的代码替换成迭代器访问。

string str("hello BBQ");
//编译器将其替换为迭代器形式
for (auto val : str)
{
	cout << val << " ";
}
cout << endl;

auto it = s1.begin();
while (it != s1.end())
{
	*it += 1;
	++it;
}

容量和大小相关函数

size和capacity

//大小
size_t size()const
{
	return _size; //返回字符串当前的有效长度
}
//容量
size_t capacity()const
{
	return _capacity; //返回字符串当前的容量
}

reserve和resize

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

		_str = tmp;

		_capacity = n;
	}
}
void resize(size_t n, char ch = '\0')
{
	if (n > _size)
	{
		reserve(n); 
		memset(_str + _size, ch, n - _size);
	}
		_size = n;
		_str[_size] = '\0';	
}

修改字符串相关函数

insert

		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity==0?4:2*_capacity);
			}
			size_t end = _size+1;//+1:'\0'也进行处理,后面就不需要末尾添加'\0'了
			while (pos < end)
			{
				_str[end] = _str[end - 1];
				end--;
			}
			_str[pos] = ch;
			++_size;
			return *this;
		}
		string& insert(size_t pos, const char* str)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}
			size_t end = _size + len;//+1:'\0'也进行处理,后面就不需要末尾添加'\0'了
			while (pos+len <= end)
			{
				_str[end] = _str[end - len];
				end--;
			}
			strncpy(_str + pos, str, len);//拷贝时不包含'\0'
			_size += len;
			return *this;
		}

push_back

	void push_back(char ch)
	{
			/*if (_size = _capacity)
			{
				reserve(2 * _capacity);//增容两倍
			}
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';
			*/
			insert(_size, ch);
	}

append

		void append(const char* str)
		{
			/*size_t len = strlen(str);
			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}
			strcpy(_str + _size, str);
			_size += len;*/
			insert(_size, str);
		}

operator+=

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

erase

		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size);

			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}

			return *this;
		}

swap

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

c_str

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

访问字符串相关函数

operator[ ]

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

find和rfind

		size_t find(const char* s, size_t pos = 0)
		{
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}
		size_t find(char ch)
		{
			for (size_t i = 0; i < _size; ++i)
			{
				if (ch == _str[i])
				{
					return i;
				}
			}
			return npos;
		}
//反向查找第一个匹配的字符
size_t rfind(char ch, size_t pos = npos)
{
	string tmp(*this); //拷贝构造对象tmp
	reverse(tmp.begin(), tmp.end()); //调用reverse逆置对象tmp的C字符串
	if (pos >= _size) //所给pos大于字符串有效长度
	{
		pos = _size - 1; //重新设置pos为字符串最后一个字符的下标
	}
	pos = _size - 1 - pos; //将pos改为镜像对称后的位置
	size_t ret = tmp.find(ch, pos); //复用find函数
	if (ret != npos)
		return _size - 1 - ret; //找到了,返回ret镜像对称后的位置
	else
		return npos; //没找到,返回npos
}

//反向查找第一个匹配的字符串
size_t rfind(const char* str, size_t pos = npos)
{
	string tmp(*this); //拷贝构造对象tmp
	reverse(tmp.begin(), tmp.end()); //调用reverse逆置对象tmp的C字符串
	size_t len = strlen(str); //待查找的字符串的长度
	char* arr = new char[len + 1]; //开辟arr字符串(用于拷贝str字符串)
	strcpy(arr, str); //拷贝str给arr
	size_t left = 0, right = len - 1; //设置左右指针
	//逆置字符串arr
	while (left < right)
	{
		::swap(arr[left], arr[right]);
		left++;
		right--;
	}
	if (pos >= _size) //所给pos大于字符串有效长度
	{
		pos = _size - 1; //重新设置pos为字符串最后一个字符的下标
	}
	pos = _size - 1 - pos; //将pos改为镜像对称后的位置
	size_t ret = tmp.find(arr, pos); //复用find函数
	delete[] arr; //销毁arr指向的空间,避免内存泄漏
	if (ret != npos)
		return _size - ret - len; //找到了,返回ret镜像对称后再调整的位置
	else
		return npos; //没找到,返回npos
}

关系运算符重载函数

//>运算符重载
bool operator>(const string& s)const
{
	return strcmp(_str, s._str) > 0;
}
//==运算符重载
bool operator==(const string& s)const
{
	return strcmp(_str, s._str) == 0;
}

//>=运算符重载
bool operator>=(const string& s)const
{
	return (*this > s) || (*this == s);
}
//<运算符重载
bool operator<(const string& s)const
{
	return !(*this >= s);
}
//<=运算符重载
bool operator<=(const string& s)const
{
	return !(*this > s);
}
//!=运算符重载
bool operator!=(const string& s)const
{
	return !(*this == s);
}

>>和<<运算符的重载以及getline函数

>>运算符的重载

//>>运算符的重载
istream& operator>>(istream& in, string& s)
{
	s.clear(); //清空字符串
	char ch = in.get(); //读取一个字符
	while (ch != ' '&&ch != '\n') //当读取到的字符不是空格或'\n'的时候继续读取
	{
		s += ch; //将读取到的字符尾插到字符串后面
		ch = in.get(); //继续读取字符
	}
	return in; //支持连续输入
}

<<运算符的重载

//<<运算符的重载
ostream& operator<<(ostream& out, const string& s)
{
	//使用范围for遍历字符串并输出
	for (auto e : s)
	{
		cout << e;
	}
	
	//cout<< s.c_str();//这种写法遇到\0结束,不能这么写
	return out; //支持连续输出
}

getline

//读取一行含有空格的字符串
istream& getline(istream& in, string& s)
{
	s.clear(); //清空字符串
	char ch = in.get(); //读取一个字符
	while (ch != '\n') //当读取到的字符不是'\n'的时候继续读取
	{
		s += ch; //将读取到的字符尾插到字符串后面
		ch = in.get(); //继续读取字符
	}
	return in;
}

string面试的正确写法
STL 的string类怎么啦?
写时拷贝
写时拷贝的缺陷

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

2023框框

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

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

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

打赏作者

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

抵扣说明:

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

余额充值