【C++】string类的模拟实现

文章目录

一、string类的构造,拷贝构造,赋值重载以及析构

1.构造函数

2.拷贝构造

3.swap问题

4.赋值重载

5.析构函数

二.常用接口

1.c_str

2.operator[]

3.迭代器和范围for

4.size

三.插入

四.删除

五.查找

六.运算符重载

七.参考代码


string类的模拟实现我们定义三个成员变量

	class string
	{
	public:
		//...
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};

下面对其中的成员函数进行依次的实现

一、string类的构造,拷贝构造,赋值重载以及析构

1.构造函数

构造函数分为无参带参这两种

无参构造函数默认构造空字符串"",所以我们只需要写一个带参的并给一个缺省值即可

string::string(const char* str)
	:_size(strlen(str))
{
	_str = new char[_size + 1];
	_capacity = _size;
	strcpy(_str, str);
}

注意:这里的_str开空间时要加1,是给\0预留位置

2.拷贝构造

对于string类型来说,如果不写拷贝构造会导致浅拷贝问题(只完成值拷贝)

所以我们需要进行深拷贝

这里先介绍一种传统写法,也是比较简单的写法

string::string(const string& s)
{
	_str = new char[s._capacity + 1];
	strcpy(_str, s._str);
	_size = s._size;
	_capacity = s._capacity;
}
	

3.swap问题

在之前学习string的使用时,我们会发现明明std中就有swap,为什么string还要再实现一个?

这是因为第一个swap的交换代价比较大,它会进行深拷贝,造成空间损耗,所以我们在自己实现swap时要考虑到这一点,可以采用交换两者指针的方式:

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

4.赋值重载

默认生成的赋值重载也会导致浅拷贝,所以我们需要实现深拷贝

这里我们也用传统写法,也是比较简单的写法

	string& string::operator=(const string& s)
	{
		
		char* tmp = new char[s._capacity + 1];
		strcpy(tmp, s._str);
		delete[] _str;
		_str = tmp;
		_size = s._size;
		_capacity = s._capacity;
		
		return *this;
	}

其实还有一种现代写法,是复用上面的拷贝构造,感兴趣的小伙伴可以自行先去了解哦

5.析构函数

析构函数比较简单,请看下面的代码:

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

下面我们来看几个常用的接口实现

二.常用接口

1.c_str

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

2.operator[]

char& string::operator[](size_t pos)//普通对象:可读可写
{
	assert(pos < _size);
	return _str[pos];
}

3.迭代器和范围for

迭代器

typedef char* iterator;
string::iterator string::begin()
{
	return _str;
}
string::iterator string::end()
{
	return _str + _size;
}

//遍历
string::iterator it1 = s1.begin();
while (it1 != s1.end())
{
	cout << *it1 << " ";
	++it1;
}
cout << endl;

范围for(底层还是迭代器)

typedef char* iterator;
string::iterator string::begin()
{
	return _str;
}
string::iterator string::end()
{
	return _str + _size;
}

//遍历
for (auto e: s1)
{
	cout << e << " ";
}
cout << endl;

打印结果如下:

4.size

size_t size() const
{
    return _size;
}

三.插入

reserve

开辟新的空间,然后进行拷贝,对旧空间进行释放


	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;

			_str = tmp;
			_capacity = n;
		}
	}

push_back

尾插一个字符,我们需要考虑扩容问题

同时,尾插之后’\0’要重新处理

void string::push_back(char ch)
{
	if (_size == _capacity)
	{
		size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
		reserve(newcapacity);
	}

	 _str[_size] = ch;
	 _str[_size + 1] = '\0';
	 ++_size;

}

append

尾插字符串,这里我们需要计算,然后决定开多少空间(直接开2倍可能不够用)

void string::append(const char* str)
{
	size_t len = strlen(str);
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}

	strcpy(_str+_size, str);
	_size += len;

}

+=

这个比较简单,我们可以复用上面的分为+=字符和+=字符串push_back和append

	string& string::operator+=(char ch)//字符
	{
		push_back(ch);
	
		return *this;
	}
	
	string& string::operator+=(const char* str)//字符串
	{
		append(str);
	
		return *this;
	}

insert

insert的代码,我们要注意画图,尤其是关于边界的一些条件的处理,我们要小心

	void string::insert(size_t pos, char ch)//处理字符
	{
		assert(pos <= _size);
		if (_size == _capacity)
		{
			size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
			reserve(newcapacity);
		}

		size_t end = _size + 1;
		while (end > pos)//这里的边界要控制清楚!
		{
			_str[end] = _str[end - 1];//右移
			--end;
		}

		_str[pos] = ch;
		++_size;
	}

	void string::insert(size_t pos, const char* str)//处理字符串
	{
		assert(pos <= _size);

		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			reserve(_size + len);
		}

		size_t end = _size + len;
		while (end > pos + len - 1)//这里的边界要控制清楚!
		{
			_str[end] = _str[end - len];//右移
			--end;
		}

		memcpy(_str + pos, str, len);
		_size += len;
	}

四.删除

说到erase,自然要跟npos联系起来,npos是string类的静态成员变量

我们可以采用定义和声明分离的方法:

const static size_t npos;//写在string.h中

const size_t string::npos = -1;//写在string.cpp中

下面来实现这个删除功能:

	void string::erase(size_t pos, size_t len)
	{
		assert(pos < _size);

		// len大于前面字符个数时,有多少删多少
		if (len >= _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}

		else
		{
			strcpy(_str + pos, _str + pos + len);
			_size -= len;
		}

	}

五.查找

从pos处开始查找字符或者字符串,找到返回下标值,没找到则返回npos

	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* sub, size_t pos)//字符串
	{
		char* p = strstr(_str + pos, sub);//调用现成的strstr来实现
		return  p - _str;
	}

