当前位置:   article > 正文

[C++初阶]string类的详解

[C++初阶]string类的详解

一、string类的模拟实现

        上面已经对string类进行了简单的介绍,大家只要能够正常使用即可。在面试中,面试官总喜欢让我们来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。大家看下以下string类的实现是否有问题?
头文件:string.h
  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #pragma once
  3. #include<iostream>
  4. #include<assert.h>
  5. using namespace std;
  6. namespace STring
  7. {
  8. class string
  9. {
  10. public:
  11. typedef char* iterator;
  12. typedef const char* const_iterator;
  13. iterator begin();
  14. iterator end();
  15. const_iterator begin() const;
  16. const_iterator end() const;
  17. string(const char* str = "");
  18. string(const string& s);
  19. string& operator=(const string& s);
  20. ~string();
  21. const char* c_str() const;
  22. size_t size() const;
  23. char& operator[](size_t pos);
  24. const char& operator[](size_t pos) const;
  25. void reserve(size_t n);
  26. void push_back(char ch);
  27. void append(const char* str);
  28. string& operator+=(char ch);
  29. string& operator+=(const char* str);
  30. void insert(size_t pos, char ch);
  31. void insert(size_t pos, const char* str);
  32. void erase(size_t pos = 0, size_t len = npos);
  33. size_t find(char ch, size_t pos = 0);
  34. size_t find(const char* str, size_t pos = 0);
  35. void swap(string& s);
  36. string substr(size_t pos = 0, size_t len = npos);
  37. bool operator<(const string& s) const;
  38. bool operator>(const string& s) const;
  39. bool operator<=(const string& s) const;
  40. bool operator>=(const string& s) const;
  41. bool operator==(const string& s) const;
  42. bool operator!=(const string& s) const;
  43. void clear();
  44. private:
  45. char* _str;
  46. size_t _size;
  47. size_t _capacity;
  48. const static size_t npos;
  49. };
  50. }

