当前位置:   article > 正文

string模拟实现

string模拟实现

string是C++里面对于字符串处理非常方便的容器,不仅支持方括号加下标的访问,还内置各种接口,用起来嘎嘎爽。

前置注意事项:

在模拟实现的时候,我们需要自己开一块命名空间,避免跟库里的string发生冲突

需要对类和对象的相关知识有一定的了解,可翻阅我之前的文章

1. string成员变量

有点类似顺序表的实现(可看这篇文章),有一个size(数组大小)和capacity(数组容量),而string的本质是字符数组,有char*来记录数据

  1. namespace myString
  2. {
  3. class string
  4. {
  5. public:
  6. typedef char* iterator;
  7. typedef const char* const_iterator;
  8. // 提供接口来访问第一个元素和最后一个元素的下一位
  9. iterator begin()
  10. {
  11. return _str;
  12. }
  13. const_iterator begin() const
  14. {
  15. return _str;
  16. }
  17. iterator end()
  18. {
  19. return _str + _size;
  20. }
  21. const_iterator end() const
  22. {
  23. return _str + _size;
  24. }
  25. const_iterator begin() const
  26. {
  27. return _str;
  28. }
  29. const_iterator end() const
  30. {
  31. return _str + _size;
  32. }
  33. // 获取大小和容量的接口
  34. // 这里要用const修饰成员函数,保证const对象也可以调用
  35. size_t size() const
  36. {
  37. return _size;
  38. }
  39. size_t capacity() const
  40. {
  41. return _capacity;
  42. }
  43. bool empty() const
  44. {
  45. return _size == 0;
  46. }
  47. //获取C类型的字符串
  48. const char* c_str() const
  49. {
  50. return _str;
  51. }
  52. private:
  53. char* _str = nullptr;
  54. size_t _size = 0;
  55. size_t _capacity = 0;
  56. public:
  57. static const size_t npos;
  58. };
  59. }

【补充】这里的npos一般赋值为-1,用来表示字符串的末尾,因为-1在无符号整型中为2^32-1,大小约为4G,所以一般不会达到,所以用来表示末尾,通常作缺省值使用。或者在查找不到需要的字符(字符串)时作为返回值表示查找失败

2. 构造和析构

(1)构造函数

  1. string(const char* str = "")
  2. :_size = strlen(str)
  3. {
  4. _capacity = _size;
  5. _str = new char[_capacity + 1];
  6. strcpy(_str, str);
  7. }

(2)拷贝构造

注意要新开一块空间再将数据拷贝过来,否则两个指针指向同一块空间时,当其中一个发生改变另一个也会变,而且在析构时同一块空间析构两次会引发程序的崩溃,而这也就是我们常说的浅拷贝

深拷贝就是先开一块新的空间,再将数据依次拷过去,可以有效地避免上述情况的发生。

  1. // 版本一
  2. string(const string& s)
  3. {
  4. _str = new char[s.capacity() + 1];
  5. strcpy(_str, s._str);
  6. _size = s._size;
  7. _capacity = s._capacity;
  8. }

版本二复用了(1)构造函数,同时写了一个swap函数

Q:有小伙伴就问了,为什么不直接用库里的swap呢?

可以看到这里要调用一次拷贝构造,两次赋值,还得调用析构把c析构掉

代价太大,所以我们不如自己实现一个

这里要用std限定住范围是因为swap会先从就近的swap开始找,但是由于参数个数不匹配,所以就会报错。 

  1. // 版本二
  2. void swap(string& s)
  3. {
  4. std::swap(_str, s._str);
  5. std::swap(_size, s._size);
  6. std::swap(_capacity, s._capacity);
  7. }
  8. string(const string& s)
  9. {
  10. string tmp(s._str);
  11. swap(tmp);
  12. }

(3)析构函数

  1. ~string()
  2. {
  3. delete[] _str;
  4. _str = nullptr;
  5. _size = _capacity = 0;
  6. }

