C++初阶-string类的模拟实现3

目录

1.string::find(char ch,size_t pos=0) const的模拟实现

2.string::find(const char* str,size_t pos=0) const的模拟实现

3.string::size() const和string::capacity() const的模拟实现

4.string::operator[] (size_t pos)和string::operator[] (size_t pos) const的模拟实现

5.string::begin()和string::end()的实现

6.string::substr(size_t pos = 0, size_t len=npos) const的模拟实现

7.string::string(const string& s)的模拟实现

8.string::operator=(const string& str)的模拟实现

9.非成员函数的关系运算符的重载

10.std::operator>>和std::operator<<的模拟实现

11.string::clear()的模拟实现

12.总结



1.string::find(char ch,size_t pos=0) const的模拟实现

这个实现起来比较简单,思路:

由于string类底层是连续存储的,所以我们可以用for循环或者之后实现了begin()、end()后也可以用迭代器的方式来遍历string类,最后找到对应的字符即可,代码如下:

//.h
size_t find(char ch,size_t pos = 0) const;
//.cpp
size_t string::find(char ch, size_t pos) const
{
	for (size_t i = 0; i < _size; i++)
	{
		if (_str[i] == ch)
		{
			return i;
		}
	}
	//如果没找到返回npos
	return npos;
}

这个函数实现起来比较简单,就不进行演示了。

2.string::find(const char* str,size_t pos=0) const的模拟实现

这个函数的实现是需要借助C语言学过的strstr函数的,通过匹配来得到开始的下标,但是由于strstr返回的是指针,所以我们要返回的是返回值-开始位置(顺序表),但是在这之前还需要判断是否有找到,一般没找到会返回nullptr(NULL),这时候我们就要返回npos了,代码实现如下:

//.h
size_t find(const char* str,size_t pos = 0) const;
//.cpp
size_t string::find(const char* str, size_t pos)const
{
	//由于是在pos位置开始找,所以传的是_str+pos
	const char* p = strstr(_str + pos, str);
	if (p == nullptr)
	{
		return npos;
	}
	return p - _str;
}

3.string::size() const和string::capacity() const的模拟实现

这个实现很简单,所以就不做过多解释了,但是我们要注意,由于函数短小,所以我们可以直接把它的定义放到.h文件中:

//.h
size_t size() const
{
	return _size;
}
size_t capacity() const
{
	return _capacity;
}

4.string::operator[] (size_t pos)和string::operator[] (size_t pos) const的模拟实现

这两个都是运算符重载,只是一个可以修改另外一个不可修改而已,我们只要传入pos后返回_str[pos]即可,当然我们还需要检查合法性,用assert即可,且由于函数比较短,所以我们直接在.h文件中定义:

//.h
char& operator[] (size_t pos)
{
	assert(pos < _size);
	return _str[pos];
}
const char& operator[] (size_t pos) const
{
	assert(pos < _size);
	return _str[pos];
}

5.string::begin()和string::end()的实现

begin的实现是直接返回_str,而end的实现是返回_str+_size;返回的是迭代器,我们可以视为一个指针,所以我们之间返回即可,而且有两个版本,一个是可以修改另外一个是不可以修改的,而且由于函数短小,可以把它定义在.h文件中,这个是比较简单的,所以我就不作过多讲解了:

//.h
//这iterator就相当于char*的别名,学完string类之后会细讲
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
	return _str;
}
iterator end()
{
	return _str + _size;
}
const_iterator begin() const
{
	return _str;
}
const_iterator end() const
{
	return _str + _size;
}

迭代器我们现在用的不多,不过我可以演示一下它的用法:

//.cpp
void test1()
{
	string s("Hello world");
	string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	//也支持范围for
	//前提是已经实现了begin()和end()函数
	for (auto ch : s)
	{
		cout << ch << " ";
	}
	cout << endl;
}

运行结果如下:

这是迭代器的基本用法,我只在这里简单讲解一下用法。

6.string::substr(size_t pos = 0, size_t len=npos) const的模拟实现

这个函数的实现比之前复杂一些,这里讲一下实现步骤:

这个函数是拷贝从pos位置开始的len个字符,但是如果我们直接取len个字符会超出范围,所以我们要提前进行判断;

其次由于返回值为string类,所以我们如果临时创建一个新对象去返回,那么这个是不行的,因为空间释放了,所以我们需要用一个拷贝构造函数,因为不然我们就是用一个对象去接受返回值的,但是拷贝构造我们暂时没实现,这个我先实现了之后再写的这个函数就不会有问题了,所以我写的代码是在已经实现拷贝构造的情况下写的,拷贝构造函数等下会实现:

