当前位置:   article > 正文

【C++】string类的模拟实现

【C++】string类的模拟实现

文章目录

一、string类的构造,拷贝构造,赋值重载以及析构

1.构造函数

2.拷贝构造

3.swap问题

4.赋值重载

5.析构函数

二.常用接口

1.c_str

2.operator[]

3.迭代器和范围for

4.size

三.插入

四.删除

五.查找

六.运算符重载

七.参考代码


string类的模拟实现我们定义三个成员变量

  1. class string
  2. {
  3. public:
  4. //...
  5. private:
  6. char* _str;
  7. size_t _size;
  8. size_t _capacity;
  9. };

下面对其中的成员函数进行依次的实现

一、string类的构造,拷贝构造,赋值重载以及析构

1.构造函数

构造函数分为无参带参这两种

无参构造函数默认构造空字符串"",所以我们只需要写一个带参的并给一个缺省值即可

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

注意:这里的_str开空间时要加1,是给\0预留位置

2.拷贝构造

对于string类型来说,如果不写拷贝构造会导致浅拷贝问题(只完成值拷贝)

所以我们需要进行深拷贝

这里先介绍一种传统写法,也是比较简单的写法

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

3.swap问题

在之前学习string的使用时,我们会发现明明std中就有swap,为什么string还要再实现一个?

这是因为第一个swap的交换代价比较大,它会进行深拷贝,造成空间损耗,所以我们在自己实现swap时要考虑到这一点,可以采用交换两者指针的方式:

  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. }

4.赋值重载

默认生成的赋值重载也会导致浅拷贝,所以我们需要实现深拷贝

这里我们也用传统写法,也是比较简单的写法

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

其实还有一种现代写法,是复用上面的拷贝构造,感兴趣的小伙伴可以自行先去了解哦

5.析构函数

析构函数比较简单,请看下面的代码:

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

下面我们来看几个常用的接口实现

二.常用接口

1.c_str

  1. const char* string::c_str() const
  2. {
  3.     return _str;
  4. }

2.operator[]

  1. char& string::operator[](size_t pos)//普通对象:可读可写
  2. {
  3. assert(pos < _size);
  4. return _str[pos];
  5. }

3.迭代器和范围for

迭代器

  1. typedef char* iterator;
  2. string::iterator string::begin()
  3. {
  4. return _str;
  5. }
  6. string::iterator string::end()
  7. {
  8. return _str + _size;
  9. }
  10. //遍历
  11. string::iterator it1 = s1.begin();
  12. while (it1 != s1.end())
  13. {
  14. cout << *it1 << " ";
  15. ++it1;
  16. }
  17. cout << endl;

范围for(底层还是迭代器)

  1. typedef char* iterator;
  2. string::iterator string::begin()
  3. {
  4. return _str;
  5. }
  6. string::iterator string::end()
  7. {
  8. return _str + _size;
  9. }
  10. //遍历
  11. for (auto e: s1)
  12. {
  13. cout << e << " ";
  14. }
  15. cout << endl;

打印结果如下:

4.size

  1. size_t size() const
  2. {
  3. return _size;
  4. }

三.插入

reserve

开辟新的空间,然后进行拷贝,对旧空间进行释放

  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. }

push_back

尾插一个字符,我们需要考虑扩容问题

同时,尾插之后’\0’要重新处理

  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. }

append

尾插字符串,这里我们需要计算,然后决定开多少空间(直接开2倍可能不够用)

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

+=

这个比较简单,我们可以复用上面的分为+=字符和+=字符串push_back和append

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

insert

