c++ - string模拟实现


前言

模拟实现c++中string类中的一些常用接口函数接口。

一、string类的成员变量和默认成员函数

1、成员变量

char* _str;	//储存字符串
size_t _capacity;	//容量大小
size_t _size;	//实际字符串大小
const static size_t npos;	//string中默认成员变量,值为 无符号的-1

在类外初始化 npos

const size_t String::npos = -1;

2、默认成员函数

(1)、构造函数
c++库中有多个构造函数,这里只实现最常用的无参构造和用一个字符串构造。

//声明
String(const char* str = "");	//给一个缺省值,当不传参时就可以当作无参构造

//定义
String::String(const char* str)
{
	//初始化
	_size = strlen(str);
	_capacity = _size;
	_str = new char[_size + 1];//多给一个空间储存'\0'

	//将str拷贝到类中的_str中
	strcpy(_str, str);
}

(2)析构函数
我们这里就是对申请的空间进行使用,和对变量置空

//声明
~String();

//定义
String::~String()
{
	//置0
	_size = _capacity = 0;
	//释放并置空
	delete[]_str;
	_str = nullptr;
}

(3)拷贝函数
因为有空间是在堆上申请的,所以这里需要用深拷贝进行拷贝。

//声明
String(const String& s);
//定义
String::String(const String& s): _str(nullptr)
{
	//对长度和容量直接复制
	_size = s._size;
	_capacity = s._capacity;
	
	//先构造一个新类作为临时变量,在对它们的_str的指向进行交换,此时就交换完成了,
	//当出了函数作用域strTmp进销毁了
	String strTmp(s._str);
	std::swap(_str, strTmp._str);
}

(4)赋值运算符重载
和拷贝函数类似,需要使用深拷贝进行赋值。

//声明
String& operator=(String s);

//定义
String& String::operator=(String s)
{
	//自己给自己赋值直接结束即可
	if(s._str == _str)
		return *this;
	//大小和容量进行复制
	_size = s._size;
	_capacity = s._capacity;

	//构造一个临时类
	String strTmp(s._str);
	//指向进行交换
	std::swap(_str, strTmp._str);

	return *this;
}

二、常用的接口

1、size

字符串长度,直接放回类中的_size即可。

//声明
size_t size()const;
//定义
size_t String::size()const
{
	return _size;
}

2、capacity

容量,直接返回类中的_capacity即可。

//声明
size_t capacity()const;

//定义
size_t String::capacity()const
{
	return _capacity;
}

3、c_str

c语言风格的字符串,直接返回类中的_str即可。

//声明
const char* c_str()const
//定义
const char* String::c_str()const
{
	return _str;
}

4、empty

字符串是否为空,通过大小进行判断

//声明
bool empty()const;
//定义
bool String::empty()const
{
	return _size == 0;
}

5、clear

清空,使_size = 0 和改变‘\0’的位置即可。

//声明
void clear();

//定义
void String::clear()
{
	_str[0] = '\0';
	_size = 0;
}

6、reserve

预留空间,需要预留的空间大于实际的空间就进行扩容,否则不做处理(即可放大不能缩小),预留空间不会影响实际字符串的大小即不会影响_size

//声明
void reserve(size_t newCapacity);
//定义
void String::reserve(size_t newCapacity)
{
	//实际空间小于需要预留的空间
	if (_capacity < newCapacity)
	{
		//重新申请一块空间
		char* tmp = new char[newCapacity + 1];
		//将原来的内容拷贝到新空间里
		strcpy(tmp, _str);
		//释放旧空间
		delete[]_str; 

		//重新指向和改变容量
		_str = tmp;
		_capacity = newCapacity;
	}
}

7、resize

改变其大小并用字符c(如果不传默认使用'\0')进行填充,可变大也可以变小(截取),可能会影响_capacity

//声明
void resize(size_t newSize, char c = ‘\0);
//定义
void String::resize(size_t newSize, char c)
{
	if (newSize > _size)
	{
		// 如果newSize大于底层空间大小,则需要重新开辟空间
		if (newSize > _capacity)
		{
			reserve(newSize);
		}

		memset(_str + _size, c, newSize - _size);
	}

	_size = newSize;
	_str[newSize] = '\0';
}

