【C++】string的模拟实现

因为在自己写string类的时候没有涉及到模板,所以就分成两个文件去实现。(由于太多,这里只挑选常用的接口)

string.h
cpp 复制代码
#pragma once
#include<iostream>
#include<string>
#include<assert.h>
using namespace std;

namespace hebre
{
	
	class string
	{
	public:
		using iterator = char*;
		using const_iterator = const char*;
		string();
		string(const char* c);
		string(const string& c);
		//string& operator=(const string& str);
		string& operator=(string str);
		string& operator+=(char ch);
		string& operator+=(const char* c);
		~string();
		void push_back(char ch);
		void append(const char* c);
		void reserve(size_t n);
		void insert(size_t pos, size_t n, char ch);
		void insert(size_t pos, const char* c);
		void erase(size_t pos, size_t len = npos);
		void clear();
		void swap(string& str);
		size_t find(char ch, size_t pos = 0);
		size_t find(const char* c, size_t pos = 0);
		string substr(size_t pos, size_t len = npos);
		char& operator[](size_t i)
		{
			assert(i < _size);
			return _str[i];
		}
		const char& operator[](size_t i) const
		{
			assert(i < _size);
			return _str[i];
		}
		size_t size() const
		{
			return _size;
		}
		size_t capacity() const
		{
			return _capacity;
		}
		const char* c_str() const
		{
			return _str;
		}
		iterator begin()
		{
			return _str;
		}
		const_iterator begin() const
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		const_iterator end() const
		{
			return _str + _size;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	public:
		static size_t npos;
	};
	void swap(string& s1, string& s2);
	bool operator== (const string& lhs, const string& rhs);
	bool operator!= (const string& lhs, const string& rhs);
	bool operator> (const string& lhs, const string& rhs);
	bool operator< (const string& lhs, const string& rhs);
	bool operator>= (const string& lhs, const string& rhs);
	bool operator<= (const string& lhs, const string& rhs);

