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;
	}
}
相关推荐
cherub.6 分钟前
深入解析信号量:定义与环形队列生产消费模型剖析
linux·c++
暮色_年华20 分钟前
Modern Effective C++item 9:优先考虑别名声明而非typedef
c++
重生之我是数学王子29 分钟前
QT基础 编码问题 定时器 事件 绘图事件 keyPressEvent QT5.12.3环境 C++实现
开发语言·c++·qt
我们的五年1 小时前
【Linux课程学习】:进程程序替换,execl,execv,execlp,execvp,execve,execle,execvpe函数
linux·c++·学习
做人不要太理性1 小时前
【C++】深入哈希表核心:从改造到封装,解锁 unordered_set 与 unordered_map 的终极奥义!
c++·哈希算法·散列表·unordered_map·unordered_set
程序员-King.1 小时前
2、桥接模式
c++·桥接模式
chnming19872 小时前
STL关联式容器之map
开发语言·c++
程序伍六七2 小时前
day16
开发语言·c++
小陈phd2 小时前
Vscode LinuxC++环境配置
linux·c++·vscode
火山口车神丶2 小时前
某车企ASW面试笔试题
c++·matlab