8、insert(字符)

pos位置插入一个字符c

//声明
void insert(size_t pos, char c);

//定义
void String::insert(size_t pos, char c)
{
	//防止pos 大于 _size造成越界
	assert( pos <= _size);

	//没有预留的空间,要进行库容扩容
	if (_size == _capacity)
	{
		size_t new_capacity = _capacity == 0 ? 4 : _capacity * 2;
		reserve(new_capacity);
	}
	
	//将pos位置后面的字符都往后移动一位,包括‘\0’
	int end = _size + 1;

	while (end >= (int)pos)
	{
		_str[end] = _str[end - 1];
		end--;
	}
	//改变大小+将c填入pos位置
	_size++;
	_str[pos] = c;
}

9、insert(字符串)

pos位置插入一个字符串s

//声明
void insert(size_t pos, const char* str);

//定义
void String::insert(size_t pos, const char* str)
{
	//防止pos 大于 _size造成越界
	assert(pos <= _size);

	int n = strlen(str);
	//判断空间是否够用
	if (_size + n > _capacity)
	{
		reserve(_size + n);
	}
	
	//将pos位置后的字符移动n位
	for (int i = _size; i >= (int)pos ; i--)
	{
		_str[i + n] = _str[i];
	}
	
	//在字符串从pos位置开始填入
	for (int i = 0; i < n; i++)
	{
		_str[i + pos] = str[i];
	}
	//改变大小
	_size += n;
}

10、push_back

尾插一个字符c,这里可以直接复用insert

//声明
void push_back(char c);
//定义
void String::push_back(char c)
{
	insert(_size, c);
}

11、append

这里依然复用insert

//声明
void append(const char* str);
//定义
void String::append(const char* str)
{
	insert(_size, str);
}

12、erase

删除从pos(不传参默认为0)位置开始的len(不传参默认为npos)个字符,当len = npos 或者 len >= _size - pos时就是将pos后面的字符全部删除。

//声明
void erase(size_t pos = 0, size_t len = npos);
//定义
void String::erase(size_t pos, size_t len)
{
	//保证删除位置正确
	assert(pos < _size);

	//情况1,将pos位置后面的字符都删除
	if (len == npos || len >= _size - pos)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	//请况2,删除pos位置后的len个字符
	else
	{
		for (size_t i = 0; i <= len; i++)
		{
			_str[i + pos] = _str[pos + len + i];
		}
		_size -= len;
	}
}

13、substr

截取一部分字符,截取从pos(不传参默认为0)位置开始的len(不传参默认为npos)个字符,当len = npos 或者 len >= _size - pos时就是将pos后面的字符全部截取。

//声明
String substr(size_t pos = 0 , size_t len = npos) const;
//定义
String String::substr(size_t pos , size_t len) const
{
	//保证位置正确
	assert(pos < _size);

	String s;
	//假设end为pos + len
	size_t  end = pos + len;

	//如果符合下面条件就要进行修正
	if (len == npos || len >= _size - pos)
	{
		end = _size;
	}

	//截取
	for (size_t i = pos; i < end; i++)
	{
		s.push_back(_str[i]);
	}
	return s;
}

14、find(字符)

查找一个字符,从pos(不传参默认为0)位置开始直接通过遍历查找,找到返回第一次出现下标,找不到返回npos

//声明
size_t find(char c, size_t pos = 0) const;
//定义
size_t String::find(char c, size_t pos) const
{
	for (int i = pos; i < size(); i++)
	{
		if (c == _str[i])
			return i;
	}
	return npos;
}

15、find(字符串)

查找一个字符串,从pos(不传参默认为0)位置开始查找,找到返回第一次出现的下标,找不到返回npos

//声明
size_t find(const char* s, size_t pos = 0) const;
//定义
size_t String::find(const char* s, size_t pos) const
{
	//借助strstr直接查找
	char *p = strstr(_str + pos, s);
	//为NULL找不到
	if (p == NULL)
		return npos;
	//指针 - 指针  = 中间的元素个数 ,所以找到位置 - 开始位置就是下标位置
	return p - _str;
}

