• C++ STL - string 成员函数 + 模拟实现


    目录

    string - 构造函数

     简介:

    分析:

    验证:

    string - 析构函数,重载赋值运算符

    ​编辑分析:

    模拟实现string构造,拷贝构造,重载赋值运算符,析构

    构造: 

    拷贝构造+重载赋值运算符

    传统写法:

    现代写法:

    string - Element access:

    前言:

    析:

    实现:

    string - 迭代器iterator

    简介:

    介绍:

    使用:

    string - 大小,容量函数

    string - 修改string操作

    string - 其余操作

    完整tiny string


    string - 构造函数

     简介:

    string构造函数重载的形式比较多样,最常用的是1. 默认构造函数  2. 拷贝构造函数  4. 以C字符串-const char* 为参数的构造函数。 
    其他的用的并不多。比如,string从pos位置开始的len个,C字符串的前n个,n个char,还有迭代器范围版本。

    1. void test1()
    2. {
    3. string s0("12345abc"); // string(const char* s);
    4. string s1; // string();
    5. string s2(s0); // 拷贝构造,string(const string& s);
    6. string s3 = s0; // 拷贝构造,C方式的拷贝构造。
    7. string s4 = "aaaaa";
    8. }

    分析:

    s2 为典型的拷贝构造,s3为C语言风格的拷贝构造。 值得注意的是s4,这里因为string(const char*) 版本构造函数不是explicit的,所以"aaaaa"隐式类型转换为(构造)string对象,再拷贝构造s4,编译器对此情况优化为直接进行构造。

    验证:

    当explicit string(const char* s); 时

    解除explicit后,如果在string(const char* s)构造函数和拷贝构造中加上输出语句,运行这个构造代码后,发现并没有拷贝构造的输出语句,即编译器对此优化为了直接构造。

    string - 析构函数,重载赋值运算符

    分析:

    比较简单,operator=重载了多个版本,注意区分赋值和构造的情况。析构函数在对象销毁时自动调用,用于回收清理对象申请的空间。

    模拟实现string构造,拷贝构造,重载赋值运算符,析构

    数据成员只有这几个。

    1. private:
    2. char* _str;
    3. size_t _size;
    4. size_t _capacity;
    5. public:
    6. const static size_t npos = -1;

    构造: 

    (都是class string的成员函数)

    1. // string() : _size(0),_capacity(0),_str(new char[1])
    2. // {
    3. // _str[0] = '\0';
    4. // }
    5. string(const char* str = "")
    6. {
    7. _size = strlen(str); // strlen不包含\0,计算的是有效字符个数。
    8. _capacity = _size;
    9. _str = new char[_capacity+1];
    10. strcpy(_str, str); // strcpy会拷贝'\0'
    11. }
    12. string(size_t n, char c)
    13. : _size(n),_capacity(n),_str(new char[n+1])
    14. {
    15. for(size_t i = 0; i < n; ++i)
    16. _str[i] = c;
    17. _str[n] = '\0';
    18. }

    没有全部实现,实现了几个常用的,明确size和capacity的意义,利用C语言库函数和new即可简单实现。

    拷贝构造+重载赋值运算符

    传统写法:

    1. string(const string& s)
    2. : _size(s._size),_capacity(s._capacity),_str(new char[s._capacity+1])
    3. {
    4. strcpy(_str,s._str);
    5. }
    6. // s1 = s3;
    7. string& operator=(const string& s)
    8. {
    9. if(this != &s)
    10. {
    11. delete[] this->_str;
    12. _str = new char[s._capacity+1];
    13. _size = s._size;
    14. _capacity = s._capacity;
    15. strcpy(_str,s._str);
    16. }
    17. return *this;
    18. }
    19. // 略微改进了一点,考虑到如果new开空间失败,不破坏原对象。
    20. string& operator=(const string& s)
    21. {
    22. if(this != &s)
    23. {
    24. char* tmp = new char[s._capacity+1];
    25. strcpy(tmp,s._str);
    26. delete[] this->_str;
    27. _str = tmp;
    28. _size = s._size;
    29. _capacity = s._capacity;
    30. }
    31. return *this;
    32. }

    现代写法:

    1. void swap(string& tmp)
    2. {
    3. std::swap(_str,tmp._str);
    4. std::swap(_size,tmp._size);
    5. std::swap(_capacity,tmp._capacity);
    6. }
    7. // 现代写法: 老板思维
    8. string(const string& s) // string s1(s2)
    9. :_size(0),_capacity(0),_str(nullptr) // 如果不这样,this的_str是随机值,tmp析构时,delete[]随机值非法。
    10. {
    11. string tmp(s._str); // 调用string(const char*) 构造函数
    12. this->swap(tmp);
    13. }
    14. string& operator=(const string& s)
    15. {
    16. if (this != &s) {
    17. // string tmp(s);
    18. string tmp(s._str);
    19. swap(tmp);
    20. }
    21. return *this;
    22. }
    23. // 最简版
    24. string& operator=(string s)
    25. {
    26. swap(s);
    27. return *this;
    28. }
    29. string& operator=(const char* s) {
    30. string tmp(s);
    31. this->swap(tmp);
    32. return *this;
    33. }
    34. ~string()
    35. {
    36. delete[] _str;
    37. _str = nullptr;
    38. _size = _capacity = 0;
    39. }

    1. 拷贝构造的现代写法:利用string(const char* s)构造出临时对象tmp,其实就是用实参string拷贝一份,然后交换*this 和 tmp,因为这里的*this刚刚初始化完,数据成员都是随机值,如果和tmpswap之后,tmp析构时,数据成员是随机值会报错,所以利用初始化列表将*this数据成员简单初始化以下,再与tmp交换。

    2. 对于swap函数,这里利用的是string的成员函数swap,上方有实现。原因有2:a、标准库中的std::swap()对于交换的两个对象,是先拷贝构造一份,再赋值两次,对于深拷贝的自定义类型来说,效率较低。  b、std::swap()会调用交换对象的类型的拷贝构造和operator=,我们在实现的拷贝构造中使用std::swap就会造成无限递归。  所以必须使用string的成员swap,而string::swap又调用std::swap()去交换内置类型数据成员。

    3. 最简版是对上方的进一步简化,在string这里,各个版本差别不是很大,但是到了vector,list等较复杂容器时,现代写法的优势将会很明显。

    string - Element access:

    前言:

    STL中的容器,统一的访问元素方式为迭代器访问,string同样支持,但是由于string底层存储是连续的,所以operator[]  at() 会更加方便一些。

    析:

    重载了const 和 非const两个版本,配对string 和 const string,对于非const string而言,[]运算符是返回元素引用的,可读可写。类似于C语言的数组,使得访问元素更加方便。

     at于operator[]的功能一样,只是at函数在pos越界时,抛出异常,而[]运算符下标越界时直接崩溃。

    back() front() 分别返回首元素和尾元素的引用,同样重载了const和非const两个版本。略了。

    1. void test2()
    2. {
    3. string s("1234567");
    4. for(size_t i = 0; i < s.size(); ++i)
    5. {
    6. ++s[i];
    7. ++s.at(i);
    8. cout<
    9. cout<at(i)<<" ";
    10. }
    11. cout<
    12. }

    实现:

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

    string - 迭代器iterator

    简介:

    迭代器,用于访问容器的元素,使用上类似于指针。事实上,对于string,vector这种底层存储为连续存储的容器而言,它们的迭代器就是一个改了名字的元素指针而已。而对于list map等存储不连续且复杂的容器而言,不再是普通的元素指针。

    对于string而言,使用[]访问元素会比迭代器更加方便,但是迭代器对于STL的其他容器而言至关重要,有些容器只能使用迭代器访问元素。迭代器的优点在于:通用性,简便性,对于STL所有容器来说,用法几乎一致。

    介绍:

    1. STL容器包括string,一共有四种迭代器,
    iterator   const_iterator   reverse_iterator   const_reverse_iterator   
    分别为迭代器,const迭代器,反向迭代器,const反向迭代器。

    3. begin()   end()  rbegin() rend()  cbegin()  cend()   crbegin()  crend()  分别为返回迭代器的成员函数。

    4. begin end 分别对应正向迭代器,rbegin rend对应反向迭代器

     

     上述四个成员函数,都重载了两个版本,用于const容器对象和非const容器对象,使用时自动匹配。那么,由此可见,这四个成员函数就足够得到全部种类的迭代器了,那后面四个又有什么用呢?  C++11新增了cbegin  cend  crbegin  crend,用以精确返回迭代器类型。提高代码可读性。也就可以让非const容器对象指定返回const_iterator。

    使用:

    1. 迭代器++,自动指向下一个元素,对于string,vector这种容器,迭代器++其实就是元素指针++,而对于list等底层存储不连续的容器,++为指向下一个元素,但是并不是内存中指针的++。

    1. void test3() {
    2. string s("abcdef");
    3. string::iterator it1 = s.begin();
    4. while (it1 != s.end()) {
    5. (*it1)++;
    6. cout << *it1;
    7. ++it1;
    8. }
    9. cout << endl;
    10. string::const_reverse_iterator it2 = s.crbegin();
    11. while (it2 != s.crend()) {
    12. cout << *it2;
    13. ++it2;
    14. }
    15. cout << endl;
    16. const string s1("123456");
    17. for (string::const_iterator it = s1.begin(); it != s1.end(); ++it) {
    18. // it为const_iterator
    19. // (*it)++; // error
    20. cout << *it;
    21. }
    22. for (auto i: s1) {
    23. cout << i;
    24. }
    25. for (const auto &i: s1) {
    26. cout << i;
    27. }
    28. }

    2. 范围for的原理就是迭代器,编译器会将范围for语句自动编译为一系列关于迭代器的语句。第一个为 char i = *it   第二个为  const char& i = *it 

    7. 解引用迭代器得到元素的引用。 begin end 左闭右开。 使用迭代器时,使用it != s.end()  而不是it < s.end()  ,对于string vector,迭代器支持<运算符,但是对于list等,由于底层存储不连续,迭代器不支持<运算符。

    1. public:
    2. typedef char* iterator;
    3. typedef const char* const_iterator;
    4. iterator begin()
    5. {
    6. return _str;
    7. }
    8. iterator end() {
    9. return _str + _size;
    10. }
    11. const_iterator begin() const
    12. {
    13. return _str;
    14. }
    15. const_iterator end() const
    16. {
    17. return _str + _size;
    18. }
    19. const_iterator cbegin() const
    20. {
    21. return _str;
    22. }
    23. const_iterator cend() const
    24. {
    25. return _str+_size;
    26. }

    简单实现,反向迭代器需要其他的学习。 


    string - 大小,容量函数

    1. size_t size() const;  返回string中有效字符个数(string底层存储时末尾有一个'\0',与C语言一样,此'\0'不算有效字符)
    2. size_t capacity() const; 返回string可容纳字符个数,string中有些空间已经申请好了,但是没有存储字符,不算是有效字符,但是属于string的空间。

    3. void resize (size_t n); void resize (size_t n, char c);   resize用于改变string的size大小,可减小,可扩大,扩大后默认用'\0'填充。size减小后,会删除元素,但是不会改变capacity

    4. void reserve (size_t n = 0);  用于改变capacity,当n<=capacity时,无操作。当n>capacity时,会扩充容量,即申请额外的空间,但是不存储有效字符。

    5. void clear(); 用于清理string,清理后size为0 capacity不变

    6. bool empty() const;   用于判空

    7. void shrink_to_fit();   释放多余的capacity,缩减到size大小。 有时不会进行操作。

    string - 修改string操作

    0. operator+=

    1. void push_back (char c); 末尾追加一个字符

    1.5. void pop_back();   ..... 略

    2. append 在string末尾追加,重载多个版本,和构造函数的参数形式几乎一样。

    3. assign  给string重新赋值,作用等同于 operator=,和append,构造函数重载形式十分类似。

    4. insert  给某一下标位置插入某些字符,重载形式还是那一套,只是多了个pos参数,有点无趣。

    5. erase  重载三个版本,需要查询网站即可。

    6. replace  重载好多版本,其实观察一下还是那一套,pos位置len个修改为各种形式,需要时查阅

    7. void swap (string& str);  这个是string的成员函数,将调用对象和参数str交换。

    总结:append push_back 直接用+=即可,assign用operator=即可,insert erase replace 用处较小,用时查阅。

    string - 其余操作

    1.  const char* c_str() const;   返回C形式的字符串首地址。

    2.  string substr (size_t pos = 0, size_t len = npos) const;   取当前字符串的字串,从pos开始len个字符,返回一个新字符串。

    3.  各种find,用于查找子串。 莫名其妙的copy,不想弄了,心态崩了。

    完整tiny string

    1. #ifndef STL_STRING_H
    2. #define STL_STRING_H
    3. namespace yzl
    4. {
    5. class string
    6. {
    7. public:
    8. typedef char* iterator;
    9. typedef const char* const_iterator;
    10. iterator begin()
    11. {
    12. return _str;
    13. }
    14. iterator end() {
    15. return _str + _size;
    16. }
    17. const_iterator begin() const
    18. {
    19. return _str;
    20. }
    21. const_iterator end() const
    22. {
    23. return _str + _size;
    24. }
    25. const_iterator cbegin() const
    26. {
    27. return _str;
    28. }
    29. const_iterator cend() const
    30. {
    31. return _str+_size;
    32. }
    33. public:
    34. // string() : _size(0),_capacity(0),_str(new char[1])
    35. // {
    36. // _str[0] = '\0';
    37. // }
    38. string(const char* str = "")
    39. {
    40. _size = strlen(str); // strlen不包含\0,计算的是有效字符个数。
    41. _capacity = _size;
    42. _str = new char[_capacity+1];
    43. strcpy(_str, str); // strcpy会拷贝'\0'
    44. }
    45. string(size_t n, char c)
    46. : _size(n),_capacity(n),_str(new char[n+1])
    47. {
    48. for(size_t i = 0; i < n; ++i)
    49. _str[i] = c;
    50. _str[n] = '\0';
    51. }
    52. // 传统写法
    53. // string(const string& s)
    54. // : _size(s._size),_capacity(s._capacity),_str(new char[s._capacity+1])
    55. // {
    56. // strcpy(_str,s._str);
    57. // }
    58. // s1 = s3;
    59. // string& operator=(const string& s)
    60. // {
    61. // if(this != &s)
    62. // {
    63. // delete[] this->_str;
    64. // _str = new char[s._capacity+1];
    65. // _size = s._size;
    66. // _capacity = s._capacity;
    67. // strcpy(_str,s._str);
    68. // }
    69. // return *this;
    70. // }
    71. // 略微改进了一点,考虑到如果new开空间失败,不破坏原对象。
    72. // string& operator=(const string& s)
    73. // {
    74. // if(this != &s)
    75. // {
    76. // char* tmp = new char[s._capacity+1];
    77. // strcpy(tmp,s._str);
    78. // delete[] this->_str;
    79. // _str = tmp;
    80. // _size = s._size;
    81. // _capacity = s._capacity;
    82. // }
    83. // return *this;
    84. // }
    85. //=======================================================
    86. void swap(string& tmp)
    87. {
    88. std::swap(_str,tmp._str);
    89. std::swap(_size,tmp._size);
    90. std::swap(_capacity,tmp._capacity);
    91. }
    92. // 现代写法: 老板思维
    93. string(const string& s) // string s1(s2)
    94. :_size(0),_capacity(0),_str(nullptr) // 如果不这样,this的_str是随机值,tmp析构时,delete[]随机值非法。
    95. {
    96. string tmp(s._str); // 调用string(const char*) 构造函数
    97. this->swap(tmp);
    98. }
    99. // string& operator=(const string& s)
    100. // {
    101. // if (this != &s) {
    102. string tmp(s);
    103. // string tmp(s._str);
    104. // swap(tmp);
    105. // }
    106. // return *this;
    107. // }
    108. // 最简版
    109. string& operator=(string s)
    110. {
    111. swap(s);
    112. return *this;
    113. }
    114. string& operator=(const char* s) {
    115. string tmp(s);
    116. this->swap(tmp);
    117. return *this;
    118. }
    119. ~string()
    120. {
    121. delete[] _str;
    122. _str = nullptr;
    123. _size = _capacity = 0;
    124. }
    125. const char* c_str() const
    126. {
    127. return _str;
    128. }
    129. size_t size() const
    130. {
    131. return _size;
    132. }
    133. size_t capacity() const
    134. {
    135. return _capacity;
    136. }
    137. char& operator[](size_t pos)
    138. {
    139. assert(pos < _size);
    140. return _str[pos];
    141. }
    142. const char& operator[](size_t pos) const
    143. {
    144. assert(pos<_size);
    145. return _str[pos];
    146. }
    147. void reserve(size_t n = 0)
    148. {
    149. if(n > _capacity)
    150. {
    151. char* tmp = new char[n+1]; // n个有效数据,1用于'\0'
    152. strcpy(tmp,_str);
    153. delete[] (_str);
    154. _str = tmp;
    155. _capacity = n; // _capacity 必须处理
    156. }
    157. }
    158. void resize(size_t n, char c = '\0')
    159. {
    160. if(n > _size)
    161. {
    162. reserve(n);
    163. for(size_t i = _size; i < n; ++i)
    164. {
    165. _str[i] = c;
    166. }
    167. _str[n] = '\0';
    168. _size = n;
    169. }
    170. else
    171. {
    172. _str[n] = '\0';
    173. _size = n;
    174. }
    175. }
    176. void clear()
    177. {
    178. _str[0] = '\0';
    179. _size = 0;
    180. }
    181. bool empty() const
    182. {
    183. return _size == 0;
    184. }
    185. void push_back(char c)
    186. {
    187. if(_size == _capacity)
    188. {
    189. reserve(_capacity == 0 ? 4 : 2*_capacity);
    190. }
    191. _str[_size] = c;
    192. ++_size;
    193. _str[_size] = '\0';
    194. }
    195. string& append(const char* s)
    196. {
    197. int size = strlen(s);
    198. if(_size + size > _capacity)
    199. {
    200. reserve(_size+size);
    201. }
    202. strcpy(_str+_size,s);
    203. _size+=size;
    204. return *this;
    205. }
    206. string& append(const string& s)
    207. {
    208. int size = s.size();
    209. if(_size + size > _capacity)
    210. {
    211. reserve(_size + size);
    212. }
    213. strcpy(_str+_size,s.c_str());
    214. _size+=size;
    215. return *this;
    216. }
    217. string& append(size_t n, char c)
    218. {
    219. if(_size + n > _capacity)
    220. {
    221. reserve(_size + n);
    222. }
    223. for(size_t i = 0; i < n; ++i)
    224. {
    225. _str[_size+i] = c;
    226. }
    227. _size+=n;
    228. return *this;
    229. }
    230. // string& append(const string& s); this->append(s.c_str());
    231. // string& append(size_t n, char c); push_back(c);
    232. string& operator+=(char c)
    233. {
    234. this->push_back(c);
    235. return *this;
    236. }
    237. string& operator+=(const char* s)
    238. {
    239. this->append(s);
    240. return *this;
    241. }
    242. string& operator+=(const string& s)
    243. {
    244. this->append(s);
    245. return *this;
    246. }
    247. string& insert(size_t pos, char c)
    248. {
    249. assert(pos <= _size);
    250. if(_size == _capacity)
    251. {
    252. reserve(_capacity == 0?4:2*_capacity);
    253. }
    254. // int end = _size;
    255. // while(end >= (int)pos)
    256. // {
    257. // _str[end+1] = _str[end];
    258. // end--;
    259. // }
    260. // 比较推荐的写法,主要是 int和size_t比较,会出现比较错误,当int小于0时。
    261. size_t end = _size+1;
    262. while(end > pos)
    263. {
    264. _str[end] = _str[end-1];
    265. end--;
    266. }
    267. _str[pos] = c;
    268. ++_size;
    269. return *this;
    270. }
    271. string& insert(size_t pos, size_t n, char ch)
    272. {
    273. assert(pos <= _size);
    274. // pos==0 n==0 则下面while会出错(size_t)
    275. if(n == 0)
    276. return *this;
    277. if(_size + n > _capacity)
    278. {
    279. reserve(_size+n);
    280. }
    281. // [pos, pos+n) n个 [_size, _size+n) n个
    282. size_t end = _size + n;
    283. while(end >= pos+n)
    284. {
    285. _str[end] = _str[end-n];
    286. end--;
    287. }
    288. for(size_t i = pos; i < pos+n; ++i)
    289. {
    290. _str[i] = ch;
    291. }
    292. _size+=n;
    293. return *this;
    294. }
    295. string& insert(size_t pos, const char* str)
    296. {
    297. assert(pos<=_size);
    298. if(strlen(str) == 0)
    299. return *this;
    300. int len = strlen(str);
    301. if(_size+len > _capacity) {
    302. reserve(_size+len);
    303. }
    304. size_t end = _size+len;
    305. while(end >= pos+len)
    306. {
    307. _str[end] = _str[end-len];
    308. end--;
    309. }
    310. // int end = _size;
    311. // while(end >= (int)pos)
    312. // {
    313. // _str[end+len] = _str[end];
    314. // end--;
    315. // }
    316. strncpy(_str+pos,str,len); // strcpy会把\0也copy进去。
    317. _size+=len;
    318. return *this;
    319. }
    320. string& insert(size_t n, const string& s)
    321. {
    322. this->insert(n,s.c_str());
    323. return *this;
    324. }
    325. string& erase(size_t pos = 0, size_t len = npos)
    326. {
    327. assert(pos < _size);
    328. if(len == npos || pos + len >= _size)
    329. {
    330. _str[pos] = '\0';
    331. _size = pos;
    332. }
    333. else
    334. {
    335. size_t i = pos + len;
    336. size_t p = pos;
    337. while(i<=_size)
    338. {
    339. _str[p] = _str[i];
    340. i++;
    341. p++;
    342. }
    343. _size -= len;
    344. }
    345. return *this;
    346. }
    347. size_t find(char c, size_t pos = 0) const
    348. {
    349. assert(pos < _size);
    350. for(size_t i = pos; i < size(); ++i) {
    351. if(_str[i] == c)
    352. return i;
    353. }
    354. return npos;
    355. }
    356. size_t find(const char* str, size_t pos = 0) const
    357. {
    358. assert(pos < _size);
    359. assert(str);
    360. const char* p = strstr(_str+pos, str);
    361. if(p == nullptr)
    362. return npos;
    363. else
    364. return p-_str;
    365. }
    366. size_t find(const string& s, size_t pos = 0) const
    367. {
    368. return find(s.c_str(), pos);
    369. }
    370. string substr(size_t pos, size_t len = npos) const
    371. {
    372. assert(pos < _size);
    373. if(len == npos || len + pos >= _size)
    374. {
    375. len = _size - pos;
    376. }
    377. string ret;
    378. for(size_t i = pos; i < pos + len; ++i)
    379. {
    380. ret+=_str[pos];
    381. }
    382. return ret;
    383. }
    384. bool operator> (const string& s) const
    385. {
    386. return strcmp(_str, s.c_str())>0;
    387. }
    388. bool operator==(const string& s) const
    389. {
    390. return strcmp(_str, s.c_str()) == 0;
    391. }
    392. bool operator >=(const string& s)const
    393. {
    394. return *this > s || *this == s;
    395. }
    396. bool operator < (const string& s)const {
    397. return !(*this >= s);
    398. }
    399. bool operator <= (const string& s)const {
    400. return !(*this>s);
    401. }
    402. bool operator != (const string& s)const {
    403. return !(*this == s);
    404. }
    405. private:
    406. char* _str;
    407. size_t _size;
    408. size_t _capacity;
    409. public:
    410. const static size_t npos = -1;
    411. // static size_t npos;
    412. };
    413. ostream& operator<<(ostream& os, const string& s)
    414. {
    415. for(size_t i = 0; i < s.size(); ++i)
    416. os<
    417. return os;
    418. }
    419. // istream& operator>>(istream& is, string& s)
    420. // {
    421. // s.clear();
    422. // char ch;
    423. // ch = is.get();
    424. // while(ch != ' ' && ch != '\n')
    425. // {
    426. // s += ch;
    427. // ch = is.get();
    428. // }
    429. // return is;
    430. // }
    431. istream& operator>>(istream& is, string& s)
    432. {
    433. s.clear();
    434. char ch;
    435. ch = is.get();
    436. const int sz = 32;
    437. char buff[sz];
    438. size_t i = 0;
    439. while(ch != ' ' && ch != '\n')
    440. {
    441. buff[i] = ch;
    442. i++;
    443. if(i == sz-1)
    444. {
    445. buff[i] = '\0';
    446. s += buff;
    447. i = 0;
    448. }
    449. ch = is.get();
    450. }
    451. buff[i] = '\0';
    452. s+=buff;
    453. return is;
    454. }
    455. // size_t string::npos = -1;
    456. }
    457. #endif //STL_STRING_H

  • 相关阅读:
    2021全国大学生电子设计竞赛论文(智能送药小车(F题))(电赛论文模板)
    微服务开发与实战Day01 - MyBatisPlus
    资料库的webrtc文件传输
    【代码随想录】栈与队列专栏(java版本)
    10个必备的 async/await 工具函数
    引用Geoip实现由IP地址获取国家城市等信息
    vue模板语法上集
    Java 使用 poi 和 aspose 实现 word 模板数据写入并转换 pdf 增加水印
    强化学习问题(一)--- 输入conda activate base无法激活虚拟环境
    [C/C++] 数据结构 链表OJ题:相交链表(寻找两个链表的相交起始结点)
  • 原文地址:https://blog.csdn.net/i777777777777777/article/details/126435973