insert的代码,我们要注意画图,尤其是关于边界的一些条件的处理,我们要小心

  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];//右移
  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. size_t len = strlen(str);
  22. if (_size + len > _capacity)
  23. {
  24. reserve(_size + 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. }

四.删除

说到erase,自然要跟npos联系起来,npos是string类的静态成员变量

我们可以采用定义和声明分离的方法:

const static size_t npos;//写在string.h中

const size_t string::npos = -1;//写在string.cpp中

下面来实现这个删除功能:

  1. void string::erase(size_t pos, size_t len)
  2. {
  3. assert(pos < _size);
  4. // len大于前面字符个数时,有多少删多少
  5. if (len >= _size - pos)
  6. {
  7. _str[pos] = '\0';
  8. _size = pos;
  9. }
  10. else
  11. {
  12. strcpy(_str + pos, _str + pos + len);
  13. _size -= len;
  14. }
  15. }

五.查找

从pos处开始查找字符或者字符串,找到返回下标值,没找到则返回npos

  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* sub, size_t pos)//字符串
  13. {
  14. char* p = strstr(_str + pos, sub);//调用现成的strstr来实现
  15. return p - _str;
  16. }

六.流插入<<和流提取>>

流插入(输出)和流提取(输入)我们之前在日期类的时候也实现过了,我们知道它不适合写成 成员函数,之前我们用的是友元,这样才能访问私有,但这里我们可以不访问私有,挨个挨个遍历,所以可以这么写:

流插入<<

  1. ostream& operator<< (ostream& os, const string& str)//流插入(输出)
  2. {
  3. for (size_t i = 0; i < str.size(); i++)
  4. {
  5. os << str[i];
  6. }
  7. return os;
  8. }

流提取>>

  1. istream& operator>> (istream& is, string& str)//流提取(输入)
  2. {
  3. char ch = is.get();
  4. while (ch != ' ' && ch != '\n')
  5. {
  6. str += ch;
  7. ch = is.get();
  8. }
  9. return is;
  10. }

但是这样还不够,我们会发现之前的数据它还留着:

所以我们需要再写个clear()来清理数据:

  1. void string::clear()
  2. {
  3. _str[0] = '\0';
  4. _size = 0;
  5. }
  6. istream& operator>> (istream& is, string& str)//流提取(输入)
  7. {
  8. str.clear();
  9. char ch = is.get();
  10. while (ch != ' ' && ch != '\n')
  11. {
  12. str += ch;
  13. ch = is.get();
  14. }
  15. return is;
  16. }

七.参考代码

这里模拟实现string类用了 string.h , string.cpp 和  test.cpp 三个文件来实现

这里给出string的各个函数定义和实现的代码

string.h

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #pragma once
  3. #include <iostream>
  4. #include <assert.h>
  5. #include <string>
  6. using namespace std;
  7. namespace bit
  8. {
  9. class string
  10. {
  11. public:
  12. typedef char* iterator;
  13. typedef const char* const_iterator;
  14. iterator begin();
  15. iterator end();
  16. const_iterator begin() const;
  17. const_iterator end() const;
  18. string(const char* str = "");
  19. ~string();
  20. const char* c_str() const;
  21. size_t size() const;
  22. char& operator[](size_t pos);
  23. const char& operator[](size_t pos) const;
  24. void push_back(char ch);
  25. void append(const char* str);
  26. void insert(size_t pos, char ch);
  27. void insert(size_t pos, const char* str);
  28. void reserve(size_t n);
  29. string& operator+=(char ch);
  30. string& operator+=(const char* str);
  31. void erase(size_t pos = 0, size_t len = npos);
  32. string(const string& s);
  33. string& operator=(const string& s);
  34. size_t find(char ch, size_t pos = 0);
  35. size_t find(const char* sub, size_t pos = 0);
  36. void swap(string& s);
  37. string substr(size_t pos = 0, size_t len = npos);
  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. bool operator!=(const string& s) const;
  44. void clear();
  45. private:
  46. char* _str;
  47. size_t _size;
  48. size_t _capacity;
  49. const static size_t npos;
  50. };
  51. istream& operator>> (istream& is, string& str);
  52. ostream& operator<< (ostream& os, const string& str);
  53. }

string.cpp

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

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

闽ICP备14008679号