【C++】STL---string类的模拟实现-创新互联

目录
  • 前言
  • 类的属性
  • 构造函数
    • 构造函数
    • 析构函数
    • 拷贝构造函数
  • 操作符重载
    • 赋值操作符重载
    • 比较操作符重载
  • 获取字符串长度
  • 下标访问
  • 迭代器
  • 尾插一个字符
  • 追加字符串
  • 字符串清空
  • 字符串交换
  • 以C的方式返回字符
  • 判断字符串是否为空
  • 修改长度
  • 指定大小扩容
  • 查找字符
  • 查找子串
  • 指定位置插入字符
  • 指定位置插入字符串
  • 删除指定区间
  • 总结:

创新互联主营门头沟网站建设的网络公司,主营网站建设方案,手机APP定制开发,门头沟h5成都小程序开发搭建,门头沟网站营销推广欢迎门头沟等地区企业咨询前言

  string类,想必大家都不陌生,这是一个用来管理字符串的类。让我们使用字符串时更方便,更遍历。所以今天我们就来简单的实现一下string类。


类的属性

我们要管理一个字符串,那么首先得有一个字符串。所以我们用char* 类型的指针,来指向一个块存储字符串的地址。_size则记录字符串的长度,_cacpcity 为字符串的空间容量

private:
		char* _str;
		size_t _size;
		size_t _cacpcity;
构造函数 构造函数

我们在实例化一个string对象的时候,可以string s("hello world");直接创建,也可以string s();使它初始时为空,所以我们可以用缺省的构造函数。如果不传参数,那么默认初始化为空。

//缺省的构造函数 
		string(const char* str = "")
			: _size(strlen(str))
			,_cacpcity(_size)
		{	//开辟一块内存
			_str = new char[_cacpcity + 1];//有效容量是cacpcity,要多一个用来存放\0
			strcpy(_str, str);
		}
析构函数

析构函数我们只需要释放 _str指向的空间即可。

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

拷贝构造,就是拷贝一个字符串 例如:string s1("hello world"); string s2(s1);。所以我们开辟一块与要拷贝字符串一样大小的空间,再把它一一复制给新字符串即可。

//拷贝构造
		string(const string& s)
			:_size(s._size)
			, _cacpcity(s._cacpcity)
		{	//开辟一块和s一样的空间
			_str = new char[_cacpcity+1];
			strcpy(_str, s._str);
		}
操作符重载 赋值操作符重载

如果我们想用 = 操作符来给字符串赋值。那我们可以重载 = 操作符。

//赋值操作符重载
		string& operator=(const string& s)
		{	//如果不是自己给自己赋值
			if (this != &s)
			{		//创建一块新空间
				char* tmp = new char[s._cacpcity+1];
				//拷贝
				strcpy(tmp, s._str);
				//销毁旧空间
				delete[] _str;
				_str = tmp;
				_size = s._size;
				_cacpcity = s._cacpcity;
			}
			return *this;
		}

当然也可以复用拷贝构造函数。

比较操作符重载

<重载

//字符串比较函数重载
		bool operator<(const string& s)
		{	return strcmp(_str, s._str)< 0;
		}

== 重载

bool operator==(const string& s)
		{	return strcmp(_str, s._str) == 0;
		}

<= 重载

bool operator<=(const string& s)
		{	return (*this< s) || (*this == s);
		}

  >重载

bool operator>(const string& s)
		{	return !((*this)<= s);
		}

 >=重载

bool operator>=(const string& s)
		{	return !(*this< s);
		}

!=重载

bool operator!=(const string& s)
		{	return !(*this == s);
		}
获取字符串长度

直接返回_size 即可

//获取长度
		size_t size()
		{	return _size;
		}
下标访问

字符串也可以进行下标访问,所以我们重载[]即可

//下标访问
		char& operator[](size_t pos)
		{	return _str[pos];
		}

当然,如果访问的是const修饰的字符串,那我们只能读,不能写。

//只读
		const char& operator[](size_t pos) const
		{	return _str[pos];
		}
迭代器
//定义2个迭代器,一个是可读可写,一个是只读
		typedef char* iterator;
		typedef const char* const_iterator;
//迭代器开始位置
		iterator begin()
		{	return _str;
		}
		const_iterator begin() const
		{	return _str;
		}

		//迭代器末尾位置
		iterator end()
		{	return _str + _size;
		}

		const_iterator end()const
		{	return _str + _size;
		}
