【c++】string类的模拟实现(下)

昨天学习了string类的基本实现,今天学习了完整的简单版实现,特来社区记录。

目录

前言

一、增

1.reserve扩容

2.push_back()插入

 3.append()插入

 4.+=重载

二、删

1.erase()

三、查

四、迭代器

五、[]重载

总结



前言

本文主要模拟实现string类的增删查改的成员函数。


一、增

        由于历史遗留问题,string类实现了push_back(), append()等增加字符或者是字符串的函数,它们或多或少都有一些内容上的重叠功能实现,其中是以+=重载最为实用。     

         在此类函数实现之前需要判断剩下的capacity够不够放插入的字符串:

        1. 插入一个字符,就判断_size+1后与_capacity的大小

        2. 插入一个字符串,就判断_size+strlen(str) 后与_capacity的大小

所以需要先实现一个扩容函数来保证没有越界问题。

1.reserve扩容

代码如下(示例):

// 扩容函数
void reserve(size_t n);

// s1.reserve(20)
void qyy::string::reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];
		if (tmp)
		{
			strcpy(tmp, _str);
			_str = tmp;
			_capacity = n;
		}
	}
}

2.push_back()插入

代码如下(示例):

// 声明
void push_back(const char ch);
void push_back(const char* str);
// 定义
void qyy::string::push_back(const char ch)
{
	if (_size + 1 > _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	// 指向'\0'
	char* end = _str + _size;
	*(end + 1) = * end;
	_str[_size++] = ch;
}
void qyy::string::push_back(const char* str)
{
	size_t len = strlen(str);
	// 判断增容
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}
	// 指向'\0'
	char* end = _str + _size;
	*(end + len) = *end;
	strncpy(_str+_size, str, len);
	_size += len;
	_str[_size] = '\0';
}

 3.append()插入

代码如下(示例):