16、swap

将两个String进行交换,我们通过交换它们的_size_capacity_str(交换指向),即可完成两个String的交换。

//声明
void swap(String& s);
//定义
void String::swap(String& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

三、重载运算符

1、比较运算符重载

通过写两个重载,然后复用这两个重载完成其他比较函数的重载即可。

//声明
bool operator<(const String& s);
bool operator<=(const String& s);
bool operator>(const String& s);
bool operator>=(const String& s);
bool operator==(const String& s);
bool operator!=(const String& s);

//定义
//重载 < == ,其他的复用
bool String::operator<(const String& s)
{
 	//利用strcmp函数
	return strcmp(_str, s._str) < 0;
}

bool String::operator==(const String& s)
{
	//利用strcmp函数
	return strcmp(_str, s._str) == 0;;
}
//复用
bool String::operator<=(const String& s)
{
	return !(*this > s);
}
bool String::operator>(const String& s)
{
	return !(*this < s || *this == s);
}
bool String::operator>=(const String& s)
{
	return !(*this < s);
}
bool String::operator!=(const String& s)
{
	return !(*this == s);
}

2、+= 运算符重载

可以直接复用尾插的接口完成。

//声明
String& operator+=(char c);
String& operator+=(const char* s);

//定义
String& String::operator+=(char c)
{
	push_back(c);
	return *this;
}

String& String::operator+=(const char* str)
{
	append(str);

	return *this;
}

3、[ ] 运算符重载

在内部直接通过字符串(char*)进行访问即可。

//声明
//可改
char& operator[](size_t index) ;
//const修饰 ,不可改
const char& operator[](size_t index) const;

//定义
char& String::operator[](size_t index)
{
	assert(index < _size);
	
	return _str[index];
}

const char& String::operator[](size_t index)const
{
	assert( index < _size);
	
	return _str[index];
}

4、>> 、<<运算符重载

这两运算符需要作为全局函数进行重载,<< 运算符在内部直接对字符串进行访问即可,>> 运算符 通过一个字符一个字符输入并借助 += 运算符进行插入到尾部,直到遇到空格或者换行结束。

//输出
ostream& operator<<(ostream& _cout, const String& s)
{
	//直接访问字符串_str
	for (int i = 0; i < s.size(); i++)
	{
		cout << s[i];
	}
	return _cout;
}

//输入
istream& operator>>(istream& _cin, String& s)
{
	//先清空原来的字符串
	s.clear();
	
	//需要利用get这个接口,_cin是不能接受空格和换行的,get可以。
	char ch = _cin.get();
	//结束条件
	while (ch != ' ' &&  ch != '\n')
	{
	//借助 += 
		s += ch;
		ch = _cin.get();
	}

	return _cin;
}

四、迭代器

我们要对 char* 重命名为 iteratorconst char* 重命名为const_iterator与库中的迭代器名称保持一致。

//重命名
typedef char* iterator;
typedef const char* const_iterator;
//声明
iterator begin();
iterator end();
const_iterator begin()const;
const_iterator end() const;
//定义 
//返回第一个位置的指针
String::iterator String::begin()
{
	return _str;
}
String::const_iterator String::begin()const
{
	return _str;
}
//返回最后位置的下一个位置的指针
String::iterator String::end()
{
	return _str + _size;
}
String::const_iterator String::end() const
{
	return _str + _size;
}

五、全部代码

String.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cassert>

using namespace std;

namespace xu
{
	class String
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

	public:
		//构造
		String(const char* str = "");

		//析构
		~String();

		//拷贝
		String(const String& s);

		//赋值
		String& operator=(String s);

		//重载 +=
		String& operator+=(char c);
		String& operator+=(const char* s);
		
		//重载 []
		char& operator[](size_t index) ;
		const char& operator[](size_t index) const;

		//重载 比较运算符
		bool operator<(const String& s);
		bool operator<=(const String& s);
		bool operator>(const String& s);
		bool operator>=(const String& s);
		bool operator==(const String& s);
		bool operator!=(const String& s);

		//迭代器接口
		// iterator
		iterator begin();
		iterator end();

		//const_iterator
		const_iterator begin()const;
		const_iterator end() const;

		//插入
		// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		void insert(size_t pos, char c);
		void insert(size_t pos, const char* str);
		//加一个字符
		void push_back(char c);
		//加一个字符串
		void append(const char* str);

		//查找
		// 返回c在string中第一次出现的位置
		size_t find(char c, size_t pos = 0) const;

		// 返回子串s在string中第一次出现的位置
		size_t find(const char* s, size_t pos = 0) const;

		
		//删除
		//清空
		void clear();
		// 删除pos位置上的元素,并返回该元素的下一个位置
		void erase(size_t pos = 0, size_t len = npos);

		//常用其他接口
		//交换
		void swap(String& s);
		//返回c语言风格的字符串
		const char* c_str()const;
		//长度
		size_t size()const;
		//容量
		size_t capacity()const;
		//是否为空
		bool empty()const;
		//预留
		void reserve(size_t newCapacity);
		//预留加修改为c
		void resize(size_t newSize, char c = '\0');
		//截取一段字符
		String substr(size_t pos = 0, size_t len = npos) const;

	private:
		char* _str;
		size_t _capacity;
		size_t _size;
		const static size_t npos;
	};

	ostream& operator<<(ostream& _cout, const String& s);
	istream& operator>>(istream& _cin, String& s);
}

