🎉个人名片:
🐼作者简介:一名乐于分享在学习道路上收获的大二在校生
🙈个人主页🎉:GOTXX
🐼个人WeChat:ILXOXVJE
🐼本文由GOTXX原创,首发CSDN🎉🎉🎉
🐵系列专栏:零基础学习C语言----- 数据结构的学习之路----C++的学习之路
🐓每日一句:如果没有特别幸运,那就请特别努力!🎉🎉🎉
————————————————
🎉文章简介:
🎉本篇文章对 string的模拟实现 等相关知识 学习的相关知识进行分享!
💕如果您觉得文章不错,期待你的一键三连哦,你的鼓励是我创作动力的源泉,让我们一起加 油,一起奔跑,让我们顶峰相见!!!🎉🎉🎉
———————————————————————————
一.前言
上篇文章*《C++string类的介绍及常用函数用法总结》*(文章链接: link)相信大家已经对string类的使用等方面详细了解了,为了更好的了解string的特性,今天我们通过string类的底层结构来手撕一个属于我们自己的类,方便更加深刻的理解这部分内容。
二.string类的模拟实现
先看一下string类里面所需要包含的成员变量:
private:
char* _str; //指向存储字符串的指针
size_t _size; //记录存储的有效数据个数
size_t _capacity; //记录空间大小
重点实现的函数
一.构造函数
string(const char* str = "") //空字符串里面默认放的\0
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity+1];
strcpy(_str, str);
}
避坑
常见错误与不合适的写法写法:
第一种:
这种是直接将成员变量全部初始化为0处理;
string()
:_str(nullptr)
,_size(0)
,_capacity(0)
{
}
不合理之处:
1.库里面string类既能能用常量字符串去构造对象,也可以无参构造对象,这样写不能达到和库里面的一样;
2._str是指向一个字符串的指针,应该以 ‘\0’ 结束,直接初始化为nullptr不能达到该效果;
3.其他函数对指针处理不当,很有可能会对指针进行解引用访问,会对空指针解引用;
第二种:
1.库里面string类既能能用字符串去构造对象,也可以无参构造对象,这样写不能达到和库里面的一样;
2.这样写构造函数效率低,每次初始化变量时都要去计算str长度,虽然可以先初始化_size,再去用_size去初始化其他变量,但是必须保证成员变量先声明_size;初始化顺序与声明顺序一样才行,代码维护性低;
string(const char* str)
:_str(new char[strlen(str) + 1])
,_size(strlen(str)) //不合适的写法
,_capacity(strlen(str))
{
strcpy(_str, str); //会将\0拷贝进去
}
第三种:
1.错误写法,将’\0’赋值给str,类型不匹配,会报错;
string(const char* str='\0') //会报错,类型不匹配
{
_str = new char[1];
_size = 0;
_capacity = 0;
_str[0] = '\0';
}
二.拷贝构造函数
第一种:传统写法
//s1(s2)
string(const string& s)
{
_str = new char[s._capacity + 1]; //开空间
strcpy(_str,s._str); //拷贝
_size = s._size; //赋值
_capacity = s._capacity;
}
第二种:现代写法
直接用s的_str构造tmp对象,然后两个对象交换即可;
string(const string& s)
{
string tmp(s._str);
swap(tmp);
}
三.赋值运算符重载
第一种:传统写法
//s1=s2
string& operator=(const string& s)
{
char* tmp = new char[s._capacity + 1]; //开空间
strcpy(tmp, s._str); //拷贝
std::swap(tmp, _str); //交换指针
_size = s._size; //赋值
_capacity = s._capacity;
return *this;
}
第二种.现代写法
//s1=s2
string& operator=(string s)
{
swap(s);
return *this;
}
这里的巧妙在于没有用传引用传参,用的传值传参,s是s2的拷贝,s的改变不影响s2,调用上面实现的swap函数,交换s与s1,s1就指向了s指向的空间,_size和_capacity都与s的一样,出了作用域,s会调用析构函数清理掉s1之前的空间;
四.析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
五.运算符[ ]重载
char operator[](size_t pos)
{
return _str[pos]; //返回对应的值
}
char operator[](size_t pos)const
{
return _str[pos];
}
六.流提取运算符重载
ostream& operator<<(ostream& out, const string& s)
{
for (int i = 0; i < s.size(); i++)
{
cout <<s[i];
}
cout << endl;
return out;
}
七.流插入运算符重载
注意:
这里不能使用 scanf 和 getchar ,因为他们是遇到空格或则\0就会停止获取,就导致ch不会等于’ ’ 和 ‘\0’,那么while循环无法结束;这里使用的是istream类里面的get函数;
istream& operator>>(istream& in, string& s)
{
char ch;
ch = in.get();
while (ch!=' '&&ch!='\0')
{
s += ch; //尾插到s
ch = in.get();
}
return in;
}
八.迭代器的实现
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
const_iterator begin()const
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator end()const
{
return _str+_size;
}