六.流插入<<和流提取>>

流插入(输出)和流提取(输入)我们之前在日期类的时候也实现过了,我们知道它不适合写成 成员函数,之前我们用的是友元,这样才能访问私有,但这里我们可以不访问私有,挨个挨个遍历,所以可以这么写:

流插入<<

	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)//流提取(输入)
{
	char ch = is.get();
	while (ch != ' ' && ch != '\n')
	{
		str += ch;
		ch = is.get();
	}

	return is;
}

但是这样还不够,我们会发现之前的数据它还留着:

所以我们需要再写个clear()来清理数据:

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

 istream& operator>> (istream& is, string& str)//流提取(输入)
	{
		str.clear();
		char ch = is.get();
		while (ch != ' ' && ch != '\n')
		{
           str += ch;
           ch = is.get();
		}

		return is;
	}

七.参考代码

这里模拟实现string类用了 string.h , string.cpp 和  test.cpp 三个文件来实现

这里给出string的各个函数定义和实现的代码

string.h

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include <iostream>
#include <assert.h>
#include <string>
using namespace std;

namespace bit
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		iterator begin();
		iterator end();

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

		string(const char* str = "");
		~string();
		const char* c_str() const;

		size_t size() const;
		char& operator[](size_t pos);
		const char& operator[](size_t pos) const;

		void push_back(char ch);
		void append(const char* str);

	
        void insert(size_t pos, char ch);
		void insert(size_t pos, const char* str);
		void reserve(size_t n);
		string& operator+=(char ch);
	    string& operator+=(const char* str);
	    void erase(size_t pos = 0, size_t len = npos);

		string(const string& s);
		string& operator=(const string& s);
 
		size_t find(char ch, size_t pos = 0);
		size_t find(const char* sub, size_t pos = 0);

		void swap(string& s);
		string substr(size_t pos = 0, size_t len = npos);

		bool operator<(const string& s) const;
		bool operator>(const string& s) const;
		bool operator<=(const string& s) const;
		bool operator>=(const string& s) const;
		bool operator==(const string& s) const;
		bool operator!=(const string& s) const;
		void clear();


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

	istream& operator>> (istream& is, string& str);
    ostream& operator<< (ostream& os, const string& str);

}

string.cpp

#include "string.h"

namespace bit
{
	const size_t string::npos = -1;

	string::iterator string::begin()
	{
		return _str;
	}

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

	string::const_iterator string::begin() const
	{
		return _str;
	}

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


	string::string(const char* str)
		:_size(strlen(str))
	{
		_str = new char[_size + 1];
		_capacity = _size;
		strcpy(_str, str);
	}

	string::string(const string& s)
	{
		_str = new char[s._capacity + 1];
		strcpy(_str, s._str);
		_size = s._size;
		_capacity = s._capacity;
	}
	

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

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

	size_t  string::size() const
	{
		return _size;
	}

	string& string::operator=(const string& s)
	{
		if (this != &s)
		{
			char* tmp = new char[s._capacity + 1];
			strcpy(tmp, s._str);
			delete[] _str;
			_str = tmp;
			_size = s._size;
			_capacity = s._capacity;
		}

		return *this;
	}

	char& string::operator[](size_t pos)
	{
		assert(pos < _size);
		return _str[pos];
	}

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

	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::insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		if (_size == _capacity)
		{
			size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
			reserve(newcapacity);
		}

		size_t end = _size + 1;
		while (end > pos)
		{
			_str[end] = _str[end - 1];//右移
			--end;
		}

		_str[pos] = ch;
		++_size;
	}

	void string::insert(size_t pos, const char* str)
	{
		assert(pos <= _size);

		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			reserve(_size + len);
		}

		size_t end = _size + len;
		while (end > pos + len - 1)
		{
			_str[end] = _str[end - len];//右移
			--end;
		}

		memcpy(_str + pos, str, len);
		_size += len;
	}

	void string::push_back(char ch)
	{
		if (_size == _capacity)
		{
			size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
			reserve(newcapacity);
		}

		 _str[_size] = ch;
		 _str[_size + 1] = '\0';
		 ++_size;

	}

	void string::append(const char* str)
	{
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			reserve(_size + len);
		}

		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::erase(size_t pos, size_t len)
	{
		assert(pos < _size);

		// len大于前面字符个数时,有多少删多少
		if (len >= _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}

		else
		{
			strcpy(_str + pos, _str + pos + len);
			_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* sub, size_t pos)
	{
		char* p = strstr(_str + pos, sub);
		return  p - _str;
	}

	void string::swap(string& s)
	{
		std::swap(_str, s._str);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}
	
	string string::substr(size_t pos, size_t len)
	{
	    // len大于后面剩余字符,有多少取多少
		if (len > _size - pos)
		{
			string sub(_str + pos);//直接构造子串返回
			return sub;
		}

		//len <= _size - pos
		else 
		{
			string sub;
			sub.reserve(len);
			for (size_t i = 0; i < len; i++)
			{
				sub += _str[pos + i];
			}
	
			return sub;
		}
    }
	

    bool string::operator<(const string& s) const
	{
		return strcmp(_str, s._str) < 0;
	}

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

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

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

	bool string::operator==(const string& s) const
	{
		return strcmp(_str, s._str) == 0;
	}

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

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


    istream& operator>> (istream& is, string& str)
	{
		str.clear();
		char ch = is.get();
		while (ch != ' ' && ch != '\n')
		{
			str += ch;
			ch = is.get();
		}

		return is;
	}

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

		return os;
	}

}

  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不吃肉的Humble

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值