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

C++之 String 类的模拟实现

本文只简述string类模拟实现的重点,其余不再过多赘述

一、模拟实现string类的构造函数

本文主要实现下图两个构造函数,即string()和string(const string& str)

而关于string的底层,其实就是数组,在物理逻辑上是连续的空间:

//string.h文件
namespace mxj
{
	class string
	{
	public:
		//不带参的构造函数,编译器默认生成的已经满足使用需求
		//无参的构造,就是字符串'\0',所以在string的模拟实现里,带参的构造函数包含了无参构造函数
		//string();
		
		//带参构造函数
		string(const char* str = "");//给缺省值"",
        //这样我们的mxj::string s1就不会报错,不然得写成mxj::string s1()

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

 带参构造函数如下,以strlen为基础,计算开辟新空间的大小,通过开辟新的内存空间并将str字符串长度的下一个位置赋值'\0'

这样也很巧妙的解决了无参构造函数。

//string.cpp文件
namespace mxj
{
		string::string(const char* str)
		:_str(new char[strlen(str) + 1])//这里为什么要加1?因为strlen不会计算\0
		, _size(strlen(str))
		, _capacity(strlen(str))
	{
		strcpy(_str, str);
	}
}

二、模拟实现string类的拷贝构造和赋值拷贝

拷贝构造和赋值拷贝可以通过交换函数来实现,拷贝构造和赋值拷贝都是深拷贝!!!

/ s2(s1),拷贝构造
	string::string(const string& s)
	{
		_str = new char[s._capacity + 1];
		strcpy(_str, s._str);
		_size = s._size;
		_capacity = s._capacity;
	}
	// s3=s2=s1=s,赋值拷贝,
	string& string::operator=(const string& s)
	{
		if (this != &s)
		{
			//先把原来的空间释放了,如果s1=s,s1原本的空间非常大,s的空间非常小,就非常容易造成空间浪费
			delete[] _str;
			//为什么要+1,因为还有\0,_size和_capacity都是不计算\0的
			_str = new char[s._capacity + 1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}

		return *this;
	}

当然,上面有关拷贝构造和赋值拷贝虽然直观,但是很啰嗦 ,通过swap函数我们也能实现

两者均不改变当前对象的资源,都是通过临时对象进行资源的交换

//string.cpp
namespace mxj
{
    string::string(const string& s)
		{
			string tmp(s._str);//将s中的资源构造对象tmp
			swap(tmp);
		}

		string& string::operator=(string s)
		{
			swap(s);//临时对象s和*this进行交换资源
			return *this;
		}

		void swap(string& s1, string& s2)
		{
			s1.swap(s2);//通过库函数中的swap来实现
		}

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

三、模拟实现string类的析构函数

析构函数需要清理类对象的资源

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

 四、赋值运算符重载[ ]

赋值运算符重载[ ],使得string类有通过下标来访问字符串数据的功能

char& operator[](size_t i)
		{
			return _str[i];
		}

五、获取字符串数据

string类底层是数组,接口直接返回字符串即可

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

六、获取当前对象元素个数

size_t size() const
		{
			return _size;
		}

 七、清理当前string类对象的数据置空

字符串的终止符号为'\0',所以将字符串第一个元素置为字符'\0',字符串有效个数置零即可

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

八、普通迭代器

迭代器是指针或者是像指针一样的东西,但在string类中,迭代器的底层是通过指针实现的

using iterator = char*;//using类似于typedef的作用
iterator begin()
{
	return _str;
}

iterator end()
{
	return _str + _size;
}



九、const修饰的迭代器 

using const_iterator = const char*;
const_iterator begin()const
{
	return _str;
}

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

十、string类一些常用接口的实现

    void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];//保留n个位置实际上要保留n+1个,要给'\0'留位置
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;//_capacity是不计算'\0'的
		}
	}


	void string::push_back(char ch)
	{
		if (_size == _capacity) {
			reserve(_capacity = 0 ? 4 : 2 * _capacity);
		}
		_str[_size] = ch;
		_size++;
	}