	ostream& operator<<(ostream& os, const string& str);
	istream& operator>>(istream& is, string& str);
	istream& getline(istream& is, string& str, char delim = '\n');
}
string.cpp
cpp 复制代码
#include"string.h"
namespace hebre
{
	//要在.cpp文件里定义静态成员变量
	//如果在.h文件里定义的话,npos在string.cpp和test.cpp文件在生成可执行文件都会重新再次定义一遍
	size_t string:: npos = -1;
	string::string()
		:_str(new char[1] {'\0'})
		, _size(0)
		, _capacity(0)
	{}
	string::string(const char* c)
		:_size(strlen(c))
	{
		_str = new char[_size + 1];
		_capacity = _size;
		strcpy(_str, c);
	}
	//传统写法
	//string::string(const string& c)
	//	:_size(c._size)
	//{
	//	_str = new char[_size + 1];
	//	_capacity = c._capacity;
	//	strcpy(_str, c._str);
	//}
	string::string(const string& c)
	{
		string tmp(c._str);
		swap(tmp);
	}
	//s1=s2
	//传统写法
	//string& string::operator=(const string& str)
	//{
	//	if (this != &str)//避免自己赋值给自己
	//	{
	//		delete[]_str;
	//		_str = new char[(str._capacity + 1)];
	//		strcpy(_str, str._str);
	//		_size = str._size;
	//		_capacity = str._capacity;
	//	}
	//	return *this;
	//}
	string& string::operator=(string str)
	{
		swap(str);
		return *this;
	}
	string& string::operator+=(char ch)
	{
		push_back(ch);
		return *this;
	}
	string& string::operator+=(const char* c)
	{
		append(c);
		return *this;
	}
	void string::insert(size_t pos, size_t n, char ch)//n代表ch的个数
	{
		assert(pos <= _size);
		if (_capacity < _size + n)
		{
			size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
			if (newcapacity < _size + n)
			{
				newcapacity = _size + n + 1;
			}
			reserve(newcapacity);
		}
		size_t end = _size+n;
		while (end>=pos+n)
		{
			_str[end] = _str[end - n];
			end--;
		}
		size_t num = n, i = pos;
		while (num)
		{
			_str[i] = ch;
			i++;
			num--;
		}
		_size += n;
	}
	void string::insert(size_t pos, const char* c)
	{
		assert(pos <= _size);
		size_t n = strlen(c);
		if (_capacity < _size + n)
		{
			size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
			if (newcapacity < _size + n)
			{
				newcapacity = _size + n + 1;
			}
			reserve(newcapacity);
		}
		size_t end = _size + n;
		while (end >= pos + n)
		{
			_str[end] = _str[end - n];
			end--;
		}
		for (int i = 0; i < n; i++)
		{
			_str[pos+i] = c[i];
		}
		_size += n;
	}
	void string::erase(size_t pos, size_t len)
	{
		assert(pos <= _size);
		if (len >= _size - pos)
		{
			_str[pos] = '\0';
		}
		else
		{
			size_t cur = pos + len;
			while (cur <= _size)
			{
				_str[cur - len] = _str[cur];
				cur++;
			}
		}
	}
	void string::clear()
	{
		_str[0] = '\0';
		_size = 0;
	}
	size_t string::find(char ch, size_t pos)
	{
		assert(pos < _size);

		size_t cur = pos;
		while (cur < _size)
		{
			if (_str[cur] == ch)
			{
				return cur;
			}
			cur++;
		}
		return npos;
	}
	size_t string::find(const char* c, size_t pos)
	{
		assert(pos < _size);
		size_t cur = pos;
		size_t len = strlen(c);
		char* check = strstr(_str, c);//检查是否含有子串的函数
		if (check != nullptr)
		{
			return _str - check;
		}
		return npos;
	}
	string string::substr(size_t pos, size_t len)
	{
		assert(pos < _size);
		if (len > _size - pos)
		{
			len = _size - pos;
		}
		hebre::string sub;
		sub.reserve(len);
		for(int i = 0; i < len; i++)
		{
			sub += _str[pos + i];
		}
		return sub;
	}
	string::~string()
	{
		delete[] _str;
		_str = nullptr;
		_size = 0;
		_capacity = 0;
	}
	void string::push_back(char ch)
	{
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : 2 * _capacity);
		}
		_str[_size] = ch;
		_str[_size + 1] = '\0';
		_size++;
	}
	void string::append(const char* c)
	{
		if ((_size + strlen(c) > _capacity))
		{
			size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
			if (_size + strlen(c) > newcapacity)
			{
				newcapacity = _size + strlen(c) + 1;
			}
			reserve(newcapacity);
		}
		strcpy(_str + _size, c);
		_str[_size + strlen(c)] = '\0';
		_size += strlen(c);
	}

	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[]_str;
			_str = tmp;
			_capacity = n;
		}
	}
	//在std中的swap若交换的是自定义类型,会经历很多的拷贝和析构,效率较低
	//我们自己实现的swap直接将其地址交换就可以取代很多不必要的操作
	void string::swap(string& str)
	{
		std::swap(_str, str._str);
		std::swap(_size, str._size);
		std::swap(_capacity, str._capacity);
	}

	/
	//直接复用hebre::string类中的swap
	void swap(string& s1, string& s2)
	{
		s1.swap(s2);
	}

	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);
	}
	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;
	}
	ostream& operator<<(ostream& os, const string& str)
	{
		hebre::string::const_iterator it = str.begin();
		while (it != str.end())
		{
			os << *it;
			it++;
		}
		return os;
	}
	istream& operator>>(istream& is, string& str)
	{
		str.clear();
		char ch;
		//这里我们不可以直接使用>>去获取字符
		//无论在cin还是scanf当中,默认换行和空格就是多个字符的分割
		//所以遇到换行和空格就会自动刷新缓存区
		//在这里的in>>,它会自动忽略掉换行和空格,所以就获取不到换行和空格
		//is >> ch;
		//get()同样也是istream库里的一个函数,它可以获取所有字符
		char buff[256];
		//使用buff减少扩容
		//同时,由于栈上开空间是在调用main函数的时候直接计算好的
		//这就要比在堆上面申请空间是要快上很多
		int i = 0;
		ch = is.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 255)
			{
				buff[255] = '\0';
				str += buff;
				i = 0;
			}
			ch = is.get();
		}
		if (i != 0)
		{
			buff[i] = '\0';
			str += buff;
		}
		return is;
	}
	istream& getline(istream& is, string& str, char delim)
	{
		str.clear();
		char ch;
		char buff[256];//使用buff减少扩容
		int i = 0;
		ch = is.get();
		while (ch != delim)
		{
			buff[i++] = ch;
			if (i == 255)
			{
				buff[255] = '\0';
				str += buff;
				i = 0;
			}
			ch = is.get();
		}
		if (i != 0)
		{
			buff[i] = '\0';
			str += buff;
		}
		return is;
	}
}
相关推荐
LaoZhangGong12320 分钟前
使用常数指针作为函数参数
c++·经验分享
边疆.20 分钟前
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++