3. reserve和resize

【区分reserve和resize】

reserve:只是开空间,

  1. void reserve(size_t n)
  2. {
  3. if(n > _capacity)
  4. {
  5. char* tmp = new char[n + 1];
  6. strcpy(tmp, _str);
  7. delete[] _str;
  8. _str = tmp;
  9. _capacity = n;
  10. }
  11. }
  12. void resize(size_t n, char c = '\0')
  13. {
  14. if(n <= _size)
  15. {
  16. _str[n] = '\0';
  17. _size = n;
  18. }
  19. else
  20. {
  21. reserve(n);
  22. for(size_t i = _size; i < n; i++)
  23. _str[i] = c;
  24. _str[n] = '\0';
  25. _size = n;
  26. }
  27. }

4. find

查找不需要修改对应的成员函数,所以使用const成员函数

strstr返回的就是在某个位置之后找某个对应的子串,返回首元素的地址

  1. // 返回c在string中第一次出现的位置
  2. size_t find (char c, size_t pos = 0) const
  3. {
  4. assert(pos < _size);
  5. for(size_t i = pos; i < _size; i++)
  6. {
  7. if(_str[i] == c)
  8. return i;
  9. }
  10. return npos;
  11. }
  12. // 返回子串s在string中第一次出现的位置
  13. size_t find (const char* s, size_t pos = 0) const
  14. {
  15. assert(pos < _size);
  16. const char* p = strstr(_str + pos, s);
  17. if(p)
  18. return p - _str; // 指针减指针得两者之间的元素个数
  19. else
  20. return npos;
  21. }

5. insert和erase

  1. void insert(size_t pos, char c)
  2. {
  3. if(_size == _capacity)
  4. {
  5. reserve(_capacity == 0 ? 4 : _capacity * 2);
  6. }
  7. int end = _size + 1;
  8. while(end > pos)
  9. {
  10. _str[end] = _str[end - 1];
  11. end--;
  12. }
  13. _str[pos] = c;
  14. _size++;
  15. }
  16. void insert(size_t pos, const char* str)
  17. {
  18. assert(pos <= _size);
  19. size_t len = strlen(str);
  20. if(_size + len > _capacity)
  21. reserve(_size + len);
  22. size_t end = _size + len;
  23. while(end > pos + len - 1)
  24. {
  25. _str[end] = _str[end - len];
  26. end--;
  27. }
  28. strncpy(_str + pos, str, len);
  29. _size += len;
  30. }
  31. // 删除pos位置上的元素,并返回该元素的下一个位置
  32. void erase(size_t pos, size_t len = npos)
  33. {
  34. aasert(pos < _size);
  35. if(len == npos || pos + len >= _size)
  36. {
  37. _str[pos] = '\0';
  38. _size = pos;
  39. }
  40. else
  41. {
  42. strcpy(_str + pos, _str + pos + len);
  43. _size -= len;
  44. }
  45. }

6. push_back和append

老思路,扩容扩二倍;记得加回 '\0'

  1. void push_back(char c)
  2. {
  3. if (_size == _capacity)
  4. reserve(_capacity == 0 ? 4 : _capacity * 2);
  5. _str[_size] = c;
  6. _str[++_size] = '\0';
  7. }

【版本二】复用insert

  1. void push_back(char c)
  2. {
  3. insert(_size, c);
  4. }

append:不要忘记扩容!!因为传进来的是c类型字符串,所以直接使用strlen

  1. void append(const char* str)
  2. {
  3. size_t len = strlen(str);
  4. if (_size + len > _capacity)
  5. reserve(_size + len);
  6. strcpy(_str + _size, str);
  7. _size += len;
  8. }

7. 重载

(1)方括号重载

  1. char& operator[](size_t index)
  2. {
  3. assert(index < _size);
  4. return _str[index];
  5. }
  6. const char& operator[](size_t index) const
  7. {
  8. assert(index < _size);
  9. return _str[index];
  10. }

