• string


     

    目录

    六、STL简介

    (一)什么是STL

    (二)STL的版本

    (三)STL六大组件

    七、string

    (一)标准库中的string

     1、string类

    2、string常用的接口 

    1)string类对象的常见构造

     2)string类对象的容量操作

    3)string类对象的访问及遍历操作

    4)string类对象的修改操作

    5)string类非成员函数

    (二)string模拟实现

     1、浅拷贝

    2、深拷贝

    3、实现 


    六、STL简介

    (一)什么是STL

    STL(standard template libaray- 标准模板库 ) C++ 标准库的重要组成部分 ,不仅是一个可复用的组件库,而且 是一个包罗数据结构与算法的软件框架

    (二)STL的版本

    原始版本
    Alexander Stepanov Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使用。 HP 版本 -- 所有 STL 实现版本的始祖。
    P. J. 版本
    P. J. Plauger 开发,继承自 HP 版本,被 Windows Visual C++ 采用,不能公开或修改,缺陷:可读性比较低,符号命名比较怪异。
    RW 版本
    Rouge Wage 公司开发,继承自 HP 版本,被 C+ + Builder 采用,不能公开或修改,可读性一般。
    SGI 版本
    Silicon Graphics Computer Systems Inc 公司开发,继承自 HP 版 本。被 GCC(Linux) 采用,可移植性好,可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。我们后面学习 STL 要阅读部分源代码, 主要参考的就是这个版本。

    (三)STL六大组件

     学习STL的三个境界:能用,明理,能扩展

    七、string

    string严格来说不是STL,而是标准库。

    (一)标准库中的string

     1、string类
    1. 字符串是表示字符序列的类
    2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作
    单字节字符字符串的设计特性。
    3. string 类是使用 char( 即作为它的字符类型,使用它的默认 char_traits 和分配器类型 ( 关于模板的更多信
    息,请参阅 basic_string)
    4. string 类是 basic_string 模板类的一个实例,它使用 char 来实例化 basic_string 模板类,并用 char_traits
    allocator 作为 basic_string 的默认参数 ( 根于更多的模板信息请参考 basic_string)
    5. 注意,这个类独立于所使用的编码来处理字节 : 如果用来处理多字节或变长字符 ( UTF-8) 的序列,这个
    类的所有成员 ( 如长度或大小 ) 以及它的迭代器,将仍然按照字节 ( 而不是实际编码的字符 ) 来操作。
    2、string常用的接口 
    1)string类对象的常见构造

    以下是常用的构造方式

    1. string s1;
    2. string s2("123");
    3. string s3 = "123";
    4. string s4(s3);
     2)string类对象的容量操作
    函数名称作用
    size返回字符串有效长度
    length返回字符串有效长度
    capacity返回空间总大小
    empty若字符串为空返回true,不为空返回false
    clear清空有效字符
    reserve预留空间
    resize将字符串大小改为n
    1. #include
    2. int main()
    3. {
    4. string s = "C++";
    5. cout << s << endl;
    6. cout <<"s.size():"<< s.size() << endl;
    7. cout <<"s.length():"<< s.length() << endl;
    8. cout << "s.capacity():"<capacity() << endl;
    9. cout << "s.empty():"<empty() << endl;
    10. cout << "-----------------------------------------------------" << endl;
    11. s.resize(6);
    12. cout << "s.size():" << s.size() << endl;
    13. cout << "s.capacity():" << s.capacity() << endl;
    14. cout << "-----------------------------------------------------" << endl;
    15. s.reserve(16);
    16. cout << "s.capacity():" << s.capacity() << endl;
    17. cout << "-----------------------------------------------------" << endl;
    18. s.clear();
    19. cout << "s.empty():" << s.empty() << endl;
    20. return 0;
    21. }

    string 容量相关方法使用代码演示
    注意:
    1. size() length() 方法底层实现原理完全相同,引入 size()的原因是为了与其他容器的接口保持一
    致,一般情况下基本都是用 size()
    2. clear() 只是将 string 中有效字符清空,不改变底层空间大小。
    3. resize(size_t n) resize(size_t n, char c) 都是将字符串中有效字符个数改变到 n 个,不同的是当字符个数增多时:resize(n) 0 来填充多出的元素空间, resize(size_t n, char c) 用字符 c 来填充多出的元素空间。注意:resize 在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大
    小,如果是将元素个数减少,底层空间总大小不变。
    4. reserve(size_t res_arg=0) :为 string 预留空间,不改变有效元素个数,当 reserve 的参数小于
    string 的底层空间总大小时, reserver不会改变容量大小
    3)string类对象的访问及遍历操作
    函数名称作用
    operator[]
    返回 pos 位置的字符, const string 类对象调用
    begin+end返回首元素地址+返回最后一个元素的下一个位置的地址
    rbegin+rend返回最后一个元素的地址+返回第一个元素的前一个位置地址
    范围forfor的新遍历方式(C++11)
    1. string s = "asdfghjkl";
    2. string::iterator it = s.begin();
    3. while (it != s.end())
    4. {
    5. cout << *it ;
    6. it++;
    7. }
    8. cout << endl;

    这就是正向迭代器,其中string::iterator也可以用auto

    1. string s = "asdfghjkl";
    2. auto it = s.begin();
    3. while (it != s.end())
    4. {
    5. cout << *it ;
    6. it++;
    7. }
    8. cout << endl;

     还有反向迭代器

    1. string s = "asdfghjkl";
    2. auto it = s.rbegin();
    3. while (it != s.rend())
    4. {
    5. cout << *it ;
    6. it++;
    7. }
    8. cout << endl;

    范围for(原理:编译器会将范围for变成迭代器)

    1. string s = "asdfghjkl";
    2. for (auto a : s)
    3. {
    4. cout << a;
    5. }
    6. cout << endl;
    4)string类对象的修改操作
    函数名称作用
    push_back在字符串尾部添加字符
    append在字符串后面追加一个字符串
    operator+=在字符串后面追加字符串str
    c_str返回C格式的字符串
    find+npos
    从字符串pos位置开始往后找字符,返回该字符在字符串中的位置
    rfind
    从字符串pos位置开始往前找字符,返回该字符在字符串中的位置
    substr
    在str中从pos位置开始,截取n个字符,然后将其返回
    insert插入字符或字符串
    erase删除字符或字符串
    1. int main()
    2. {
    3. string s = "hello ";
    4. cout << s << endl;
    5. cout << "------------------------------------------------" << endl;
    6. s.push_back('w');
    7. cout << s << endl;
    8. cout << "------------------------------------------------" << endl;
    9. s += "orld";
    10. cout << s << endl;
    11. cout << "------------------------------------------------" << endl;
    12. s.append(" /C++");
    13. cout << s << endl;
    14. cout << "------------------------------------------------" << endl;
    15. cout << s.find('h') << endl;
    16. cout << "------------------------------------------------" << endl;
    17. cout << s.substr(2, 5) << endl;
    18. cout << "------------------------------------------------" << endl;
    19. s.insert(0,1,'+');
    20. s.insert(s.begin(), '+');
    21. cout << s << endl;
    22. cout << "------------------------------------------------" << endl;
    23. s.erase(0, 2);
    24. cout << s << endl;
    25. cout << "------------------------------------------------" << endl;
    26. return 0;
    27. }

    注意,我们在文档时,substr是有缺省值的

     当我们不给截多少字符的时候,会给npos值,那么npos值是多少呢???

    是-1吗???

    文档中npos的类型是size_t,这是无符号整形,也就是说这里的-1并不真的是,而是42亿+

    到现在为止,我们并没有遇到过这么大的长度,所以可以理解为,截到字符串结束

    还有值得注意的是insert

    在插入一个字符的时候我们需要传三个参数,或者传迭代器也就是(6)

    1. s.insert(0,1,'+'); //(5)
    2. s.insert(s.begin(), '+'); //(6)

    当erasr没有第二个参数时,默认为删除到字符串最后

    5)string类非成员函数
    函数名称作用
    operator+字符串添加,不改变原来的字符串
    operator>>输入运算符重载
    operator<<输出运算符重载
    getline获取一行字符串
    relational operators比较大小
    1. #include
    2. int main()
    3. {
    4. string s = "hello ";
    5. cout << s << endl;
    6. cout << "------------------------------------------------" << endl;
    7. string s2 = s + "C++";
    8. cout << s << endl;
    9. cout << s2 << endl;
    10. cout << "------------------------------------------------" << endl;
    11. string s4;
    12. getline(cin, s4);
    13. cout << s4 << endl;
    14. cout << "------------------------------------------------" << endl;
    15. return 0;
    16. }

    getline主要是解决cin遇到空格就停止读取的问题

    这些是比较常见的string接口,当然还有很多接口

    具体介绍,大家可以看看官网 icon-default.png?t=N7T8https://cplusplus.com/

    (二)string模拟实现

     1、浅拷贝

    下面代码是有问题的 

    1. class String
    2. {
    3. public:
    4. /*String()
    5. :_str(new char[1])
    6. {*_str = '\0';}
    7. */
    8. //String(const char* str = "\0") 错误示范
    9. //String(const char* str = nullptr) 错误示范
    10. String(const char* str = "")
    11. {
    12. // 构造String类对象时,如果传递nullptr指针,可以认为程序非
    13. if (nullptr == str)
    14. {
    15. assert(false);
    16. return;
    17. }
    18. _str = new char[strlen(str) + 1];
    19. strcpy(_str, str);
    20. }
    21. ~String()
    22. {
    23. if (_str)
    24. {
    25. delete[] _str;
    26. _str = nullptr;
    27. }
    28. }
    29. private:
    30. char* _str;
    31. };
    32. // 测试
    33. void TestString()
    34. {
    35. String s1("hello bit!!!");
    36. String s2(s1);
    37. }

    说明:上述 String 类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用 s1 s2 时,编译器会调用默认的拷贝构造。最终导致的问题是, s1 s2 共用同一块内存空间,在释放时同一块 空间被释放多次而引起程序崩溃 ,这种拷贝方式,称为浅拷贝。
    浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来 。如果 对象中管理资源 ,最后就会 导致多个对象共 享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为 还有效,所以当继续对资源进项操作时,就会发生发生了访问违规
    可以采用深拷贝解决浅拷贝问题,即: 每个对象都有一份独立的资源,不要和其他对象共享
    2、深拷贝
    如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。
    3、实现 

    代码仅供参考 

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

  • 相关阅读:
    Vue3 + VueRouter + Vite + pinia组件化开发实战入门
    c 的I/O 复用:select()
    视频拍摄教程分享
    【JS面试题】面试官:“[1,2,3].map(parseInt)“ 输出结果是什么?答上来就算你通过面试
    软件测试常用的功能测试方法
    《吐血整理》保姆级系列教程-玩转Fiddler抓包教程(5)-Fiddler监控面板详解
    ChatGPT在生态保护和可持续发展中的潜在作用如何?
    DeepStream系列之yolov8部署测试
    常见首屏优化
    SQL进阶(五):With 函数 vs 视图函数
  • 原文地址:https://blog.csdn.net/Metaleaf/article/details/132670956