String.cpp

#include"String.h"

namespace xu
{
	const size_t String::npos = -1;
	//输出
	ostream& operator<<(ostream& _cout, const String& s)
	{
		for (int i = 0; i < s.size(); i++)
		{
			cout << s[i];
		}
		return _cout;
	}

	//输入
	istream& operator>>(istream& _cin, String& s)
	{
		s.clear();

		char ch = _cin.get();
		while (ch != ' ' &&  ch != '\n')
		{
			s += ch;
			ch = _cin.get();
		}

		return _cin;
	}

	//构造
	String::String(const char* str)
	{
		//初始化
		_size = strlen(str);
		_capacity = _size;
		_str = new char[_size + 1];//多给一个空间储存'\0'

		//将str拷贝到类中的_str中
		strcpy(_str, str);
	}

	//析构
	String::~String()
	{
		_size = _capacity = 0;
		delete[]_str;
		_str = nullptr;
	}

	//拷贝
	String::String(const String& s): _str(nullptr)
	{
		_size = s._size;
		_capacity = s._capacity;

		String strTmp(s._str);
		std::swap(_str, strTmp._str);
	}

	//赋值
	String& String::operator=(String s)
	{
		//大小和容量进行复制
		_size = s._size;
		_capacity = s._capacity;

		//构造一个临时类
		String strTmp(s._str);
		//指向进行交换
		std::swap(_str, strTmp._str);

		return *this;
	}

	 iterator
	String::iterator String::begin()
	{
		return _str;
	}
	String::iterator String::end()
	{
		return _str + _size;
	}

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

	//加一个字符
	String& String::operator+=(char c)
	{
		push_back(c);
		return *this;
	}

	//压入一个字符
	void String::push_back(char c)
	{
		insert(_size, c);
	}

	//加一个字符串
	String& String::operator+=(const char* str)
	{
		append(str);

		return *this;
	}

	//加一个字符串
	void String::append(const char* str)
	{
		insert(_size, str);
	}

	//是否为空
	bool String::empty()const
	{
		return _size == 0;
	}

	//长度
	size_t String::size()const
	{
		return _size;
	}

	//容量
	size_t String::capacity()const
	{
		return _capacity;
	}

	//返回c语言风格的字符串
	const char* String::c_str()const
	{
		return _str;
	}

	char& String::operator[](size_t index)
	{
		assert(index < _size);

		return _str[index];
	}

	const char& String::operator[](size_t index)const
	{
		assert( index < _size);

		return _str[index];
	}

	//清空
	void String::clear()
	{
		_str[0] = '\0';
		_size = 0;
	}

	//交换
	void String::swap(String& s)
	{
		std::swap(_str, s._str);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}

