STL::string 模拟实现

成员//成员函数

成员 (初始化)

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

private:
	char* _str = nullptr;
	size_t _size = 0;
	size_t _capacity = 0;

	const static size_t npos = -1;//足够大 18446744073709551615
};

1.首先需要一个char* 来指向储存的字符串内容

2.需要_size来控制字符串大小

3.需要_capacity来控制空间大小

begin/end

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

const_iterator cbegin()const   { return _str; }
const_iterator cend()const   { return _str + _size; }

首先实现begin( ) 和end( ) cbegin( )和 cend( )函数,这样就可以支持范围for的使用了

size/capacity 

size_t size()const
{
	return _size;
}

size_t capacity()const
{
	return _capacity;
}
直接返回成员 _size_capacity就可以了
注意要加const否则会权限放大

 reverse

void reverse(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;
		tmp = nullptr;
		_capacity = n;
	}
}

 扩容函数:与之前的vector同理,先开一个临时空间,再把本身的内容拷贝到新空间

这里用到strcpy

 

destination(目标) source(资源),将资源中的内容拷贝到目标字符串中 

c_str//substr 

const char* c_str()
{
	return _str;
}

string substr(size_t pos, size_t len = npos)
{
	assert(pos >= 0 && pos < _size);
	size_t end = pos + len;
	if (len == npos || pos + len > _size)
	{
		end = _size;
	}
	string str;
	str.reverse(end-pos);
	for (size_t i = pos; i < end; i++)
	{
		str += _str[i];
	}

	return str;
}

c_str:返回string里储存的 char* 内容,所以这里直接返回成员 _str 就可以了

substr :从本身的字符串中的 pos位置向后len长度的内容拷贝到新的sting对象 返回拷贝的对象(就相当于取到这个字符串的一段如果len过长的话默认拷贝到本身的最后一位

然后遍历赋值返回新的字符串就可以完成任务

find 

 

size_t find(char ch,size_t pos=0)//从pos后边位置开始find
{
	assert(pos >= 0 && pos < _size);
	for (size_t i = pos; i < _size; i++)//遍历
	{
		if (_str[i] == ch)
		{
			return i;
		}
	}
	return npos;// 没找到 18446744073709551615
}
size_t find(const char* str, size_t pos = 0)
{
	assert(pos >= 0 && pos < _size);
	const char* ptr = strstr(_str + pos, str);
	if (ptr == nullptr)
	{
		return npos;
	}
	else
	{
		return ptr - _str;
	}
}

 

这里两个重载(一个find char ,一个find const char *)

遍历判断就可以完成查找

 

构造函数

/*string()
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
}*/

 由于我们在声明成员的时候已经进行了默认初始化,所以这里的构造函数什么都不需要做

这里如果写上初始化列表的话,会报错:多个构造函数,所以这里直接屏蔽掉

                             

拷贝构造

 

string(const char* str = "")
{
	_size = strlen(str);
	_capacity = _size;
	_str = new char[_capacity + 1];

	strcpy(_str, str);
}

 直接将 str 中的内容strcpy到 _str 中,这里strlen可以得到str的长度

 

//传统写法
/*string(const string& str)
{
	_str = new char[str._capacity + 1];
	strcpy(_str, str._str);
	_size = str._size;
	_capacity = _capacity;
}*/
string(const string& str)
{
	string tmp = str;
	swap(tmp);
}
void swap(string& str)
{
	std::swap(_str, str._str);
	std::swap(_size, str._size);
	std::swap(_capacity, str._capacity);
}

 拷贝新的string时道理可以和拷贝const char * 时一样,但是也可以与vector那里一样 直接交换临时资源

注意:

string tmp = str;

需要重载operator = (),要不然会报错(后面谈operator的时候介绍)

析构

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

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

这里把clear也在这里提到 由于字符串会识别 '\0' 来当作字符串的结尾所以直接把第一个元素设置成' \0 '就可以了

析构:释放_str指向的内容,再把成员清零,完成释放  

插入元素

push_back( ) 

void push_back(char ch)
{
	if (_size == _capacity)
	{
		size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
		reverse(newcapacity);
	}
	_str[_size] = ch;
	_size++;
	_str[_size] = '\0';
}

尾插:首先检查容量够不够,如果不够则需要扩容,这里扩容机制与之前vector一致直接复用reverse( )因为_str[_size]位置时最后一个元素的后面一个位置一般时'\0'所以直接 _str[_size]=ch完成尾插 不要忘了再把最后一个位置的后一位设置成' \0 '这也是为什么capacity需要多开一个的原因——用来存'\0'

 

append( ) 

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

	strcpy(_str + _size, str);
	_size += len;
	_str[_size] = '\0';
}

 

 

 追加:在原字符串后边加上一个新的字符串const char *类型)(这里只重载第一个)

