【C++】string的模拟实现

以utf-8编码的string进行模拟,之后还会持续维护和新增内容,有什么问题请大家及时指出。

1.string的成员变量

	private:
		char* _str = nullptr;
		//以下两参数不包含'\0'
		size_t _size = 0;
		size_t _capacity = 0;

	public:
		static const int npos;

2.构造、析构、拷贝构造、operator=()

	//构造函数
	string(const char* str ="")
		:_size(strlen(str))
	{
		//这样初始化可以减少strlen的调用
		_capacity = _size;
		_str = new char[_capacity + 1];
		strcpy(_str, str);
	}
		//s2(s1)
		//深拷贝
		//string(const string& s)
		//{
		//	_str = new char[s._capacity + 1];
		//	strcpy(_str, s._str);
		//	_size = s._size;
		//	_capacity = s._capacity;
		//}
	
		//拷贝现代写法
		//这里不能写成和operator=一样,因为传参需要调用拷贝构造
		//而这里就是实现拷贝构造,写成那样会死循环
		string(const string& s)
		{
			//调用构造
			string tmp(s._str);
			swap(tmp);
		}

		//string& operator=(const string& s)
		//{
		//	char* tmp = new char[s._capacity + 1];
		//	strcpy(tmp, s._str);

		//	delete[] _str;
		//	_str = tmp;
		//	_size = s._size;
		//	_capacity = s._capacity;

		//	return *this;
		//}
		//现代写法
		string& operator=(string s)
		{
			swap(s);
			return *this;
		}
		
		//析构函数
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

3.c_str()、swap()、clear()

	//返回一个指向正规C字符串的指针常量, 内容与本string串相同
	//为了与c语言兼容,在c语言中没有string类型,
	//故必须通过string类对象的成员函数c_str()
	//把string 对象转换成c中的字符串样式
	const char* c_str() const{
		return _str;
	}
	
	//交换
	//使用模板的swap调用三次拷贝和一次析构,效率低
	void swap(string& s)
	{
		std::swap(_str, s._str);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}
	
	void clear()
	{
		_size = 0;
		_str[_size] = '\0';
	}

4.size()、capacity()、operator[]()


	size_t size() const{
		return _size;
	}
	
	size_t capacity() const{
		return _capacity;
	}

	char& operator[](size_t pos)
	{
		//检查越界,相较于数组的优势
		assert(pos < _size);
		//_str所在的区域在堆上,出了作用域还在
		//用引用返回返回的不是拷贝,是别名
		return _str[pos];
	}
	//为了const string加的函数重载  
	const char& operator[](size_t pos) const 
	{
		assert(pos < _size);
		return _str[pos];
	}

5.迭代器iterator

	typedef char* iterator;
	typedef const char* const_iterator;
	const_iterator begin() const{
		return _str;
	}

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

	iterator begin() {
		return _str;
	}

	iterator end(){
		return _str + _size;
	}

6.reserve()、resize()

reserve用来开辟空间,resize用来指定容器大小

	//开辟空间,若n<_capacity,则保持不变
	void reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];//多开一个,存 \0
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}
	//如果指定的大小大于原来的大小,扩充出来的空间全部置为ch
	void resize(size_t n,char ch = '\0')
	{
		if (n <= _size)
		{
			_str[n] = '\0';
			_size = n;
		}
		else {
			reserve(n);
			for (size_t i = _size; i < n; i++)
			{
				_str[i] = ch;
			}
			_str[n] = '\0';
			_size = n;
	
		}
	}
	

7.push_back(),append(),operator+=()

	void push_back(char ch)
	{
		//扩容2倍
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : 2 * _capacity);
		}
		_str[_size] = ch;
		_size++;
		_str[_size] = '\0';

		//复用insert()
		//insert(_size, ch);
	}

	void append(const char* str)
	{
		size_t len = strlen(str);
		if (len + _size > _capacity)
		{
			reserve(_size + len);
		}
		//strcat先要从头遍历到'\0',才在后面追加
		//strcpy会把'\0'也拷贝过去
		strcpy(_str + _size, str);
		_size += len;

		//复用insert()
		//insert(_size, str);
	}

	string& operator+= (char ch)
	{
		push_back(ch);
		return *this;
	}
	string& operator+=(const char* str)
	{
		append(str);
		return *this;
	}