	//比较重载
	bool String::operator<(const String& s)
	{
		return strcmp(_str, s._str) < 0;
	}

	bool String::operator==(const String& s)
	{

		return strcmp(_str, s._str) == 0;;
	}

	bool String::operator<=(const String& s)
	{
		return !(*this > s);
	}

	bool String::operator>(const String& s)
	{
		return !(*this < s || *this == s);
	}

	bool String::operator>=(const String& s)
	{
		return !(*this < s);
	}

	bool String::operator!=(const String& s)
	{
		return !(*this == s);
	}


	// 返回c在string中第一次出现的位置
	size_t String::find(char c, size_t pos) const
	{

		for (int i = pos; i < size(); i++)
		{
			if (c == _str[i])
				return i;
		}

		return npos;
	}

	// 返回子串s在string中第一次出现的位置
	size_t String::find(const char* s, size_t pos) const
	{
		char *p = strstr(_str + pos, s);
		if (p == NULL)
			return npos;

		return p - _str;
	}


	// 在pos位置上插入字符c/字符串str,并返回该字符的位置
	void String::insert(size_t pos, char c)
	{
		//防止pos 大于 _size造成越界
		assert( pos <= _size);

		//没有预留的空间,要进行库容扩容
		if (_size == _capacity)
		{
			size_t new_capacity = _capacity == 0 ? 4 : _capacity * 2;
			reserve(new_capacity);
		}
		
		//将pos位置后面的字符都往后移动一位,包括‘\0’
		int end = _size + 1;

		while (end >= (int)pos)
		{
			_str[end] = _str[end - 1];
			end--;
		}
		//改变大小+将c填入pos位置
		_size++;
		_str[pos] = c;
	}

	void String::insert(size_t pos, const char* str)
	{
		//防止pos 大于 _size造成越界
		assert(pos <= _size);

		int n = strlen(str);
		//判断空间是否够用
		if (_size + n > _capacity)
		{
			reserve(_size + n);
		}

		//将pos位置后的字符移动n位
		for (int i = _size; i >= (int)pos ; i--)
		{
			_str[i + n] = _str[i];
		}

		//在字符串从pos位置开始填入
		for (int i = 0; i < n; i++)
		{
			_str[i + pos] = str[i];
		}

		//改变大小
		_size += n;

	}

	// 删除pos位置上的元素
	void String::erase(size_t pos, size_t len)
	{
		//保证删除位置正确
		assert(pos < _size);

		//情况1,将pos位置后面的字符都删除
		if (len == npos || len >= _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		//请况2,删除pos位置后的len个字符
		else
		{
			for (size_t i = 0; i <= len; i++)
			{
				_str[i + pos] = _str[pos + len + i];
			}
			_size -= len;
		}
	}

	//预留
	void String::reserve(size_t newCapacity)
	{
		//实际空间小于需要预留的空间
		if (_capacity < newCapacity)
		{
			//重新申请一块空间
			char* tmp = new char[newCapacity + 1];
			//将原来的内容拷贝到新空间里
			strcpy(tmp, _str);
			//释放旧空间
			delete[]_str; 

			//重新指向和改变容量
			_str = tmp;
			_capacity = newCapacity;
		}
	}

	//预留加修改为c
	void String::resize(size_t newSize, char c)
	{
		if (newSize > _size)
		{
			// 如果newSize大于底层空间大小,则需要重新开辟空间
			if (newSize > _capacity)
			{
				reserve(newSize);
			}

			memset(_str + _size, c, newSize - _size);
		}

		_size = newSize;
		_str[newSize] = '\0';
	}

	//截取字符串
	String String::substr(size_t pos , size_t len) const
	{
		//保证位置正确
		assert(pos < _size);

		String s;
		//假设end为pos + len
		size_t  end = pos + len;

		//如果符合下面条件就要进行修正
		if (len == npos || len >= _size - pos)
		{
			end = _size;
		}

		//截取
		for (size_t i = pos; i < end; i++)
		{
			s.push_back(_str[i]);
		}
		return s;
	}
}
  • 28
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值