	void string::append(const char* str)
	{
		size_t len = strlen(str);
		if (len + _size > _capacity)
		{
			size_t newcapacity = 2 * _capacity;
			//如果扩了两倍还是不满足,那就按串的大小来扩
			if (len + _size >newcapacity)
			{
				newcapacity = len + _size;
			}
			reserve(newcapacity);
		}
		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)
	{
		//判断pos位置是否合法
		assert(pos <= _size);
		//判断是否需要扩容
		if (_size == _capacity) {
			reserve(_capacity == 0 ? 4 : 2 * _capacity);
		}
		//pos+1位置往后挪
		size_t end = _size;
		while (end > pos) {
			_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) {
			size_t newCapacity = 2 * _capacity;
			if (newCapacity < _size + len)
			{
				newCapacity = _size + len;
			}
			reserve(newCapacity);
		}

		//数据往后挪:str的长度为len,pos位置每一个字符都需要向后挪len个,
		size_t end = _size+len;
		while (end > pos + len-1) {
			_str[end] = _str[end-len];
			--end;
		}

		//插入
		for (size_t i = 0; i < pos; 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 {
			size_t end = pos + len;
			while (end > pos) {
				_str[end-len] = _str[end];
				++end;
			}
			_size -= len;
		}
	}

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

	size_t string::find(const char* str, size_t pos)
	{
		assert(pos < _size);
		const char* ptr = strstr(pos+_str,str);
		if (ptr == nullptr)
		{
			return npos;
		}
		else
		{
			return ptr - _str;
		}
	}

	string string::substr(size_t pos, size_t len)
	{
		assert(pos < _size);
		if (len > (_size - pos)) {
			len = _size - pos;
		}
		mxj::string sub;
		sub.reserve(len);
		for (size_t i = pos; i < len; i++)
		{
			sub += _str[pos + i];//尾插
		}
		return sub;
	}

	//通过复用
	bool operator== (const string& lhs, const string& rhs)
	{
		return strcmp(lhs.c_str(), rhs.c_str()) == 0;
	}
	bool operator!= (const string& lhs, const string& rhs)
	{
		return !(lhs == rhs);
	}
	bool operator> (const string& lhs, const string& rhs)
	{
		return strcmp(lhs.c_str(), rhs.c_str()) > 0;
	}
	bool operator< (const string& lhs, const string& rhs)
	{
		return !(lhs >=rhs);
	}
	bool operator>= (const string& lhs, const string& rhs)
	{
		return (lhs > rhs)||(lhs==rhs);
	}
	bool operator<= (const string& lhs, const string& rhs)
	{
		return !(lhs > rhs);
	}

	ostream& operator<<(ostream& os, const string& str)
	{
		for (size_t i = 0; i < str.size(); i++)
		{
			os << str[i];
		}
		return os;
	}

	istream& operator>>(istream& is, string& str)
	{
		str.clear();
		char ch;
		//is >> ch;
		ch = is.get();
		while (ch != ' ' && ch != '\n')
			//如果想实现getline的效果while( ch != '\n')
		{
			str += ch;
			ch = is.get();
		}

		return is;
	}


http://www.kler.cn/a/420078.html

相关文章:

  • JavaScript(JS)的对象
  • 039集——渐变色之:CAD中画彩虹()(CAD—C#二次开发入门)
  • Ai编程cursor + sealos + devBox实现登录以及用户管理增删改查(十三)
  • 08-模块与包
  • 电脑关机的趣味小游戏——system函数、strcmp函数、goto语句的使用
  • 【golang】单元测试,以及出现undefined时的解决方案
  • k8s api对象,CRD
  • linux 操作系统环境配置 redhat9
  • 如何利用微型5G网关为智慧无人矿车提供精确定位
  • pytest(一)csv数据驱动
  • AI开发 - GPT之魂 用Python 演示chatGPT的自注意力机制 - 机器学习
  • JavaScript根据数据生成柱形图
  • 大数据Hadoop实战:从基础到应用
  • STM407IGT6+WS2818灯带
  • ubuntu部署RocketMQ
  • Kubernetes 集群网络:Flannel 与 Calico 的区别
  • AD7606使用方法
  • 学习记录:js算法(一百零七):解码方法
  • 求自然数N的所有约数之和。:JAVA
  • LWIP和FATFS 实现 FTP 服务端
  • EasyMedia播放rtsprtmp视频流(flvhls)
  • 事务常见分类
  • 软件质量保证——单元测试之白盒技术
  • hdlbits系列verilog解答(Dff16e-同步复位上升沿16位触发器)-85
  • Python中字符串和正则表达式
  • python 练习题