string的模拟实现
📌string的模拟实现
😊本文为小碗里原创,CSDN首发📅发布时间:2002/3/19🙌欢迎大家👍点赞❤收藏✨加关注✒本文大约3100词左右🙏笔者水平有限,如有错误,还望告诉笔者,万分感谢!🚩有什么问题也可在评论区一起交流哦!
文章目录
- 📌string的模拟实现
-
- 🎉前言
- 🎉模拟实现
-
- 👂一.主干结构
- 👂二.成员函数
-
- 🥩系列一
-
- 1.构造函数
- 2.析构函数
- 3.拷贝构造函数(深拷贝)
- 4.赋值运算符重载
- 🥩系列二
-
- 1.增容 reserve
- 2.设置内存大小 resize
- 3.插入字符 insert
- 4.插入字符串 insert
- 5.尾插字符 push_back
- 6.尾插字符串 append
- 7.+=
- 7.字符查找 find
- 8.字符串查找 find
- 9.字符(串)擦除 erase
- 10.清除 clear
- 🥩系列三
-
- 1.operator[]
- 2.c_str
- 3.size
- 🥩系列四(迭代器)
- 👂三.非成员函数
-
- 系列四(关系运算符)
-
- 1.operator>
- 2.operator==
- 3.operator>=
- 4.operator<
- 5.operator<=
- 6.operator!=
- 🥩系列五
-
- 1.operator<<
- 2.operator>>
- 🎉完整代码
- 🎶练习
🎉前言
在学习了string后,我们了解并掌握了对string的使用,但对于string的内部实现,有很多人穷尽一生也没有搞明白,那今天小碗就带大家简单模拟一下string的内部实现,给string做一个了断!
let’s go!🏃♂️🏃♀️
🎉模拟实现
👂一.主干结构
#include//平时练习可以全部展开,命名污染可忽略using namespace std;//自己模拟实现的string要与标准库里的string区别//故模拟实现的string放在自己的命名空间namespace S{class string{public: //成员函数private: //成员变量char* _str; //字符串int _size; //当前有效字符个数(不含'\0')int _capacity; //有效字符容量(不含'\0') public: static const size_t npos = -1; //const修饰的静态的成员变量可以在类里给缺省值 //但非const修饰的成员变量只能在类外初始化};}
👂二.成员函数
🥩系列一
1.构造函数
string(const char* str = "")//默认缺省值为'\0':_str(nullptr), _size(strlen(str)), _capacity(_size){_str = new char[_capacity + 1];//为'\0'多开一个空间strcpy(_str, str);}
2.析构函数
~string(){ //当_str不为nullptr时释放空间if (_str){delete[] _str;_str = nullptr;_size = _capacity = 0;}}
3.拷贝构造函数(深拷贝)
//写法一:1.0版本string(const string& s):_str(nullptr), _size(s._size), _capacity(s._capacity){_str = new char[_capacity + 1];//为'\0'多开一个空间strcpy(_str, s._str);}//写法二:2.0版本(分析如下图)string(const string& s):_str(nullptr),_size(s._size),_capacity(s._capacity) //_str必须初始化为nullptr{ //复用构造函数开空间,且tmp为局部变量,之后有析构函数销毁string tmp(s._str);std::swap(_str, tmp._str);}//这里也可用我们自己实现的swap成员函数,它的定义在下文给出//写法三:string(const string& s):_str(nullptr), _size(0), _capacity(0){string tmp(s._str);swap(tmp);}
图 1 图1 图1
[浅拷贝VS深拷贝]
简而言之,浅拷贝就是单纯的值拷贝,尤其对于指针,完成的是指针的值拷贝,拷贝出来的新指针一同指向同一块空间,对一个指针进行操作时,另一个指针也会受到印象;
深拷贝则是从新开一块内存空间,新的指针指向新开的空间
4.赋值运算符重载
//写法一:1.0版本string& operator=(const string& s){if (this != &s)//避免自己给自己赋值{int len = strlen(s._str); //避免new抛异常,而_str却被释放 char* tmp = new char[len + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_capacity = _size = len;} //函数调用完成后,*this没有被销毁,故采用引用返回,减少拷贝return *this;}
部分读者可能会问,为何要重新new一个tmp,再把s._str拷贝给tmp,再销毁 __str,将其指向tmp?何必如此麻烦?
也许你们的写法如下:
string& operator=(const string& s){if (this != &s){int len = strlen[s._str]; //假如new失败了,既没有开新的空间,还把_str释放了delete[] _str;_str = new char[len + 1];strcpy(_str, s._str);_capacity = _size = len;}return *this;}
细节决定成败…
//写法二:2.0版本(分析如图2)string& operator=(string s)//注意,这里不是引用传参,传值传参调用拷贝构造函数{swap(s);return *this;}//交换两个string类对象(也是一个成员函数,默认第一个形参是this指针)void swap(string& s){ //用全局的swap std::swap(s._str, _str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}
图 2 图2 图2
🥩系列二
1.增容 reserve
void reserve(size_t n)//n:要增到的容量大小{if (n > _capacity){char* tmp = new char[n + 1];//为'\0'多开一个空间strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}
2.设置内存大小 resize
void resize(size_t n, char ch = '\0'){if (n > _capacity){ //开辟n+1个空间,第n+1个空间的下标是nreserve(n);memset(_str + _size, ch, n - _size);_size = n;_str[_size] = '\0';}else{_str[n] = '\0';_size = n;}}
3.插入字符 insert
//分析如图3string& insert(size_t pos, char ch){ //断言(要引头文件#include)assert(pos <= _size);if (_size == _capacity){//_capacity可能为0reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size;//_size位置是'\0'while (end > pos){_str[end + 1] = _str[end];end--;}_str[end] = ch;_size++;return *this;}
图 3 图3 图3
4.插入字符串 insert
string& insert(size_t pos,const char* s){ //断言assert(&& pos <= _size);size_t len = strlen(s);if (_size == _capacity){ //会多开辟一个空间reserve(_size + strlen(s));}size_t end = _size;while (end>pos){_str[end + len] = _str[end];end--;}//限定拷贝字符的长度strncpy(_str + pos, s, len);_size += len;return *this;}
5.尾插字符 push_back
void push_back(char ch){insert(_size, ch);}
6.尾插字符串 append
void append(const char* s){insert(_size, s);}
细节🤔:
你可曾想过,对于两个重载的insert函数来说,他们带了返回值,而push_back和append没带,大多数读者可能认为这里其实没必要带返回值,这里带返回值笔者认为一方面库就是这么实现的,模拟的时候跟着库的标准来,另一方面带上返回值也有它的一些用处,比如进行连续插入.
int main(){my_string::string s1("lllll");s1.insert(2, "sss").insert(3, "ddd");return 0;}
7.+=
string& operator+=(char ch){ //复用push_back(ch);return *this;}string& operator+=(const char* s){ //复用append(s);return *this;}
7.字符查找 find
size_t find(char ch){for (size_t i = 0; i < _size; i++){if (_str[i] == ch){return i;}}//npos:无符号整型的最大值,static const size_t npos = -1; return npos;}
8.字符串查找 find
//从pos位置开始查找size_t find(const char* s,size_t pos = 0){//char* strstr(const char *str1, const char *str2) //str2是否是str1的字串 //如果是则返回第一个字串的起始地址,否则返回nullptrconst char* tmp = strstr(_str + pos, s);if (tmp == nullptr){return npos;}else{ return tmp - _str;//返回下标}}
9.字符(串)擦除 erase
string& erase(size_t pos = 0, size_t n=npos){assert(pos < _size);if (n == npos || pos + n >= _size){_str[pos] = '\0';_size = pos;}else{ //直接往前覆盖strcpy(_str + pos, _str + pos + n);/*int end = pos;while (end + n < _size){_str[end] = _str[end + n];end++;}*/_size -= n;}return *this;}
10.清除 clear
//清除内容,不释放空间void clear(){_str[0] = '\0';_size = 0;}
🥩系列三
1.operator[]
//用于支持修改char& operator[](size_t pos){ assert(pos<_size && pos>=0)return _str[pos];}//用于支持访问const char& operator[](size_t pos) const//类成员变量不会被修改,加上const{assert(pos < _size && pos>=0);return _str[pos];}
2.c_str
//获取string类里的字符串const char* c_str() const{return _str;}
3.size
size_t size(){return _size;}
🥩系列四(迭代器)
typedef char* iterator;typedef const char* const_iterator;const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}iterator begin(){return _str;}iterator end(){return _str + _size;}
👂三.非成员函数
系列四(关系运算符)
1.operator>
//卷王写法:bool operator>(const S::string& s1, const S::string& s2){size_t i = 0;while (i < s1.size() && i < s2.size()){//用[]访问类成员私有变量if (s1[i] < s2[i]){return false;}else{i++;}}return s1.size()>i ? true : false; //eg: asd asd -> false // asd asdf -> false // asdf asd -> true -> s1.size()>i}//摆王写法:bool operator>(const S::string& s1, const S::string& s2){ return strcmp(s1.c_str(), s2.c_str()) > 0;}
2.operator==
//卷王写法:bool operator==(const S::string& s1, const S::string& s2){size_t i = 0;while (i < s1.size() && i < s2.size()){if (s1[i] != s2[i]){return false;}else{i++;}}if (i == s1.size() && i == s2.size()){return true;}else{return false;}}//摆王写法:bool operator==(const S::string& s1, const S::string& s2){ return strcmp(s1.c_str(),s2.c_str()) == 0;}
[以下皆可复用]
3.operator>=
bool operator>=(const S::string& s1, const S::string& s2){return s1 > s2 || s1 == s2;}
4.operator<
bool operator<(const S::string& s1, const S::string& s2){return !(s1 >= s2);}
5.operator<=
bool operator<=(const S::string& s1, const S::string& s2){return !(s1>s2);}
6.operator!=
bool operator!=(const S::string& s1, const S::string& s2){return !(s1 == s2);}
🥩系列五
1.operator<<
ostream& operator<<(ostream& out, const string& s){for (size_t i = 0; i < s.size(); ++i){ //[]支持访问私有out << s[i];} //范围for/*for (auto c : s){out << c;}*/}
2.operator>>
istream& operator>>(istream& in, string& s){s.clear();char ch = in.get();while (ch != '\n'){s += ch;ch = in.get();}return in;}
🎉完整代码
namespace S{class string{public:typedef char* iterator;typedef const char* const_iterator;const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}iterator begin(){return _str;}iterator end(){return _str + _size;}//构造函数string(const char* str = ""):_str(nullptr), _size(strlen(str)), _capacity(_size){_str = new char[_capacity + 1];strcpy(_str, str);}//析构函数~string(){//当_str不为nullptr时释放空间if (_str){delete[] _str;_str = nullptr;_size = _capacity = 0;}}string(const string& s):_str(nullptr), _size(0), _capacity(0){string tmp(s._str);swap(tmp);}//赋值运算符重载//现代写法void swap(string& s){ std::swap(s._str, _str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}string& operator=(string s){swap(s);return *this;}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}string& insert(size_t pos, char ch){assert(pos >= 0 && pos <= _size);if (_size == _capacity){//_capacity可能为0reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size;while (end > pos){_str[end + 1] = _str[end];end--;}_str[end] = ch;_size++;return *this;}string& insert(size_t pos, const char* s){assert(pos >= 0 && pos <= _size);size_t len = strlen(s);if (_size == _capacity){reserve(_size + strlen(s));}size_t end = _size;while (end>pos){_str[end + len] = _str[end];end--;}//限定拷贝字符的长度strncpy(_str, s, len);_size += len;return *this;}void push_back(char ch){insert(_size, ch);}void append(const char* s){insert(_size, s);}string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* s){append(s);return *this;}void resize(size_t n, char ch = '\0'){if (n > _capacity){reserve(n);memset(_str + _size, ch, n - _size);_size = n;_str[_size] = '\0';}else{_str[n] = '\0';_size = n;}}size_t find(char ch){for (size_t i = 0; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}//从pos位置开始查找size_t find(const char* s,size_t pos = 0){//char* strstr(const char *str1, const char *str2)char* tmp = strstr(_str + pos, s);if (tmp != nullptr){return tmp - _str;}else{return npos;}}string& erase(size_t pos = 0, size_t n=npos){assert(pos >= 0 && pos < _size);if (n == npos || pos + n >= _size){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + n);_size -= n;}return *this;}//用于支持修改char& operator[](size_t pos){assert(pos < _size && pos >= 0);return _str[pos];}//用于支持访问const char& operator[](size_t pos) const//类成员变量不会被修改,加上const{assert(pos < _size && pos >= 0);return _str[pos];}char* c_str() const {return _str;}void clear(){_str[0] = '\0';_size = 0;}size_t size() const{return _size;}private:char* _str;int _size;int _capacity;public:static const size_t npos = -1;};}bool operator>(const S::string& s1, const S::string& s2){size_t i = 0;while (i < s1.size() && i < s2.size()){//用[]访问类成员私有变量if (s1[i] < s2[i]){return false;}else{i++;}}return s1.size()>i ? true : false;}bool operator==(const S::string& s1, const S::string& s2){size_t i = 0;while (i < s1.size() && i < s2.size()){if (s1[i] != s2[i]){return false;}else{i++;}}if (i == s1.size() && i == s2.size()){return true;}else{return false;}}bool operator>=(const S::string& s1, const S::string& s2){return s1 > s2 || s1 == s2;}bool operator<(const S::string& s1, const S::string& s2){return !(s1 >= s2);}bool operator<=(const S::string& s1, const S::string& s2){return !(s1>s2);}bool operator!=(const S::string& s1, const S::string& s2){return !(s1 == s2);}ostream& operator<<(ostream& out, const string& s){for (size_t i = 0; i < s.size(); ++i){//[]支持访问私有out << s[i];}}istream& operator>>(istream& in, string& s){s.clear();char ch = in.get();while (ch != '\n'){s += ch;ch = in.get();}return in;}
🎶练习
以下是各个函数的接口,供读者自行练习
#include#include#includeusing namespace std;namespace S{class string{public:typedef char* iterator;typedef const char* const_iterator;const_iterator begin() constconst_iterator end() constiterator begin()iterator end()string(const char* str = "") ~string()string& operator=(string s)void reserve(size_t n)string& insert(size_t pos, char ch)string& insert(size_t pos, const char* s)void push_back(char ch)void append(const char* s)string& operator+=(char ch)string& operator+=(const char* s)void resize(size_t n, char ch = '\0')size_t find(char ch)size_t find(const char* s,size_t pos = 0)string& erase(size_t pos = 0, size_t n=npos)char& operator[](size_t pos)const char& operator[](size_t pos) constchar* c_str() const void clear() size_t size() constprivate:char* _str;int _size;int _capacity;public:static const size_t npos = -1;};}bool operator>(const S::string& s1, const S::string& s2)bool operator==(const S::string& s1, const S::string& s2)bool operator>=(const S::string& s1, const S::string& s2)bool operator<(const S::string& s1, const S::string& s2)bool operator<=(const S::string& s1, const S::string& s2)bool operator!=(const S::string& s1, const S::string& s2)ostream& operator<<(ostream& out, const string& s)istream& operator>>(istream& in, string& s)
最后,笔者想说的是,string的成员函数还有很多,这里模拟实现的都是string中较为常用的,其他的函数读者有兴趣的话可以自己下去模拟实现一下,当然也可与笔者一起交流学习!😁🤪😆
ch)
string& operator+=(const char* s)void resize(size_t n, char ch = '\0')size_t find(char ch)size_t find(const char* s,size_t pos = 0)string& erase(size_t pos = 0, size_t n=npos)char& operator[](size_t pos)const char& operator[](size_t pos) constchar* c_str() const void clear() size_t size() constprivate:char* _str;int _size;int _capacity;public:static const size_t npos = -1;};
}
bool operator>(const S::string& s1, const S::string& s2)
bool operator==(const S::string& s1, const S::string& s2)
bool operator>=(const S::string& s1, const S::string& s2)
bool operator<(const S::string& s1, const S::string& s2)
bool operator<=(const S::string& s1, const S::string& s2)
bool operator!=(const S::string& s1, const S::string& s2)
ostream& operator<<(ostream& out, const string& s)
istream& operator>>(istream& in, string& s)
> 最后,笔者想说的是,string的成员函数还有很多,这里模拟实现的都是string中较为常用的,其他的函数读者有兴趣的话可以自己下去模拟实现一下,当然也可与笔者一起交流学习!😁🤪😆