(2)比较大小

注意strcmp函数是c语言的接口,需要转成const char*才能传参

  1. bool operator==(const string& s1, const string& s2)
  2. {
  3. int ret = strcmp(s1.c_str(), s2.c_str());
  4. return ret == 0;
  5. }
  6. bool operator<(const string& s1, const string& s2)
  7. {
  8. int ret = strcmp(s1.c_str(), s2.c_str());
  9. return ret < 0;
  10. }
  11. bool operator<=(const string& s1, const string& s2)
  12. {
  13. return s1 < s2 || s1 == s2;
  14. }
  15. bool operator>(const string& s1, const string& s2)
  16. {
  17. return !(s1 <= s2);
  18. }
  19. bool operator>=(const string& s1, const string& s2)
  20. {
  21. return !(s1 < s2);
  22. }
  23. bool operator!=(const string& s1, const string& s2)
  24. {
  25. return !(s1 == s2);
  26. }

(3)流提取和流插入

【注意点】

a. 范围for的底层是迭代器,所以需要先定义迭代器 const begin() 和 const end() 之后才能使用

b. 这里的流提取采用了“缓冲数组”的思路,避免了开辟空间不够用的问题

  1. ostream& operator<<(ostream& _cout, const string& s)
  2. {
  3. for (auto e : s)
  4. {
  5. _cout << e;
  6. }
  7. return _cout;
  8. }
  9. istream& operator>>(istream& _cin, bit::string& s)
  10. {
  11. s.clear();
  12. char buff[128];
  13. char ch = _cin.get();
  14. size_t i = 0;
  15. while (ch != ' ' && ch != '\n')
  16. {
  17. buff[i++] = ch;
  18. if (i == 127)
  19. {
  20. buff[127] = '\0';
  21. s += buff;
  22. i = 0;
  23. }
  24. ch = _cin.get();
  25. }
  26. if (i > 0)
  27. {
  28. buff[i] = '\0';
  29. s += buff;
  30. }
  31. return _cin;
  32. }
  33. // 获取一行信息(可包含空格)
  34. istream& getline(istream& _cin, string& s)
  35. {
  36. s.clear();
  37. char ch = _cin.get();
  38. while (ch != '\n')
  39. {
  40. s += ch;
  41. ch = _cin.get();
  42. }
  43. return _cin;
  44. }

(4)尾插

直接复用push_back和append

  1. string& operator+=(char c)
  2. {
  3. push_back(c);
  4. return *this;
  5. }
  6. string& operator+=(const char* str)
  7. {
  8. append(str);
  9. return *this;
  10. }

(5)赋值运算符

【版本一】

  1. string& operator=(const string& s)
  2. {
  3. char* tmp = new char[s._capacity + 1];
  4. strcpy(tmp, s._str);
  5. delete[] _str;
  6. _str = tmp;
  7. _size = s._size;
  8. _capacity = s._capacity;
  9. return *this;
  10. }

【版本二】 

传参时,发生拷贝构造,所以交换之后不对原来的对象产生影响

  1. string& operator=(string s)
  2. {
  3. swap(s);
  4. return *this; //返回左值(支持连续赋值)
  5. }

 

8. clear

  1. void clear()
  2. {
  3. _str[0] = '\0';
  4. _size = 0;
  5. }