//string.cpp

  1. #include"string.h"
  2. namespace STring
  3. {
  4. const size_t string::npos = -1;
  5. string::iterator string::begin()
  6. {
  7. return _str;
  8. }
  9. string::const_iterator string::begin() const
  10. {
  11. return _str;
  12. }
  13. string::iterator string::end()
  14. {
  15. return _str + _size;
  16. }
  17. string::const_iterator string::end() const
  18. {
  19. return _str+_size;
  20. }
  21. string:: string(const char* str)
  22. :_size(strlen(str))
  23. {
  24. _str = new char[_size+1];
  25. _capacity = _size+1;
  26. strcpy(_str, str);
  27. }
  28. string::string(const string& s)
  29. {
  30. _str = new char[s._capacity];
  31. strcpy(_str, s._str);
  32. _size = s._size;
  33. _capacity = s._capacity;
  34. }
  35. string::~string()
  36. {
  37. delete[] _str;
  38. _str = nullptr;
  39. _size = _capacity = 0;
  40. }
  41. const char* string::c_str() const
  42. {
  43. return _str;
  44. }
  45. size_t string :: size() const
  46. {
  47. return _size;
  48. }
  49. void string::reserve(size_t n)
  50. {
  51. if (n > _capacity)
  52. {
  53. char* tmp = new char[n + 1];
  54. strcpy(tmp, _str);
  55. delete[] _str;
  56. _str = tmp;
  57. _capacity = n;
  58. }
  59. }
  60. void string::push_back(char ch)
  61. {
  62. if (_size == _capacity)
  63. {
  64. size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
  65. reserve(newcapacity);
  66. }
  67. _str[_size] = ch;
  68. _str[_size + 1] = '\0';
  69. ++_size;
  70. }
  71. void string::append(const char* str)
  72. {
  73. size_t len = strlen(str);
  74. if (_size + len > _capacity)
  75. {
  76. reserve(_size + len);
  77. }
  78. strcpy(_str + _size, _str);
  79. _size += len;
  80. insert(_size, _str);
  81. }
  82. void string::insert(size_t pos, char ch)
  83. {
  84. assert(pos <= _size);
  85. if (_size = _capacity)
  86. {
  87. size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
  88. reserve(newcapacity);
  89. }
  90. size_t end = _size + 1;
  91. while (end > pos)
  92. {
  93. _str[end] = _str[end - 1] + 1;
  94. --end;
  95. }
  96. _str[pos] = ch;
  97. ++_size;
  98. }
  99. void string::insert(size_t pos, const char* str)
  100. {
  101. assert(pos <= _size);
  102. int len = strlen(str);
  103. if (_size + len > _capacity)
  104. {
  105. reserve(_capacity+len);
  106. }
  107. size_t end = _size + len;
  108. while (end > pos+len+1)
  109. {
  110. _str[end] = _str[end - len];
  111. --end;
  112. }
  113. memcpy(_str+pos,str,len);
  114. _size += len;
  115. }
  116. void string::erase(size_t pos, size_t len)
  117. {
  118. assert(pos < _size);
  119. if (len >= _size - pos)
  120. {
  121. _str[pos] = '\0';
  122. _size = len;
  123. }
  124. else
  125. {
  126. strcpy(_str + pos, _str + pos + len);
  127. _size = len;
  128. }
  129. }
  130. size_t string::find(char ch, size_t pos)
  131. {
  132. for (size_t i = pos; i < _size; i++)
  133. {
  134. if (_str[i] == ch)
  135. {
  136. return i;
  137. }
  138. }
  139. return npos;
  140. }
  141. size_t string::find(const char* str, size_t pos)
  142. {
  143. char* p = strstr(_str + pos, str);
  144. return p - _str;
  145. }
  146. void string::swap(string& s)
  147. {
  148. std::swap(_str, s._str);
  149. std::swap(_size, s._size);
  150. std::swap(_capacity, s._capacity);
  151. }
  152. string string::substr(size_t pos, size_t len)
  153. {
  154. if (len > _size - pos)
  155. {
  156. string sub(_str + pos);
  157. return sub;
  158. }
  159. else
  160. {
  161. string sub;
  162. sub.reserve(len);
  163. for (size_t i = 0; i < len; i++)
  164. {
  165. sub += _str[i + pos];
  166. }
  167. return sub;
  168. }
  169. }
  170. string& string:: operator=(const string& s)
  171. {
  172. if (this != &s)
  173. {
  174. char* tmp = new char[s._capacity];
  175. strcpy(tmp, s._str);
  176. delete[] _str;
  177. _str = tmp;
  178. _size = s._size;
  179. _capacity = s._capacity;
  180. }
  181. return *this;
  182. }
  183. char& string::operator[](size_t pos)
  184. {
  185. if (pos < _size)
  186. return _str[pos];
  187. }
  188. const char& string::operator[](size_t pos) const
  189. {
  190. if (pos < _size)
  191. return _str[pos];
  192. }
  193. string& string::operator+=(char ch)
  194. {
  195. push_back(ch);
  196. return *this;
  197. }
  198. string& string::operator+=(const char* str)
  199. {
  200. append(str);
  201. return *this;
  202. }
  203. bool string::operator<(const string& s) const
  204. {
  205. return strcmp(_str, s._str) < 0;
  206. }
  207. bool string::operator>(const string& s) const
  208. {
  209. return !(*this <= s);
  210. }
  211. bool string::operator<=(const string& s) const
  212. {
  213. return *this < s || *this == s;
  214. }
  215. bool string::operator>=(const string& s) const
  216. {
  217. return !(*this < s);
  218. }
  219. bool string::operator==(const string& s) const
  220. {
  221. return strcmp(_str, s._str) == 0;
  222. }
  223. bool string::operator!=(const string& s) const
  224. {
  225. return !(*this == s);
  226. }
  227. void string::clear()
  228. {
  229. _str[0] = '\0';
  230. _size = 0;
  231. }
  232. istream& operator>> (istream& is, string& str)
  233. {
  234. str.clear();
  235. char ch = is.get();
  236. while (ch != ' ' && ch != '\n')
  237. {
  238. str += ch;
  239. ch = is.get();
  240. }
  241. return is;
  242. }
  243. ostream& operator<< (ostream& os, const string& str)
  244. {
  245. for (size_t i = 0; i < str.size(); i++)
  246. {
  247. os << str[i];
  248. }
  249. return os;
  250. }
  251. }