8.insert()、erase()

	void insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : 2 * _capacity);
		}
		int end = _size;
		//pos==0的时候由于end=-1,pos类型为size_t
		//end强转为size_t类型,导致死循环,所以pos要强转
		while (end >= (int)pos){
			_str[end + 1] = _str[end];
			--end;
		}
		_str[pos] = ch;
		_size++;
	}

	void insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			// 扩容
			reserve(_size + len);
		}

		size_t end = _size + len;
		while (end > pos + len - 1)
		{
			_str[end] = _str[end - len];
			end--;
		}
		//char *strncpy(char *dest, const char *src, size_t n) 
		//把 src 所指向的字符串复制到 dest,最多复制 n 个字符。
		//当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。
		strncpy(_str + pos, str, len);
		_size += len;
	}

	void erase(size_t pos, size_t len = npos)
	{
		assert(pos < _size);
		//len > _size - pos 解决溢出风险
		if (len == npos || len > _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else {
			strcpy(_str + pos, _str + pos + len);
			_size -= len;
		}
	}

9.find()、substr()

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

	size_t find(const char* sub, size_t pos = 0) const {

		assert(pos < _size);
		const char* p = strstr(_str + pos, sub);
		if (p)
		{
			return p - _str;
		}
		else
		{
			return npos;
		}
	}

	string substr(size_t pos = 0, size_t len = npos)
	{
		string sub;
		if (len == npos || len >= _size-pos)
		{
			for (size_t i = pos; i < _size ; i++)
			{
				sub += _str[i];
			}
		}
		else
		{
			for (size_t i = pos; i < pos + len; i++)
			{
				sub += _str[i];
			}

		}
		return sub;
	}

10.比较运算符重载

	bool operator==(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret == 0;
	}

	bool operator>(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret > 0;
	}
	bool operator<(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret < 0;
	}

	bool operator>=(const string& s1, const string& s2)
	{
		return s1 > s2 || s1 == s2;
	}
	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}
	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

11.operator<<(),operator>>()、getline()

	std::ostream& operator<<(std::ostream& out, const string& s)
	{
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}

	std::istream& operator>>(std::istream& in, string& s)
	{
		s.clear();
		char ch;
		ch=in.get();
		//使用字符数组存储,可以避免频繁扩容,效率低
		char buff[128];
		size_t i = 0;
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[127] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}

		return in;
	}
	std::istream& getline(std::istream& in, string& s)
	{
		s.clear();
		char ch;
		ch = in.get();
		char buff[128];
		size_t i = 0;
		while (ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[127] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}
	}

