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;
	}
}
相关推荐
LaoZhangGong12320 分钟前
使用常数指针作为函数参数
c++·经验分享
边疆.21 分钟前
C++类和对象 (中)
c语言·开发语言·c++·算法
随便取个六字2 小时前
C++学习:类和对象(二)
c++·学习
jimmy.hua3 小时前
C++刷怪笼(9)继承
开发语言·c++
OMGmyhair3 小时前
【 C++ 】C++11的初步学习
开发语言·c++·学习
秋说4 小时前
【数据结构 | PTA】懂蛇语
数据结构·c++
凯子坚持 c5 小时前
String的长度有限,而我对你的思念却无限延伸
c++
何曾参静谧5 小时前
「C/C++」C++20 之 #include<ranges> 范围
c语言·c++·c++20
挨代码6 小时前
C++ —— 常见的初始化
c++
酒鬼猿6 小时前
C++初阶(七)--类和对象(4)
开发语言·c++