> 文档中心 > string的模拟实现

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中较为常用的,其他的函数读者有兴趣的话可以自己下去模拟实现一下,当然也可与笔者一起交流学习!😁🤪😆