目录
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)的模拟实现
10.std::operator>>和std::operator<<的模拟实现
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.总结
这一讲的内容比较多,但是内容都比较简单,所以注意的点也不是很多,现在已经把重要的函数都实现完了,但是下一讲会把这些初级版本进行优化,也就是说,这个不是最终版本,最终的版本虽然实现比较复杂了,但是总归还是效率提升了,比如:我们如果一个一个字符提取,那么效率非常低,并且这个>>是有问题的,它涉及到其他的知识,下一讲一起讲了。
喜欢的可以一键三连哦!下讲再见!