尾插一个字符

就是在字符串末端插入一个字符,我们只需要在_size的位置插入,随后_size++即可。不过要保证容量必须充足。

//尾插一个字符
		void push_back(char c)
		{	//检查容量
			if (_size == _cacpcity)
			{		AddCacpcity(_cacpcity == 0 ? 15 : _cacpcity * 2);
			}
			_str[_size] = c;
			_size++;
			_str[_size] = '\0';
		}

扩容函数

void AddCacpcity(size_t newCacpcity)
		{		//末尾位置留一个\0,所以+1
				char* str = new char[newCacpcity + 1];
				//旧字符串的内容拷贝到新字符串
				strcpy(str, _str);
				//销毁旧字符串
				delete[] _str;
				_str = str;
				_cacpcity = newCacpcity;
		}
追加字符串

如果想在尾部追加字符串的话,我们可以实现一个 append()函数。

//追加一个字符串
		void append(const char* str)
		{	//检查容量
			if (_cacpcity< (_size)+strlen(str))
			{		AddCacpcity(_size + strlen(str));
			}
			//直接在末尾的位置加上str
			strcpy(_str + _size, str);
			_size += strlen(str);
		}

然后我们可以重载 +=运算符,拿来复用 append()函数。

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

		}

然后我们重载 +运算符,但需要注意的是,+运算符不改变当前字符串,所以我们要值返回。

//+重载
		string operator+(const char* str)
		{	string s(*this);
			s += str;

			return s;
		}
字符串清空

直接把第一个字符改成\0即可

//清空
		void clear()
		{	_str[0] = '\0';
			_size = 0;
		}
字符串交换

我们用std命名空间里面的swap函数,对每个成员进行交换。

//字符串交换
		void swap(string& s)
		{	std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_cacpcity, s._cacpcity);
		}
以C的方式返回字符

c语言的字符串其实就是一个char*指针,遇到\0结束,所以我们直接返回str就可以了。

//以c的方式返回字符串
		const char* c_str()const
		{	return _str;
		}
判断字符串是否为空

直接判断第一个元素是否是 \0

//判断字符串是否为空
		bool empty()const
		{	return _str[0] == '\0';
		}
修改长度

修改长度我们要分多种情况,一般长度减少时,我们不改变现有容量。容量不够时,我们才增容。 如果 长度减少,或者不变,我们只需要把 减少的新长度给_size,然后把当前位置变成\0。如果是增加长度,我们要考虑容量,不够的话需要增容,然后memset函数把增容的部分改成你指定的字符(默认\0)。

//修改长度
		void resize(size_t n, char c = '\0')
		{	//如果修改的值比当前长度小
			if (n<= _size)
			{		//截断
				_size = n;
				_str[_size] = '\0';
			}
			//如果修改的值比当前长度大
			else
			{		//扩容
				if(n >_cacpcity)
				{AddCacpcity(n);
				}
				//从size位置到n的位置设置为c
				memset(_str + _size, c, n - _size);
				//最后位置填上\0
				_size = n;
				_str[_size] = '\0';
			}
		}
指定大小扩容

之前写过一个扩容函数,直接把指定的大小传过去即可。还是老规矩,减少不处理。

//指定容量。只增加,减少不处理
		void reserve(size_t n)
		{	if (n >_cacpcity)
			{		AddCacpcity(n);
			}
		}
查找字符

该查找只会找到从指定位置开始,第一个出现的字符。如果要查找第二个,那么就在第一个字符的后面开始查找。

// 返回c在string中第一次出现的位置
		size_t find(char c, size_t pos = 0) const
		{	for (int i = pos; i< _size; i++)
			{		if (_str[i] == c)
					return i;
			}
			
			return nops;
		}

nops是一个无符号的数,代表找不到返回的值。

static const size_t nops = -1;
查找子串

C语言中有strstr函数,我们可以复用。

// 返回子串s在string中第一次出现的位置
		size_t find(const char* s, size_t pos = 0) const
		{	char* tmp = strstr(_str, s);
			if (tmp == NULL)
				return nops;
			
			return tmp - _str;
		}
指定位置插入字符

只需要把pos位置后面的字符都往后移动一格,随后把字符放进pos位置。

// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		string& insert(size_t pos, char c)
		{	//判断容量
			if (_size == _cacpcity)
			{		AddCacpcity(_cacpcity == 0 ? 15 : _cacpcity * 2);
			}
			//pos位置后往后移
			size_t end = _size + 1;
			while (pos< end)
			{		_str[end] = _str[end - 1];
				end--;
			}
			_str[pos] = c;
			_size++;

			return *this;
		}
指定位置插入字符串

插入字符是都往后移动一格,插入字符串那就是把pos位置后面的字符都像后 移动字符串的长度格。然后把字符串从pos位置开始写入。

//插入字符串
		string& insert(size_t pos, const char* str)
		{	size_t len = strlen(str);
			//判断容量
			if (_cacpcity< (len + _size))
			{		AddCacpcity(len + _size);
			}
			//移动len格
			size_t end1 = _size+1;
			size_t end2 = _size + len ;
			while (pos< end1 )
			{		_str[end2] = _str[end1-1]  ;
				end1--;
				end2--;
			}
			int i = pos;
			while (*str)
			{		 _str[i++] = *str++;
			}
			_size += len;
			return *this;
		}
删除指定区间

从pos位置开始,删除len个空间。那么我们需要先判断 len是否大于pos后面的长度,如果大于那就是后面全部删除,那么我们只需要把pos位置置空成\0即可。如果不大于就说明在中间删除,那么就从pos的第len个位置开始往pos后面的位置覆盖,覆盖到\0结束。

// 删除
		string& erase(size_t pos, size_t len)
		{	//如果要删除的长度大于后面的剩余长度
			if (len >= _size - pos)
			{		len = _size - pos;
				_size -= len;
				_str[pos] = '\0';
				return *this;
			}
			//把后面的往前移,覆盖式删除
			size_t begin = pos+len;
			while (_str[begin])
			{		_str[begin-len] = _str[begin];
				begin++;
			}
			_size -= len;
			return *this;
		}
		

全部代码:

namespace wyl
{class string
	{public:
		typedef char* iterator;
		typedef const char* const_iterator;
		//缺省的构造函数 
		string(const char* str = "")
			: _size(strlen(str))
			,_cacpcity(_size)
		{	//开辟一块内存
			_str = new char[_cacpcity + 1];
			strcpy(_str, str);
		}

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

		//拷贝构造
		string(const string& s)
			:_size(s._size)
			, _cacpcity(s._cacpcity)
		{	//开辟一块和s一样的空间
			_str = new char[_cacpcity+1];
			strcpy(_str, s._str);
		}

		//赋值操作符重载
		string& operator=(const string& s)
		{	//如果不是自己给自己赋值
			if (this != &s)
			{		//创建一块新空间
				char* tmp = new char[s._cacpcity+1];
				//拷贝
				strcpy(tmp, s._str);
				//销毁旧空间
				delete[] _str;
				_str = tmp;
				_size = s._size;
				_cacpcity = s._cacpcity;
			}
			return *this;
		}

		//获取长度
		size_t size()
		{	return _size;
		}
		
		//下标访问
		char& operator[](size_t pos)
		{	return _str[pos];
		}
		//只读
		const char& operator[](size_t pos) const
		{	return _str[pos];
		}

		//迭代器开始位置
		iterator begin()
		{	return _str;
		}
		const_iterator begin() const
		{	return _str;
		}

		//迭代器末尾位置
		iterator end()
		{	return _str + _size;
		}

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

		// 扩容
		void AddCacpcity(size_t newCacpcity)
		{		char* str = new char[newCacpcity + 1];
				strcpy(str, _str);
				delete[] _str;
				_str = str;
				_cacpcity = newCacpcity;
		}

		//尾插一个字符
		void push_back(char c)
		{	//检查容量
			if (_size == _cacpcity)
			{		AddCacpcity(_cacpcity == 0 ? 15 : _cacpcity * 2);
			}
			_str[_size] = c;
			_size++;
			_str[_size] = '\0';
		}


		string& operator+=(char c)
		{	push_back(c);
			return *this;
		}

		//追加一个字符串
		void append(const char* str)
		{	if (_cacpcity< (_size)+strlen(str))
			{		AddCacpcity(_size + strlen(str));
			}
			strcpy(_str + _size, str);
			_size += strlen(str);
		}

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

		}