12.总代码

	class string {
	public:
		//迭代器
		typedef char* iterator;
		typedef const char* const_iterator;
		const_iterator begin() const{
			return _str;
		}
	
		const_iterator end() const{
			return _str + _size;
		}
	
		iterator begin() {
			return _str;
		}
	
		iterator end(){
			return _str + _size;
		}
	
		//string()
		//	:_str(new char[1])
		//	,_size(0)
		//	,_capacity(0)
		//{
		//	_str[0] = '\0';
		//}
	
		//初始化列表是按声明顺序进行的
		//全缺省构造函数
		string(const char* str ="")
			:_size(strlen(str))
		{
			//这样初始化可以减少strlen的调用
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		//拷贝构造函数:它用于在创建一个新对象时,将一个已有对象的值复制到新对象中。
		//通常在对象的初始化过程中调用,如对象声明时的初始化、函数参数传递(按值传递)或返回对象(按值返回)等情况。
		//赋值运算符重载函数:它用于将一个已有对象的值赋给另一个已有对象。
		//通常在已存在的对象上调用,使用赋值运算符
		//s2(s1)
		//深拷贝
		//string(const string& s)
		//{
		//	_str = new char[s._capacity + 1];
		//	strcpy(_str, s._str);
		//	_size = s._size;
		//	_capacity = s._capacity;
		//}
	
		//拷贝现代写法
		//这里不能写成和operator=一样,因为传参需要调用拷贝构造
		//而这里就是实现拷贝构造,写成那样会死循环
		string(const string& s)
		{
			//调用构造
			string tmp(s._str);
			swap(tmp);
		}
	
		//string& operator=(const string& s)
		//{
		//	char* tmp = new char[s._capacity + 1];
		//	strcpy(tmp, s._str);
	
		//	delete[] _str;
		//	_str = tmp;
		//	_size = s._size;
		//	_capacity = s._capacity;
	
		//	return *this;
		//}
		//现代写法
		string& operator=(string s)
		{
			swap(s);
			return *this;
		}
		//析构函数
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}
		//返回一个指向正规C字符串的指针常量, 内容与本string串相同
		//为了与c语言兼容,在c语言中没有string类型,
		//故必须通过string类对象的成员函数c_str()
		//把string 对象转换成c中的字符串样式
		const char* c_str() const{
			return _str;
		}
	
		//遍历
		//将const修饰的“成员函数”称之为const成员函数,
		// const修饰类成员函数,实际修饰该成员函数隐含的this指针,
		//表明在该成员函数中不能对类的任何成员进行修改
		//返回string的大小
		size_t size() const{
			return _size;
		}
		
		size_t capacity() const{
			return _capacity;
		}
	
	
		char& operator[](size_t pos)
		{
			//检查越界,相较于数组的优势
			assert(pos < _size);
			//_str所在的区域在堆上,出了作用域还在
			//用引用返回返回的不是拷贝,是别名
			return _str[pos];
		}
		//为了const string加的函数重载  
		const char& operator[](size_t pos) const 
		{
			assert(pos < _size);
			return _str[pos];
		}
	
		//如果指定的大小大于原来的大小,扩充出来的空间全部置为ch
		void resize(size_t n,char ch = '\0')
		{
			if (n <= _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else {
				reserve(n);
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_str[n] = '\0';
				_size = n;
	
			}
		}
	
	
		//开辟空间,若n<_capacity,则保持不变
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];//多开一个,存 \0
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}
	
		void push_back(char ch)
		{
			//扩容2倍
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';
	
			//使用insert()
			//insert(_size, ch);
		}
	
		void append(const char* str)
		{
			size_t len = strlen(str);
			if (len + _size > _capacity)
			{
				reserve(_size + len);
			}
			//strcat先要从头遍历到'\0',才在后面追加
			//strcpy会把'\0'也拷贝过去
			strcpy(_str + _size, str);
			_size += len;
	
			//使用insert()
			//insert(_size, str);
		}
	
		string& operator+= (char ch)
		{
			push_back(ch);
			return *this;
		}
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}
	
		//插入单个字符
		void insert(size_t pos, char ch)
		{
			assert(pos <= _size);
	
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}
	
			int end = _size;
			//pos==0的时候由于end=-1,pos类型为size_t
			//end强转为size_t类型,导致死循环,所以pos要强转
			while (end >= (int)pos)
			{
				_str[end + 1] = _str[end];
				--end;
			}
			_str[pos] = ch;
			_size++;
		}
		//在某个位置插入字符串
		void insert(size_t pos, const char* str)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				// 扩容
				reserve(_size + len);
			}
	
			size_t end = _size + len;
			while (end > pos + len - 1)
			{
				_str[end] = _str[end - len];
				end--;
			}
			//char *strncpy(char *dest, const char *src, size_t n) 
			//把 src 所指向的字符串复制到 dest,最多复制 n 个字符。
			//当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。
			strncpy(_str + pos, str, len);
			_size += len;
		}
	
		//删除某个位置的字符
		void erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);
			//len > _size - pos 解决溢出风险
			if (len == npos || len > _size - pos)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else {
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
		}
		//交换
		//使用模板的swap调用三次拷贝和一次析构,效率低
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
	
		size_t find(char ch, size_t pos = 0) const {
			
			assert(pos < _size);
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
					return i;
			}
			return npos;
		}
	
		size_t find(const char* sub, size_t pos = 0) const {
	
			assert(pos < _size);
			const char* p = strstr(_str + pos, sub);
			if (p)
			{
				return p - _str;
			}
			else
			{
				return npos;
			}
		}
	
		string substr(size_t pos = 0, size_t len = npos)
		{
			string sub;
			if (len == npos || len >= _size-pos)
			{
				for (size_t i = pos; i < _size ; i++)
				{
					sub += _str[i];
				}
			}
			else
			{
				for (size_t i = pos; i < pos + len; i++)
				{
					sub += _str[i];
				}
	
			}
			return sub;
		}
		//将容器清空
		void clear()
		{
			_size = 0;
			_str[_size] = '\0';
		}
	
	private:
		char* _str = nullptr;
		//以下两参数不包含'\0'
		size_t _size = 0;
		size_t _capacity = 0;
	
	public:
		static const int npos;
	
	};
	const int string::npos = -1;
	
	//重载swap
	void swap(string& x, string& y)
	{
		x.swap(y);
	}
	
	bool operator==(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret == 0;
	}
	
	bool operator>(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret > 0;
	}
	bool operator<(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret < 0;
	}
	
	bool operator>=(const string& s1, const string& s2)
	{
		return s1 > s2 || s1 == s2;
	}
	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}
	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}
	
	std::ostream& operator<<(std::ostream& out, const string& s)
	{
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}
	
	std::istream& operator>>(std::istream& in, string& s)
	{
		s.clear();
		char ch;
		ch=in.get();
		//使用字符数组存储,可以避免频繁扩容,效率低
		char buff[128];
		size_t i = 0;
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[127] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}
	
		return in;
	}
	std::istream& getline(std::istream& in, string& s)
	{
		s.clear();
		char ch;
		ch = in.get();
		char buff[128];
		size_t i = 0;
		while (ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[127] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}
	}