现在我们看到的是,我这边写的,接下来我将会对于这些内容进行解析,首先我这边的模拟实现都是基于理解和string。   

1.对于命名空间的定义

  1. namespace STring
  2. {
  3. }

2.string类的定义和迭代器

  1. class string
  2. {
  3. public:
  4. private:
  5. }

1)私有成员

这里的内容比较少。

  1. char* _str;
  2. size_t _size;
  3. size_t _capacity;
  4. const static size_t npos;

2)公有成员

  1. typedef char* iterator;
  2. typedef const char* const_iterator;
  3. iterator begin();
  4. iterator end();
  5. const_iterator begin() const;
  6. const_iterator end() const;
  7. //string();
  8. string(const char* str = "");
  9. string(const string& s);
  10. string& operator=(const string& s);
  11. ~string();
  12. const char* c_str() const;
  13. size_t size() const;
  14. char& operator[](size_t pos);
  15. const char& operator[](size_t pos) const;
  16. void reserve(size_t n);
  17. void push_back(char ch);
  18. void append(const char* str);
  19. string& operator+=(char ch);
  20. string& operator+=(const char *str);
  21. void insert(size_t pos, char ch);
  22. void insert(size_t pos, const char* str);
  23. void erase(size_t pos = 0, size_t len = npos);
  24. size_t find(char ch, size_t pos = 0);
  25. size_t find(const char* str, size_t pos = 0);
  26. void swap(string& s);
  27. string substr(size_t pos = 0, size_t len = npos);
  28. bool operator<(const string& s) const;
  29. bool operator>(const string& s) const;
  30. bool operator<=(const string& s) const;
  31. bool operator>=(const string& s) const;
  32. bool operator==(const string& s) const;
  33. bool operator!=(const string& s) const;
  34. void clear();

3)迭代器

我们都知道在string中我们常常使用迭代器来完成不少操作,我们知道迭代器其实就是一种指针,所以这里我们选择这样实现

  1. typedef char* iterator;
  2. typedef const char* const_iterator;
  3. iterator begin();
  4. iterator end();
  5. const_iterator begin() const;
  6. const_iterator end() const;

3.构造函数

声明:

string(const char* str = "");

这里我们选择用缺省函数保证字符串内容

下面是搞函数的内部实现:

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

首先我们用列表初始化得出存储的字符串大小,然后给字符串开辟空间,然后把求出空间(注意:字符串最后有'\0')然后通过拷贝函数把str字符串把内容拷贝给_str存储。

4.拷贝构造函数

声明:

string(const string& s);

函数内部实现:

  1. string::string(const string& s)
  2. {
  3. _str = new char[s._capacity];
  4. strcpy(_str, s._str);
  5. _size = s._size;
  6. _capacity = s._capacity;
  7. }

思路具体和上面差不多,开辟新空间,然后把字符串拷入,把大小和空间也拷贝。

5.析构函数

声明:

~string();

这里比较简单,就是把开辟的空间释放然后大小空间归零

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

6.获取字符串和存储的字符串大小

声明:

  1. const char* c_str() const;
  2. size_t size() const;

这个直接返回相应的值

  1. const char* string::c_str() const
  2. {
  3. return _str;
  4. }
  5. size_t string :: size() const
  6. {
  7. return _size;
  8. }