【代码汇总】

  1. namespace myString
  2. {
  3. class string
  4. {
  5. friend ostream& operator<<(ostream& _cout, const string& s);
  6. friend istream& operator>>(istream& _cin, string& s);
  7. public:
  8. typedef char* iterator;
  9. typedef const char* const_iterator;
  10. public:
  11. string(const char* str = "")
  12. :_size(strlen(str))
  13. {
  14. _capacity = _size;
  15. _str = new char[_capacity + 1];
  16. strcpy(_str, str);
  17. }
  18. string(const string& s)
  19. {
  20. _str = new char[s.capacity() + 1];
  21. strcpy(_str, s._str);
  22. _size = s._size;
  23. _capacity = s._capacity;
  24. }
  25. string& operator=(const string& s)
  26. {
  27. char* tmp = new char[s._capacity + 1];
  28. strcpy(tmp, s._str);
  29. delete[] _str;
  30. _str = tmp;
  31. _size = s._size;
  32. _capacity = s._capacity;
  33. return *this;
  34. }
  35. ~string()
  36. {
  37. delete[] _str;
  38. _str = nullptr;
  39. _size = _capacity = 0;
  40. }
  41. //
  42. // iterator
  43. iterator begin()
  44. {
  45. return _str;
  46. }
  47. const_iterator begin() const
  48. {
  49. return _str;
  50. }
  51. iterator end()
  52. {
  53. return _str + _size;
  54. }
  55. const_iterator end() const
  56. {
  57. return _str + _size;
  58. }
  59. /
  60. // modify
  61. void push_back(char c)
  62. {
  63. if (_size == _capacity)
  64. reserve(_capacity == 0 ? 4 : _capacity * 2);
  65. _str[_size] = c;
  66. _str[++_size] = '\0';
  67. }
  68. string& operator+=(char c)
  69. {
  70. push_back(c);
  71. return *this;
  72. }
  73. void append(const char* str)
  74. {
  75. size_t len = strlen(str);
  76. if (_size + len > _capacity)
  77. reserve(_size + len);
  78. strcpy(_str + _size, str);
  79. _size += len;
  80. }
  81. string& operator+=(const char* str)
  82. {
  83. append(str);
  84. return *this;
  85. }
  86. void clear()
  87. {
  88. _str[0] = '\0';
  89. _size = 0;
  90. }
  91. void swap(string& s)
  92. {
  93. std::swap(_str, s._str);
  94. std::swap(_size, s._size);
  95. std::swap(_capacity, s._capacity);
  96. }
  97. const char* c_str() const
  98. {
  99. return _str;
  100. }
  101. /
  102. // capacity
  103. size_t size()const
  104. {
  105. return _size;
  106. }
  107. size_t capacity()const
  108. {
  109. return _capacity;
  110. }
  111. bool empty()const
  112. {
  113. return _size == 0;
  114. }
  115. void resize(size_t n, char c = '\0')
  116. {
  117. if (n <= _size)
  118. {
  119. _str[n] = '\0';
  120. _size = n;
  121. }
  122. else
  123. {
  124. reserve(n);
  125. for (size_t i = _size; i < n; i++)
  126. _str[i] = c;
  127. _str[n] = '\0';
  128. _size = n;
  129. }
  130. }
  131. void reserve(size_t n)
  132. {
  133. if (n > _capacity)
  134. {
  135. char* tmp = new char[n + 1];
  136. strcpy(tmp, _str);
  137. delete[] _str;
  138. _str = tmp;
  139. _capacity = n;
  140. }
  141. }
  142. /
  143. // access
  144. char& operator[](size_t index)
  145. {
  146. assert(index < _size);
  147. return _str[index];
  148. }
  149. const char& operator[](size_t index) const
  150. {
  151. assert(index < _size);
  152. return _str[index];
  153. }
  154. /
  155. //relational operators
  156. // 返回c在string中第一次出现的位置
  157. size_t find(char c, size_t pos = 0) const
  158. {
  159. assert(pos < _size);
  160. for (size_t i = pos; i < _size; i++)
  161. {
  162. if (_str[i] == c)
  163. return i;
  164. }
  165. return npos;
  166. }
  167. // 返回子串s在string中第一次出现的位置
  168. size_t find(const char* s, size_t pos = 0) const
  169. {
  170. assert(pos < _size);
  171. const char* p = strstr(_str + pos, s);
  172. if (p)
  173. return p - _str;
  174. else
  175. return npos;
  176. }
  177. // 在pos位置上插入字符c/字符串str,并返回该字符的位置
  178. void insert(size_t pos, char c)
  179. {
  180. if (_size == _capacity)
  181. {
  182. reserve(_capacity == 0 ? 4 : _capacity * 2);
  183. }
  184. int end = _size + 1;
  185. while (end > pos)
  186. {
  187. _str[end] = _str[end - 1];
  188. end--;
  189. }
  190. _str[pos] = c;
  191. _size++;
  192. }
  193. void insert(size_t pos, const char* str)
  194. {
  195. assert(pos <= _size);
  196. size_t len = strlen(str);
  197. if (_size + len > _capacity)
  198. reserve(_size + len);
  199. size_t end = _size + len;
  200. while (end > pos + len - 1)
  201. {
  202. _str[end] = _str[end - len];
  203. end--;
  204. }
  205. strncpy(_str + pos, str, len);
  206. _size += len;
  207. }
  208. // 删除pos位置上的元素,并返回该元素的下一个位置
  209. void erase(size_t pos, size_t len = npos)
  210. {
  211. assert(pos < _size);
  212. if (len == npos || pos + len >= _size)
  213. {
  214. _str[pos] = '\0';
  215. _size = pos;
  216. }
  217. else
  218. {
  219. strcpy(_str + pos, _str + pos + len);
  220. _size -= len;
  221. }
  222. }
  223. private:
  224. char* _str;
  225. size_t _capacity;
  226. size_t _size;
  227. public:
  228. static const size_t npos;
  229. };
  230. const size_t string::npos = -1;
  231. void swap(string& x, string& y)
  232. {
  233. x.swap(y);
  234. }
  235. bool operator<(const string& s1, const string& s2)
  236. {
  237. int ret = strcmp(s1.c_str(), s2.c_str());
  238. return ret < 0;
  239. }
  240. bool operator==(const string& s1, const string& s2)
  241. {
  242. int ret = strcmp(s1.c_str(), s2.c_str());
  243. return ret == 0;
  244. }
  245. bool operator<=(const string& s1, const string& s2)
  246. {
  247. return s1 < s2 || s1 == s2;
  248. }
  249. bool operator>(const string& s1, const string& s2)
  250. {
  251. return !(s1 <= s2);
  252. }
  253. bool operator>=(const string& s1, const string& s2)
  254. {
  255. return !(s1 < s2);
  256. }
  257. bool operator!=(const string& s1, const string& s2)
  258. {
  259. return !(s1 == s2);
  260. }
  261. ostream& operator<<(ostream& _cout, const string& s)
  262. {
  263. for (auto e : s)
  264. {
  265. _cout << e;
  266. }
  267. return _cout;
  268. }
  269. istream& operator>>(istream& _cin, string& s)
  270. {
  271. s.clear();
  272. char buff[128];
  273. char ch = _cin.get();
  274. size_t i = 0;
  275. while (ch != ' ' && ch != '\n')
  276. {
  277. buff[i++] = ch;
  278. if (i == 127)
  279. {
  280. buff[127] = '\0';
  281. s += buff;
  282. i = 0;
  283. }
  284. ch = _cin.get();
  285. }
  286. if (i > 0)
  287. {
  288. buff[i] = '\0';
  289. s += buff;
  290. }
  291. return _cin;
  292. }
  293. istream& getline(istream& _cin, string& s)
  294. {
  295. s.clear();
  296. char ch = _cin.get();
  297. while (ch != '\n')
  298. {
  299. s += ch;
  300. ch = _cin.get();
  301. }
  302. return _cin;
  303. }
  304. };

 

如果你能看到这里,给你点个赞!

如果觉得这篇文章不错的话,不妨点个赞支持一下,你的支持是我持续更新的动力~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/479024
推荐阅读
相关标签
  

闽ICP备14008679号