### 实现C++自定义String类 为了创建一个具备基本功能的`String`类,可以考虑以下几个方面: #### 类成员变量 首先声明私有成员来存储字符数组以及字符串长度。 ```cpp class Mystring { private: char* str; size_t length; }; ``` 这允许内部管理动态分配的内存用于保存实际字符串数据[^1]。 #### 构造函数与析构函数 提供默认构造函数、带参数构造函数和拷贝构造函数,并实现析构函数以释放资源。 ```cpp public: // 默认构造函数 Mystring() : str(nullptr), length(0) {} // 带参构造函数 explicit Mystring(const char* s) { if (s != nullptr) { length = strlen(s); str = new char[length + 1]; strcpy(str, s); } else { str = nullptr; length = 0; } } // 拷贝构造函数 Mystring(const Mystring& source) { length = source.length; if (source.str != nullptr) { str = new char[source.length + 1]; strcpy(str, source.str); } else { str = nullptr; } } // 析构函数 ~Mystring() { delete[] str; } } ``` 上述代码展示了如何初始化对象并处理深浅复制问题。 #### 运算符重载 通过运算符重载使`Mystring`能够像内置类型一样操作。这里展示赋值运算符(`=`)的例子。 ```cpp // 赋值运算符 Mystring& operator=(const Mystring& rhs) { if (this == &rhs) return *this; delete[] str; length = rhs.length; if (rhs.str != nullptr) { str = new char[rhs.length + 1]; strcpy(str, rhs.str); } else { str = nullptr; } return *this; } ``` 此部分实现了安全的对象间赋值逻辑。 #### 成员方法 增加一些实用的方法如获取字符串大小、访问单个字符等。 ```cpp size_t get_length() const {return length;} char at(size_t pos) const {if(pos<length)return str[pos];else throw std::out_of_range("Out of range");} void append(const Mystring& other){ char* temp = new char[this->length + other.length + 1]; strcpy(temp, this->str); strcat(temp, other.str); delete[] this->str; this->str = temp; this->length += other.length; } ``` 这些辅助函数增强了类的功能性和易用性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值