当前位置: 首页 > article >正文

string类的模拟实现

首先将声明的函数放到.h文件中;一般短小频繁的函数直接放到类里面;

函数定义和main函数放到.c文件中;

声明函数:

#pragma once
#include<iostream>
#include<string>
#include<assert.h>
using namespace std;

namespace fpj
{
	class string
	{
	public:
		
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		const iterator begin() const
		{
			return _str;
		}
		const iterator end() const
		{
			return _str + _size;
		}
		//短小频繁的函数直接放到类里面,默认是inline
		/*string()//构造函数  无参
			//:_str(nullptr)//空指针无法解引用
			:_str(new char[1]{'\0'})
			,_size(0)
			,_capacity(0)
		{}*/
		//string(const char* str)//有参
		string(const char* str=" ")//将构造函数有参和无参合并
		{
			_size=strlen(str);
			//capacity不包含\0
			_capacity = _size;
			_str = new char[_capacity + 1];//多开一个空间
			strcpy(_str, str);
		}
		void clear()//清理
		{
			_str[0] = '\0';
			_size = 0;
		}
		const char* c_str() const
		{
			return _str;
		}
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}
		size_t size() const
		{
			return _size;
		}
		size_t capacity() const
		{
			return _capacity;
		}
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		const char& operator[](size_t pos)  const//不能修改
		{
			assert(pos < _size);
			return _str[pos];
		}
		//交换
		void swap(string& s) //s1(this),s2(s)
		{
			std::swap(_str, s._str); //swap使用的是string中的交换函数
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		//深拷贝  s2(s1)
		string(const string& s)
		{
			/*_str = new char[s._capacity + 1];//传统方法
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;*/

			//现代写法
			string tmp(s._str);//构造
			swap(tmp);
		}

		//s2=s1;
		//string& operator=(const string& s)//传统写法`
		//{
		//	//if(this!=&s)//防止自己给自己赋值
		//	//{
		//	//	delete[] _str;
		//	//	_str = new char[s._capacity + 1];
		//	//	strcpy(_str, s._str);
		//	//	_size = s._size;
		//	//	_capacity = s._capacity;
		//	//	return *this;
		//	//}
		//	if (this != &s)//现代写法
		//	{
		//		string tmp(s._str);//拷贝构造tmp对象,s2想要的,  s._str是s1
		//		swap(tmp);
		//	}
		//	return *this;
		//}
		string& operator=(string tmp)//赋值现代写法,一行代码搞定
		{
			swap(tmp);
			return *this;
		}
		void reserve(size_t n);
		void push_back(char ch);
		void append(const char* str);
		string& operator+=(char ch);
		string& operator+=(const char* str);
		void insert(size_t pos, char ch);
		void insert(size_t pos, const char* str);
		void erase(size_t pos, size_t len = npos);

		size_t find(char ch, size_t pos = 0);
		size_t find(const char* str, size_t pos = 0);
		string substr(size_t pos = 0, size_t len = npos);
	private:
		char* _str=nullptr;
		size_t _size=0;
		size_t _capacity=0;
		static const size_t npos;
	};
	void test1();
	void test2();
	void test3();
	bool operator<(const string& s1, const string& s2);
	bool operator<=(const string& s1, const string& s2);
	bool operator>(const string& s1, const string& s2);
	bool operator>=(const string& s1, const string& s2);
	bool operator==(const string& s1, const string& s2);
	bool operator!=(const string& s1, const string& s2);

	ostream& operator<<(ostream& out, const string& s);
	istream& operator>>(istream& in, string& s);
}

浅拷贝:编译器只是将对象中的值拷贝过来。还是共用一块空间,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该 资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。

深拷贝:给每个对象独立分配资源,保证多个对象之间不会因共享资源而造成多次释放造成程序奔溃问题.

深拷贝的传统写法和现代写法:

string(const string& s)
		{
			_str = new char[s._capacity + 1];//传统方法
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}

string (const string&s)//现代写法
{
    string tmp(s._str)//构造一个tmp和s._str拥有相同的空间,大小
    swap(tmp);//将s._str和tmp的值进行交换
}

赋值的传统写法和现代写法

string& operator=(const string& s)//传统写法`
{
	if(this!=&s)//防止自己给自己赋值
	{
		delete[] _str;
		_str = new char[s._capacity + 1];
		strcpy(_str, s._str);
		_size = s._size;
		_capacity = s._capacity;
		return *this;
	}
	return *this;
}

string& operator=(const string& s)//现代写法
{
    if(this!=&s)
{
    string tmp(s._str);
    swap(tmp);//调用swap函数,这个函数会交换当前对象的内部状态和tmp对象的状态。通过这种方式,当前对象获得了tmp的内容,而tmp在作用域结束时会被自动销毁,从而释放原有的资源。
}
    return *this;
}

函数的实现:

#include"string.h"