先检查容量,防止容量不够,然后直接把传入的字符串拷贝到_str+_size位置,最后加上'\0'

insert 

void insert(size_t pos, char ch)
{
	assert(pos >= 0 && pos < _size);
	if (_size == _capacity)//扩容同push_back
	{
		size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
		reverse(newcapacity);
	}
	size_t end = _size + 1;
	while (end > pos)
	{
		_str[end] = _str[end - 1];
		--end;
	}
	_str[pos] = ch;
	++_size;
}

void insert(size_t pos, const char* str)
{
	assert(pos >= 0 && pos < _size);
	size_t len = strlen(str);//扩容同append
	if (_size + len > _capacity)
	{
		reverse(_size + len);
	}

	size_t end = _size + len;
	while (end > pos)
	{
		_str[end] = _str[end - len];
		--end;
	}
	strncpy(_str + pos, str, len);
	_size += len;
}

 

insert:插入在_str[pos]位置插入新的元素(不管时const char* 还是char)

首先检查容量!!!   再挪动pos后边的元素,往后挪动1位或多位,最后把需要插入的内容赋值到腾出来的空间中,完成插入 (这里重载了插入字符和字符串两种)

删除元素

erase( )

void erase(size_t pos, size_t len = npos)
{
	assert(pos >= 0 && pos < _size);
	if (len == npos || pos + len > _size)//如果需要删除长度过长
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + len + pos);//把pos后边len以后的内容拷贝到pos后
		_size -= len;
	}
}

 

如果len过长,就直接把pos以后的内容全部删除 只需要把pos位置改成'\0'即可 

如果需要删除中间一段,把后边的内容覆盖需要删除的即可  直接strcpy

operator

=

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;
}

原理与拷贝构造一致(二者可以替换) 

[ ] 

 

const char& operator[](size_t pos)const
{
	assert(pos >= 0 && pos <= _size);
	return _str[pos];
}

char& operator[](size_t pos)
{
	assert(pos >= 0 && pos <= _size);
	return _str[pos];
}

 直接返回pos位置的引用就可以了,这里重载了 const 版本和 非const 版本

+=

 

string& operator+=(char ch)
{
	push_back(ch);

	return *this;
}
string& operator+=(const char* str)
{
	append(str);

	return *this;
}

 +=:在原字符串后边追加新的元素即可,这里没有重载追加string类型(取出string中的_str复用即可)这里直接复用push_back( )append( )

>>输入

istream& operator>>(istream& in, string& s)
{
	s.clear();
	char buff[128] = {'\0'};
	char ch = in.get();
	int i = 0;
	while (ch != ' ' && ch != '\n')//输入不为空格和回车
	{
		buff[i++] = ch;
		if (i == 127)
		{
			buff[i] = '\0';
			s += buff;
			i = 0;
		}
		ch = in.get();
	}
	if (i > 0)
	{
		buff[i] = '\0';
		s += buff;
	}
	return in;
}

这里不能写在类里边,需要写在类外,用buff来接收输入的char,然后将buff中储存的内容追加到 string 中完成输入

 <<输出

 

ostream& operator<<(ostream& out, const string& s)
{
	for (auto e : s)
	{
		out << e;
	}
	return out;
}

遍历string 然后依次打印每一个元素即可 (这里用到了范围for ,需要支持begin() 和

end( ) ,否则范围for会报错) !

 头文件

#include<assert.h>
#include<string.h>

我们需要保证成员函数中的pos在0-_size之间,否则会越界,这里直接暴力断言

我们用到的strlen strcpy strstr strncpy等函数都在string.h 中

  • 21
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值