7.reserve

声明:

void reserve(size_t n);

函数内部:

  1. void string::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. }

这个函数(这里我们是大致模拟VS的)我们之前讲过如果n>_capacity就会扩容,否则无反应。

8.insert

声明:

  1. void insert(size_t pos, char ch);
  2. void insert(size_t pos, const char* str);

因为我们会用inset插入字符和字符串,所以这里我们定义两种

函数内部:

  1. void string::insert(size_t pos, char ch)
  2. {
  3. assert(pos <= _size);
  4. if (_size = _capacity)
  5. {
  6. size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
  7. reserve(newcapacity);
  8. }
  9. size_t end = _size + 1;
  10. while (end > pos)
  11. {
  12. _str[end] = _str[end - 1] + 1;
  13. --end;
  14. }
  15. _str[pos] = ch;
  16. ++_size;
  17. }
  18. void string::insert(size_t pos, const char* str)
  19. {
  20. assert(pos <= _size);
  21. int len = strlen(str);
  22. if (_size + len > _capacity)
  23. {
  24. reserve(_capacity+len);
  25. }
  26. size_t end = _size + len;
  27. while (end > pos+len+1)
  28. {
  29. _str[end] = _str[end - len];
  30. --end;
  31. }
  32. memcpy(_str+pos,str,len);
  33. _size += len;
  34. }

这里的实现和我们以前写顺序表的插入差不多,把目标位置以及之后的字符串后移动,知道空出需要的空间大小,然后插入。

9.push_back和append

声明:

  1. void push_back(char ch);
  2. void append(const char* str);

着两个一个是尾插入,一个是插入字符串

  1. void string::push_back(char ch)
  2. {
  3. if (_size == _capacity)
  4. {
  5. size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
  6. reserve(newcapacity);
  7. }
  8. _str[_size] = ch;
  9. _str[_size + 1] = '\0';
  10. ++_size;
  11. }
  12. void string::append(const char* str)
  13. {
  14. size_t len = strlen(str);
  15. if (_size + len > _capacity)
  16. {
  17. reserve(_size + len);
  18. }
  19. strcpy(_str + _size, _str);
  20. _size += len;
  21. insert(_size, _str);
  22. }

这个就是正常尾插的实现,但是append是字符串,为了方便我们可以直接调用insert

10.erase

声明:

void erase(size_t pos = 0, size_t len = npos);

这是声明我直接仿照的cplusplus的定义

注意我们需要

const size_t string::npos = -1;

函数内部:

  1. void string::erase(size_t pos, size_t len)
  2. {
  3. assert(pos < _size);
  4. if (len >= _size - pos)
  5. {
  6. _str[pos] = '\0';
  7. _size = len;
  8. }
  9. else
  10. {
  11. strcpy(_str + pos, _str + pos + len);
  12. _size = len;
  13. }
  14. }

这里很简单就是把pos后删除,我们考虑两种情况,一种是直接删除,一种是删除部分

11.find

声明:

  1. size_t find(char ch, size_t pos = 0);
  2. size_t find(const char* str, size_t pos = 0);

这个函数真正的底层实现很难,这里因为我们只是模拟实现,我们可以这样子解决

函数内部:

  1. size_t string::find(char ch, size_t pos)
  2. {
  3. for (size_t i = pos; i < _size; i++)
  4. {
  5. if (_str[i] == ch)
  6. {
  7. return i;
  8. }
  9. }
  10. return npos;
  11. }
  12. size_t string::find(const char* str, size_t pos)
  13. {
  14. char* p = strstr(_str + pos, str);
  15. return p - _str;
  16. }

12.swap

声明:

	void swap(string& s);

函数内部:

  1. void string::swap(string& s)
  2. {
  3. std::swap(_str, s._str);
  4. std::swap(_size, s._size);
  5. std::swap(_capacity, s._capacity);
  6. }