		//+重载
		string operator+(const char* str)
		{	string s(*this);
			s += str;

			return s;
		}

		string operator+(const string& str)
		{	string s(*this);
			s += str._str;

			return s;
		}


		//清空
		void clear()
		{	_str[0] = '\0';
			_size = 0;
		}

		//字符串交换
		void swap(string& s)
		{	std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_cacpcity, s._cacpcity);
		}

		//以c的方式返回字符串
		const char* c_str()const
		{	return _str;
		}

		//判断字符串是否为空
		bool empty()const
		{	return _str[0] == '\0';
		}

		//修改长度
		void resize(size_t n, char c = '\0')
		{	//如果修改的值比当前长度小
			if (n<= _size)
			{		//截断
				_size = n;
				_str[_size] = '\0';
			}
			//如果修改的值比当前长度大
			else
			{		//扩容
				if(n >_cacpcity)
				{AddCacpcity(n);
				}
				//从size位置到n的位置设置为c
				memset(_str + _size, c, n - _size);
				//最后位置填上\0
				_size = n;
				_str[_size] = '\0';
			}
		}

		//指定容量。只增加,减少不处理
		void reserve(size_t n)
		{	if (n >_cacpcity)
			{		AddCacpcity(n);
			}
		}

		//字符串比较函数重载
		bool operator<(const string& s)
		{	return strcmp(_str, s._str)< 0;
		}

		bool operator<=(const string& s)
		{	return (*this< s) || (*this == s);
		}

		bool operator>(const string& s)
		{	return !((*this)<= s);
		}

		bool operator>=(const string& s)
		{	return !(*this< s);
		}

		bool operator==(const string& s)
		{	return strcmp(_str, s._str) == 0;
		}

		bool operator!=(const string& s)
		{	return !(*this == s);
		}

		// 返回c在string中第一次出现的位置
		size_t find(char c, size_t pos = 0) const
		{	for (int i = pos; i< _size; i++)
			{		if (_str[i] == c)
					return i;
			}
			
			return nops;
		}

		// 返回子串s在string中第一次出现的位置
		size_t find(const char* s, size_t pos = 0) const
		{	char* tmp = strstr(_str, s);
			if (tmp == NULL)
				return nops;
			
			return tmp - _str;
		}

		// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		string& insert(size_t pos, char c)
		{	//判断容量
			if (_size == _cacpcity)
			{		AddCacpcity(_cacpcity == 0 ? 15 : _cacpcity * 2);
			}
			//pos位置后往后移
			size_t end = _size + 1;
			while (pos< end)
			{		_str[end] = _str[end - 1];
				end--;
			}
			_str[pos] = c;
			_size++;

			return *this;
		}

		//插入字符串
		string& insert(size_t pos, const char* str)
		{	size_t len = strlen(str);
			//判断容量
			if (_cacpcity< (len + _size))
			{		AddCacpcity(len + _size);
			}
			//移动len格
			size_t end1 = _size+1;
			size_t end2 = _size + len ;
			while (pos< end1 )
			{		_str[end2] = _str[end1-1]  ;
				end1--;
				end2--;
			}
			int i = pos;
			while (*str)
			{		 _str[i++] = *str++;
			}
			_size += len;
			return *this;
		}



		// 删除
		string& erase(size_t pos, size_t len)
		{	//如果要删除的长度大于后面的剩余长度
			if (len >= _size - pos)
			{		len = _size - pos;
				_size -= len;
				_str[pos] = '\0';
				return *this;
			}
			//把后面的往前移,覆盖式删除
			size_t begin = pos+len;
			while (_str[begin])
			{		_str[begin-len] = _str[begin];
				begin++;
			}
			_size -= len;
			return *this;
		}
		

	private:
		char* _str;
		size_t _size;
		size_t _cacpcity;

		static const size_t nops = -1;

	};
}
总结:

string类的细节还有很多,在这里只能简单实现一下。在实现的过程中需要注意的几点。

1.避免内存越界,否则析构时销毁会出错。
2.释放new出来的内存,避免内存泄漏。
3.需要深拷贝,否则会出现析构多次的情况。

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


分享标题:【C++】STL---string类的模拟实现-创新互联
路径分享:http://csdahua.cn/article/hdgge.html
扫二维码与项目经理沟通

我们在微信上24小时期待你的声音

解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流