// 定义
void append(const char ch, size_t pos);
void append(const char* str, size_t pos);
//定义
void qyy::string::append(const char ch, size_t pos)
{
	assert(pos <= _size);
	// 判断增容
	if (_size + 1 > _capacity)
	{
		reserve(_capacity * 2);
	}

	char* begin = _str + pos;
	char* end = _str + _size;
	while (end > begin)
	{
		*(end + 1) = *end;
		end--;
	}
	_str[pos] = ch;
	_str[++_size] = '\0';
}
// 定义
void qyy::string::append(const char* str, size_t pos)
{
	assert(pos <= _size);
	size_t len = strlen(str);
	// 判断增容
	while (_size + len > _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	char* begin = _str + pos - 1;
	char* end = _str + _size;
	while (end > begin)
	{
		*(end + len) = *end;
		end--;
	}
	strncpy(_str + pos, str, len);
	_size += len;
	_str[_size] = '\0';
}

 4.+=重载

        直接复用push_back() 和 append()即可。

 代码如下(示例):

// 声明
string& operator+=(const char ch);
string& operator+=(const char* str);

// 定义
string& qyy::string::operator+=(const char ch)
{
	qyy::string::push_back(ch);
	return *this;
}
string& qyy::string::operator+=(const char* str)
{
	qyy::string::append(str, _size);
	return *this;
}

二、删

1.erase()

        删除函数分为两种情况:

        1. 要删除的长度小于从要删除的位置开始剩余字符的个数,也就是够删

        2. 与1相反,不够删的情况,那就是从pos开始所有的字符全部删干净

代码如下(示例):

// 删
string& erase(size_t pos, size_t len);

// 定义
string& qyy::string::erase(size_t pos, size_t len)
{
	// 剩下的字符个数不够删
	if (_size - pos < len)
	{
		_str[pos] = '\0';
		_size = _capacity = 0;
		return *this;
	}
	// 够删
	while (len > 0)
	{
		char* begin = _str + pos;
		char* end = _str + _size;
		while (begin < end)
		{
			*begin = *(begin + 1);
			begin++;
		}
		_size--;
		len--;
	}
	return *this;
}

三、查

        函数要实现从pos开始查找给定的字符或者字符串,返回查找到的字符或者字符串位置的下标,找不到那就返回npos。

代码如下(示例):

// 查
size_t find(const char ch, size_t pos);
size_t find(const char* str, size_t pos);

// 定义
size_t qyy::string::find(const char ch, size_t pos)
{
	int i = 0;
	for (i = 0; i < _size; i++)
	{
		if (_str[i] == ch)
		{
			return i;
		}
	}
	if (i == _size)
	{
		return npos;
	}
}
size_t qyy::string::find(const char* str, size_t pos)
{
	const char* ptr = strstr(_str, str);
	if (ptr)
		return ptr - _str;
	else
		return npos;
}

四、迭代器

        迭代器我目前的理解就是一个指针,begin指向空间的开头,end则指向空间的末尾。

代码如下(示例):

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

五、[]重载

        此运算符的重载使得字符串能像数组一样能够用下标支持随机访问。

代码如下(示例):

// []重载
char operator[](size_t pos);

// 定义
char qyy::string::operator[](size_t pos)
{
	// 读取内容须在字符串中
	assert(pos < _size);
	return *(_str + pos);
}

 六、流提取、流插入

        ">>" 和 "<<"的重载很微妙:

        如果需要访问私有,为了方便那就不可避免地需要重载为友元函数(比如在日期类中),但是在string中,因为有[](下标)的重载可以访问到字符串的各个元素,所以只用在全局定义即可。(在全局是为了避免存在隐含的this参数使得cout与string对象的参数顺序符合常识,即cout是<<的第一个参数,string对象是<<的第二个参数)

代码如下(示例):

// 声明
std::ostream& operator<<(std::ostream& out, string& s);
std::istream& operator>>(std::istream& in, string& s);

// 定义
// cout<<
std::ostream& qyy::operator<<(std::ostream& out, string& s)
{
	for (auto ch : s)
	{
		out << ch;
	}
	out << '\n';
	// 不能写:
	// 这样会在遇到'\0'时终止
	//out << s.c_str();
	return out;
}

// cin>>
std::istream& qyy::operator>>(std::istream& in, string& s)
{
	// 必须使用匿名对象去调用成员函数
	string().clear(s);
	char ch = in.get();
	while (ch != ' ' && ch != '\n')
	{
		s += ch;
		ch = in.get();
	}
	return in;
}

七、比较大小

        与C库的strcmp()实现相同,我自己同时也早了一个轮子。

代码如下(示例):

// 定义
bool operator<(string& s);
bool operator>(string& s);
bool operator==(string& s);
bool operator<=(string& s);
bool operator>=(string& s);

// 比较大小
bool qyy::string::operator<(string& s)
{
	//char* p1 = _str;
	//char* p2 = s._str;
	//while (p1 != _str + _size && p2 != s._str + s._size)
	//{
	//	if (*p1 == *p2)
	//	{
	//		p1++;
	//		p2++;
	//	}
	//	else if (*p1 < *p2)
	//	{
	//		return true;
	//	}
	//	else if (*p1 > *p2)
	//	{
	//		return false;
	//	}
	//}
	 两个字符串不一样长
	//if (p1 == _str + _size && p2 != s._str+s._size)
	//{
	//	return true;
	//}
	//else
	//{
	//	return false;
	//}
	// 或者直接调用库函数
	return strcmp(_str, s.c_str()) < 0;
}
bool qyy::string::operator==(string& s)
{
	return strcmp(_str, s.c_str()) == 0;
}
bool qyy::string::operator<=(string& s)
{
	return (*this < s || *this == s);
}
bool qyy::string::operator>=(string& s)
{
	return !(*this < s);
}
bool qyy::string::operator>(string& s)
{
	return !(*this <= s);
}

八、清空

        清空与erase中全部删完的情况相同,且为头删。

代码如下(示例):

// 清空
void clear(string& s); 

// 定义
void qyy::string::clear(string& s)
{
	s._str[0] = '\0';
	s._size = 0;
}

总结

今天系统学习了string类成员函数的详细实现,速度并不是很快,对于数组的控制不是很到位,像书上说的一样:“在这件事上,你总是少了一位。”以后要加强学习,加大代码量练习,再接再厉!

你可能感兴趣的:(c++学习路程,c++,c语言,学习,经验分享)