namespace fpj
{
	const size_t string::npos = -1;
	void string::reserve(size_t n)//扩容
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}
	void string::push_back(char ch)//ch是字符
	{
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : 2 * _capacity);
		}
		_str[_size] = ch;
		_size++;
		_str[_size] = '\0';//否则会出现乱码
	}
	void string::append(const char* str)//str是指针数组   不能2倍扩容,根据实际需要扩容
	{
		size_t len = strlen(str);//需要的长度
		if (_size + len > _capacity)
		{
			// 大于2倍,需要多少开多少,小于2倍按2倍扩
			reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
		}

		strcpy(_str + _size, str);
		_size += len;
	}
	string& string::operator+=(char ch)
	{
		push_back(ch);
		return *this;
	}
	string& string::operator+=(const char* str)
	{
		append(str);
		return *this;
	}
	void string::insert(size_t pos, char ch)//插入字符
	{
		assert(pos <= _size);
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : 2 * _capacity);
		}
		//挪动数据
		//int  end = _size;//当pos=1时,end = -1,size_t是无符号的,-1相当于是127,所以不会跳出循环,size_t要变为int,
		//while ((int)pos <= end)//此时pos也要强转
		//{
		//	_str[end+1] = _str[end];
		//	--end;
		//}
		//_str[pos] = ch;
		//_size++;
		
		//挪动数据
		size_t end = _size+1;//end指向\0
		while (pos <end)
		{
			_str[end] = _str[end-1];
			--end;
		}
		_str[pos] = ch;
		_size++;
	}
	void string::insert(size_t pos, const char* str)//插入字符串
	{
		size_t len = strlen(str);//需要的长度
		if (_size + len > _capacity)
		{
			// 大于2倍,需要多少开多少,小于2倍按2倍扩
			reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
		}
		size_t end = _size + len;
		while (end > pos + len-1)
		{
			_str[end] = _str[end - len];
			--end;
		}
		for (size_t i = 0; i < len; i++)
		{
			_str[pos + i] = str[i];
		}
		_size += len;
	}
	void string::erase(size_t pos, size_t len )
	{
		assert(pos < _size);
		if (len >= _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			for (size_t i = pos + len; i <= _size; i++)
			{
				_str[i - len] = _str[i];
			}
			_size -= len;
		}
	}
	size_t string::find(char ch, size_t pos )//pos初始化为0
	{
		assert(pos < _size);
		for (size_t i = pos; i < _size; ++i)
		{
			if (_str[pos] == ch)
			{
				return i;
			}
		}
		return npos;
	}
	size_t string::find(const char* str, size_t pos)
	{
		assert(pos < _size);
		const char* ptr = strstr(_str + pos, str);//返回匹配成功的第一个位置
		if (ptr == nullptr)//没有匹配成功
		{
			return npos;
		}
		else
		{
			return ptr - _str;//尾指针-首指针是pos位置
		}
	}
	string string::substr(size_t pos , size_t len)//获取子串
	{
		assert(pos < _size);
		if (len > _size - pos)
		{
			len = _size - pos;
		}
		string sub;
		sub.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			sub += _str[pos + i];
		}
		return sub;//此时需要深拷贝,默认是浅拷贝  产生临时对象
	}
	bool operator<(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str())< 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);
	}

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

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

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

	ostream& operator<<(ostream& out, const string& s)//写,ostream& out前面不加const
	{
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}
	istream& operator>>(istream& in, string& s)
	{
		const int N = 256;
		char buffle[N];
		int i = 0;

		char ch;
		ch = in.get();//cin scanf 输入提取默认空格\换行都是分隔符,所以加上这一行(in提取不到空格和字符)
		while (ch != ' ' && ch != '\n')
		{
			buffle[i++] = ch;
			if (i == N - 1)
			{
				buffle[i] = '\0';
				s += buffle;
				i = 0;
			}
		}
		if (i > 0)
		{
			buffle[i] = '\0';
			s += buffle;
		}
		return in;
	}
}


http://www.kler.cn/news/358828.html

相关文章:

  • 软件压力测试如何进行?深圳软件测试机构分享
  • VsCode 如何自定义代码片段(Code Snippet)
  • 政府信息化项目验收检测有哪些注意事项?
  • Git-本地项目同步到远程仓库
  • Python游戏编程入门注-第2章2
  • Anzo Capital 昂首资本带你解读货币相关性
  • Spring MessageSource国际化原理
  • 自动化运维:提升效率与降低风险的关键
  • 【MySQL】提高篇—索引与性能优化:如何创建与管理索引
  • HTML5 语义元素
  • Flink系统架构和应用部署方式
  • 云函数+对象存储全包!MemFire Cloud让开发如此轻松
  • 功能强大且简单易用的实时算法视频监控,智慧快消开源了。
  • 数据挖掘示例
  • 前端UI框架
  • 为什么SSH协议是安全的?
  • 深入理解 SQL 中的高级数据处理特性:约束、索引和触发器
  • 阿里云验证码短信发送服务搭建(flask)
  • 鸿蒙NEXT开发声明式UI是咋回事?
  • 通达信“钻石顶底,多空涨跌即时提示公式!无限制