//.h
string substr(size_t pos = 0,size_t len = npos) const;
//.cpp
string string::substr(size_t pos, size_t len) const
{
	size_t leftlen = _size - pos;
	//判断是否是真实长度
	//也可以用!=
	if (len > leftlen)
	{
		len = leftlen;
	}
	string tmp;
	//申请len个空间
	tmp.reserve(len);
	for (size_t i = 0; i < len; i++)
	{
		//尾插
		tmp += _str[pos + i];
	}
	return tmp;
}

我们来测试一下这个函数:

//.cpp
void test1()
{
	string s("Hello world");
	string s1(s.substr(6));
	cout << s.c_str() << endl;
	cout << s1.c_str() << endl;
}

运行结果如下:

当然我们也可以用在其他地方,比如赋值上,但是这个函数暂时还没有实现,是浅拷贝。

7.string::string(const string& s)的模拟实现

这个是拷贝构造函数,由于我们拷贝构造是用一个已经实例化的对象去创建另外一个对象,所以我们需要先申请内存,而且这个空间必须要包含\0,所以我们要申请的是_capacity+1个空间;而且我们之后还要拷贝原对象的值到另外一个对象上,并把_size和_capacity都传递过去,所以代码实现如下:

//.h
string(const string& s);
//.cpp
string::string(const string& s)
{
	//我们需要多申请一块空间,因为我们还要存储\0
	_str = new char[s._capacity + 1];
	strcpy(_str, s._str);
	_size = s._size;
	_capacity = s._capacity;
}

8.string::operator=(const string& str)的模拟实现

这个函数的实现和拷贝构造函数的实现差不多,只是它返回值是对象的拷贝而已,而且返回的不是新对象,而是对象的拷贝,所以实现步骤如下:

如果遇到自己拷贝自己的情况我们需要直接返回*this,而不是继续运行!

我们需要先开辟新空间并释放旧空间

再把数据拷贝过去后把_size和_capacity也复制过去;

最后返回*this即可。

代码实现如下:

//.h
string& operator=(const string& s);
//.cpp
string& string::operator=(const string& s)
{
    if(this==&s)
    {
     return* this;
    }
	//开辟新空间,释放旧空间
	delete[]  _str;
	_str = new char[s._capacity + 1];
	strcpy(_str, s._str);
	_size = s._size;
	_capacity = s._capacity;
	return *this;
}

这个函数就不测试了,实现也是比较简单的。

9.非成员函数的关系运算符的重载

关系运算符包括以下函数:

我们只实现两个参数都是string类型的,而且我就不把它们置为友元函数了,直接置为成员函数,由于函数比较短小我们也可以把定义放在.h文件中,也可以不放入,我这里就不放入了:

//.h
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;
//.cpp
bool string::operator==(const string& s) const
{
	return strcmp(_str,s._str);
}
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 || *this == s;
}
bool string::operator>(const string& s) const
{
	//也可以return !(*this<=s)
	return strcmp(_str, s._str) < 0;
}
bool string::operator>=(const string& s) const
{
	return !(*this < s);
}

10.std::operator>>和std::operator<<的模拟实现

这两个操作符只有一个要注意的点,那就是它们一定要成为全局函数,但不一定要成为类的友元函数。这是一个额外的知识,其次,我们要注意的点是流提取(>>)这个需要在遇到' '或者'\n'时停止插入,所以最终代码如下(其中涉及到clear函数,这个之后会讲)其中的>>是有问题的,因为这个涉及到隐秘的申请,需要到进阶版本才能真的讲完

//.cpp
ostream& operator<<(ostream& out, const string& s)
{
	for (auto ch : s)
	{
		out << ch;
	}
	return out;
}
istream& operator>>(istream& in, string& s)
{
	char ch;
	in >> ch;
	while (ch != ' ' && ch != '\n')
	{
		s += ch;
		in >> ch;
	}
	return in;
}

11.string::clear()的模拟实现

这个函数非常简单,我都不想讲的,只要把_str='\0'并把_size=0即可,就相当于erase所有数据后把_size=0;不过我们直接用赋值就可以实现了,由于函数比较短小,所以我就在.h文件中定义了:

//.h
void clear()
{
	//赋值
	_str[0] = '\0';
	_size = 0;
}

12.总结

这一讲的内容比较多,但是内容都比较简单,所以注意的点也不是很多,现在已经把重要的函数都实现完了,但是下一讲会把这些初级版本进行优化,也就是说,这个不是最终版本,最终的版本虽然实现比较复杂了,但是总归还是效率提升了,比如:我们如果一个一个字符提取,那么效率非常低,并且这个>>是有问题的,它涉及到其他的知识,下一讲一起讲了。

喜欢的可以一键三连哦!下讲再见!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值