很简单的内容,没什么好说的

13.substr

这个就是很简单的一个函数

声明:

string substr(size_t pos = 0, size_t len = npos);

函数内部:

  1. string string::substr(size_t pos, size_t len)
  2. {
  3. if (len > _size - pos)
  4. {
  5. string sub(_str + pos);
  6. return sub;
  7. }
  8. else
  9. {
  10. string sub;
  11. sub.reserve(len);
  12. for (size_t i = 0; i < len; i++)
  13. {
  14. sub += _str[i + pos];
  15. }
  16. return sub;
  17. }
  18. }

ps:上面我用了一些运算符重载,建议结合后面的运算符重载观看

14.运算符重载

这里是我所有使用的运算符重载以完成模拟实现

声明:

  1. string& operator=(const string& s);
  2. char& operator[](size_t pos);
  3. const char& operator[](size_t pos) const;
  4. string& operator+=(char ch);
  5. string& operator+=(const char *str);
  6. bool operator<(const string& s) const;
  7. bool operator>(const string& s) const;
  8. bool operator<=(const string& s) const;
  9. bool operator>=(const string& s) const;
  10. bool operator==(const string& s) const;
  11. bool operator!=(const string& s) const;

函数实现

  1. string& string:: operator=(const string& s)
  2. {
  3. if (this != &s)
  4. {
  5. char* tmp = new char[s._capacity];
  6. strcpy(tmp, s._str);
  7. delete[] _str;
  8. _str = tmp;
  9. _size = s._size;
  10. _capacity = s._capacity;
  11. }
  12. return *this;
  13. }
  14. char& string::operator[](size_t pos)
  15. {
  16. if (pos < _size)
  17. return _str[pos];
  18. }
  19. const char& string::operator[](size_t pos) const
  20. {
  21. if (pos < _size)
  22. return _str[pos];
  23. }
  24. string& string::operator+=(char ch)
  25. {
  26. push_back(ch);
  27. return *this;
  28. }
  29. string& string::operator+=(const char* str)
  30. {
  31. append(str);
  32. return *this;
  33. }
  34. bool string::operator<(const string& s) const
  35. {
  36. return strcmp(_str, s._str) < 0;
  37. }
  38. bool string::operator>(const string& s) const
  39. {
  40. return !(*this <= s);
  41. }
  42. bool string::operator<=(const string& s) const
  43. {
  44. return *this < s || *this == s;
  45. }
  46. bool string::operator>=(const string& s) const
  47. {
  48. return !(*this < s);
  49. }
  50. bool string::operator==(const string& s) const
  51. {
  52. return strcmp(_str, s._str) == 0;
  53. }
  54. bool string::operator!=(const string& s) const
  55. {
  56. return !(*this == s);
  57. }

=号是重载的拷贝构造

[]号是实现下标访问

+=是重载push_back和append

剩下的都是完成判断的

15.clear

声明:

void clear();

函数内部:

直接把_st[0]='\0',_size=0即可

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

16.输入输出流的重载

这里我们之前写日期类的时候应该写过,我就不多讲了,直接展示

声明:

  1. istream& operator>> (istream& is, string& str);
  2. ostream& operator<< (ostream& os, const string& str);

注意:不要声明在类中

函数内部:

  1. istream& operator>> (istream& is, string& str)
  2. {
  3. str.clear();
  4. char ch = is.get();
  5. while (ch != ' ' && ch != '\n')
  6. {
  7. str += ch;
  8. ch = is.get();
  9. }
  10. return is;
  11. }
  12. ostream& operator<< (ostream& os, const string& str)
  13. {
  14. for (size_t i = 0; i < str.size(); i++)
  15. {
  16. os << str[i];
  17. }
  18. return os;
  19. }

以上就是我们对于string类的模拟实现希望可以帮助大家更好的理解

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

闽ICP备14008679号