【C++】-- STL之String模拟实现(三)

简介: 【C++】-- STL之String模拟实现

26.operator>>

输入重载>>让string直接输入,像内置类型一样;从标准输入流读取字符,遇到' '或'\0'就停止

1.  istream& operator>>(istream& in, string& s)
2.  {
3.    s.clear();
4.    char ch;
5.    ch = in.get();
6.    while (ch != ' ' && ch != '\0')
7.    {
8.      s += ch;
9.      ch = in.get();
10.     }
11.     return in;
12.   }

完整代码段

1. #pragma  once  
2. #define  _CRT_SECURE_NO_WARNINGS
3. #include<iostream>
4. #include<assert.h>
5. #include<stdlib.h>
6. 
7. using namespace std;
8. //定义一个新的命名空间,和全局命名空间隔离开
9. namespace delia
10. {
11.   class string
12.   {
13.   public:
14. 
15.     typedef char* iterator;
16. 
17.     //迭代器
18.     iterator begin()
19.     {
20.       return _str;
21.     }
22. 
23.     //迭代器
24.     iterator end()
25.     {
26.       return _str + _size;
27.     }
28. 
29.     //const迭代器
30.     iterator begin() const 
31.     {
32.       return _str;
33.     }
34. 
35.     //const迭代器
36.     iterator end() const
37.     {
38.       return _str + _size;
39.     }
40. 
41.     //构造函数
42.     string(const char* str = "")
43.     {
44.       _str = new char[strlen(str) + 1];//1是留给'/0'的,_size最大为申请字节数-1,_capacity也为申请字节数-1
45.       strcpy(_str, str);
46. 
47.       _size = strlen(str);
48.       _capacity = _size;
49.     }
50. 
51.     //现代拷贝构造
52.     string(const string& s)
53.       :_str(nullptr)
54.       , _size(0)
55.       , _capacity(0)
56.     {
57.       string tmp(s._str);
58.       swap(tmp);
59.     }
60. 
61.     //赋值运算符重载
62.     string& operator=(string s)
63.     {
64.       swap(s);
65.       return *this;
66.     }
67. 
68.     //析构函数
69.     ~string()
70.     {
71.       delete[] _str;
72.       _str = nullptr;
73.       _size = 0;
74.       _capacity = 0;
75.     }
76. 
77.     //实现交换函数
78.     void swap(string& s)
79.     {
80.       ::swap(_str, s._str);//用::指定调用全局的swap函数即库里的swap函数
81.       ::swap(_size, s._size);
82.       ::swap(_capacity, s._capacity);
83.     }
84. 
85.     //const string对象遍历,只读
86.     char& operator[](size_t i)  const//char要加&,如果不加,那么return就是传值返回,返回的是_str[i]的拷贝,临时对象,而临时对象具有常性,不能被修改,只能读
87.                      //如果要对_str进行修改的话,char要加&,用传引用返回
88.     {
89.       assert(i < _size);
90.       return _str[i];
91.     }
92. 
93.     //非const string对象遍历,可读可写
94.     char& operator[](size_t i)
95.     {
96.       assert(i < _size);
97.       return _str[i];
98.     }
99. 
100.    //求string对象大小
101.    size_t size() const
102.    {
103.      return strlen(_str);
104.    }
105. 
106.    //获取c形式字符串,将 const string* 类型 转化为 const char* 类型
107.    const char* c_str()
108.    {
109.      return _str;
110.    }
111. 
112.    //开空间,扩展_capacity,_size不变
113.    void reserve(size_t n)
114.    {
115.      //1.申请新空间
116.      //2.拷贝字符串
117.      //3.释放旧空间
118.      //4.指向新空间
119.      //5.更新容量
120.      if (n > _capacity)
121.      {
122.        char* tmp = new char[n + 1];//1.申请新空间,多开的那一个空间,存放\0
123.        strncpy(tmp, _str, _size + 1);//2.将字符串包含\0在内都拷贝到新空间
124.        delete[] _str;//3.释放旧空间
125. 
126.        _str = tmp;//4.指向新空间
127.        _capacity = n;//5.更新容量
128.      }
129.    }
130. 
131.    //开空间+初始化,扩展_capacity,_size也要修改
132.    void resize(size_t n, char val = '\0')//如果没有显式给出val,val为\0
133.    {
134.      //1.当n<_size,无需增容
135.      if (n < _size)
136.      {
137.        _size = n;//直接更新_size
138.        _str[_size] = '\0';//将_size位置置为\0
139.      }
140.      //2.当n>_size,分两种情况
141.      //(1)_size < n <_capacity,无需增容,直接将_size到n位置置为val
142.      //(2)n >_capacity,需增容,并将_size到n位置置为val
143.      //这两种情况只有是否增容的区别,其他没有区别
144.      else
145.      {
146.        if (n > _capacity)
147.        {
148.          reserve(n);
149.        }
150. 
151.        for (size_t i = _size; i < n; i++)
152.        {
153.          _str[i] = val;
154.        }
155. 
156.        _str[n] = '\0';//将n的位置置\0
157.        _size = n;//更新_size
158.      }
159.    }
160. 
161.    //尾插一个字符
162.    void push_back(char ch)
163.    {
164.      //字符个数已经达到容量时,重新开空间
165.      if (_size == _capacity)
166.      {
167.        reserve(_capacity == 0 ? 4 : _capacity * 2);//如果是第一次开空间,就开4个字节,如果不是第一次开空间,就开成原来的2倍容量
168.      }
169. 
170.      _str[_size] = ch;//将字符ch缀到字符串末尾
171.      _str[_size + 1] = '\0';//字符ch后面加\0,表明字符串结束
172.      _size++;//字符串大小+1
173.    }
174. 
175.    //追加字符串
176.    void append(const char* str)
177.    {
178.      size_t len = _size + strlen(str);//函数执行完毕后字符串的大小
179. 
180.      if (len > _capacity)
181.      {
182.        reserve(len);//空间不够,增容
183.      }
184. 
185.      strcpy(_str + _size, str);//将str内容拷贝至_str末尾
186.      _size = len;//更新_size
187.    }
188. 
189.    //+= 1个字符
190.    string& operator+= (char c)
191.    {
192.      push_back(c);//尾插字符
193.      return *this;
194.    }
195. 
196.    //+= 字符串
197.    string& operator+= (const char* str)
198.    {
199.      append(str);//拼接字符串
200.      return *this;
201.    }
202. 
203.    //insert插入一个字符
204.    string& insert(size_t pos, const char ch)
205.    {
206.      assert(pos <= _size);//pos必须小于等于字符串长度
207. 
208.      if (_size == _capacity)
209.      {
210.        reserve(2 * _capacity);//如果满了就增容
211.      }
212. 
213.      size_t end = _size + 1;
214.      while (end > pos)//将pos至_size之间的字符依次向后挪动一个位置,来腾出pos位置
215.      {
216.        _str[end] = _str[end - 1];
217.        end--;//end为0时,因此end类型应为int,那么end--就为-1,pos为无符号数,不满足循环条件,退出循环
218.            //但如果end类型为size_t,那么end为0时,end--就为-1,在内存中存储为无符号数(-1补码全1),满足循环条件,永远无法退出循环
219.      }
220. 
221.      _str[pos] = ch;//pos位置插入ch
222.      _size++;//更新_size
223. 
224.      return *this;
225.    }
226. 
227.    //erase 删除字符
228.    string& erase(size_t pos, size_t len = npos)
229.    {
230.      assert(pos < _size);
231. 
232.      size_t leftLen = _size - pos;//从pos位置到字符串结束的字符长度
233.      if (leftLen < len)//1.pos之后的字符全部删掉
234.      {
235.        _str[pos] = '\0';
236.        _size = pos;
237.      }
238.      else//2.pos之后的字符删掉一部分,没删完的部分要挪到pos位置
239.      {
240.        strcpy(_str + pos, _str + pos + len);
241.        _size -= len;
242.      }
243.      return *this;
244.    }
245. 
246.    //查找字符
247.    size_t find(char ch, size_t pos = 0)
248.    {
249.      assert(pos < _size);
250.      for (size_t i = 0; i < _size; i++)
251.      {
252.        if (_str[i] == ch)
253.        {
254.          return i;//找到就返回字符所在位置
255.        }
256.      }
257.      return npos;//没找到就返回-1
258.    }
259. 
260.    //查找字符串(子串)
261.    size_t find(const char* str, size_t pos = 0)
262.    {
263.      assert(pos < _size);
264.      const char* ret  = strstr(_str + pos, str);
265. 
266.      if (ret)
267.      {
268.        return ret - _str;//计算str与_str的偏移量,即str在_str中的下标
269.      }
270.      else
271.      {
272.        return npos;//没找到就返回-1
273.      }
274.    }
275. 
276. 
277.    //字符串比较,只需要实现>和==
278.    bool operator>(const string& s)
279.    {
280.      return strcmp(_str, s._str) < 0;
281.    }
282. 
283.    bool operator==(const string& s)
284.    {
285.      return strcmp(_str, s._str) == 0;
286.    }
287. 
288.    // 其它4个运算符都可以对>和==这两个运算符进行重载
289.    bool operator>=(const string& s)
290.    {
291.      return (*this > s) || (*this == s);
292.    }
293. 
294.    bool operator<(const string& s)
295.    {
296.      return !(*this >= s);
297.    }
298. 
299.    bool operator<=(const string& s)
300.    {
301.      return !(*this > s);
302.    }
303. 
304.    bool operator!=(const string& s)
305.    {
306.      return !(*this == s);
307.    }
308. 
309.    void clear()
310.    {
311.      _size = 0;
312.      _str[0] = '\0';
313.    }
314. 
315.  private:
316.    char* _str;
317.    size_t _size;
318.    size_t _capacity;
319. 
320.    static const size_t npos;
321.  };
322. 
323.  const size_t string::npos = -1;//npos初始化
324. 
325.  //输出重载<<让string直接输出打印,像内置类型一样,用范围for就可以实现
326.  ostream& operator<<(ostream& out, const string& s)
327.  {
328.    for (auto ch: s)
329.    {
330.      out << s;
331.    }
332.    return out;
333.  }
334. 
335.  //输入重载>>让string直接输入,像内置类型一样;从标准输入流读取字符,遇到' '或'\0'就停止
336.  istream& operator>>(istream& in, string& s)
337.  {
338.    s.clear();
339.    char ch;
340.    ch = in.get();
341.    while (ch != ' ' && ch != '\0')
342.    {
343.      s += ch;
344.      ch = in.get();
345.    }
346.    return in;
347.  }
348. 
349.  void f(const string& s)
350.  {
351.    for (size_t i = 0; i < s.size(); i++)
352.    {
353.      cout << s[i] << " ";
354.    }
355.    cout << endl;
356.  }
357. 
358.  void test_string1()
359.  {
360.    string s1("hello world");
361.    string s2(s1);
362. 
363.    string s3("cplusplus.com");
364.    std::swap(s1, s3);//调用库里的swap函数,3次深拷贝,再把原来的空间释放掉,再开一样大的空间,代价大
365.    s1.swap(s3);//调用delia::string类里面的成员函数swap,仅仅只是成员变量的交换,s3指向了s1的空间,s1指向了s3的空间
366. 
367.    string s4 = s1.c_str();
368.    s1[2] = 'w';
369.    f(s1);//h e w l o   w o r l d
370. 
371.    for (size_t i = 0; i < s1.size(); i++)
372.    {
373.      cout << s1[i] << " ";
374.    }
375.    cout << endl;//h e w l o   w o r l d
376.  }
377. 
378.  void test_string2()
379.  {
380.    string s1("hello world");
381. 
382.    //使用迭代器遍历
383.    string::iterator it = s1.begin();
384.    while (it != s1.end())
385.    {
386.      cout << *it << " ";
387.      it++;
388.    }
389.    cout << endl;//h e l l o   w o r l d
390. 
391.    //使用范围for遍历,范围for会被编译器替换成迭代器形式,如果把17-26行屏蔽,会发现编译不过
392.    for (auto ch : s1)
393.    {
394.      cout << *it << " ";
395.    }
396.    cout << endl;//h e l l o   w o r l d
397.  }
398. 
399.  void test_string3()
400.  {
401.    string s1("hello world");
402.    s1 += "abc";
403.    for (auto& ch : s1)
404.    {
405.      cout << ch << " ";
406.    }
407.    cout << endl;//h e l l o   w o r l d a b c
408. 
409.    s1 += "xyz";
410.    for (auto& ch : s1)
411.    {
412.      cout << ch << " ";
413.    }
414.    cout << endl;//h e l l o   w o r l d a b c x y z
415.  }
416. 
417.  void test_string4()
418.  {
419.    string s1("hello world");
420.    s1 += '#';
421.    for (auto ch : s1)
422.    {
423.      cout << ch << " ";
424.    }
425.    cout << endl;//hello world#
426. 
427.    s1 += "xyz";
428.    for (auto ch : s1)
429.    {
430.      cout << ch << " ";
431.    }
432.    cout << endl;//hello world#xyz
433. 
434.    s1 += "!!!!!!!!!!!!!!!!!!!!";
435.    for (auto ch : s1)
436.    {
437.      cout << ch << " ";
438.    }
439.    cout << endl;//hello world#xyz!!!!!!!!!!!!!!!!!!!!
440.  }
441. 
442.  void test_string5()
443.  {
444.    string s1("hello world");
445.    s1.resize(3);
446.    cout << s1.c_str() << endl;//hel
447.  }
448. 
449.  void test_string6()
450.  {
451.    string s1("hello");
452.    s1.resize(6, 'a');
453.    cout << s1.c_str() << endl;//helloa
454.  }
455. 
456.  void test_string7()
457.  {
458.    string s1("hello");
459.    s1.resize(9, 'o');
460.    cout << s1.c_str() << endl;//helloooo
461.  }
462. 
463.  void test_string8()
464.  {
465.    string s1("hello");
466.    s1.insert(0, 'u');
467.    cout << s1.c_str() << endl;//uhello
468.  }
469. 
470.  void test_string9()
471.  {
472.    string s1("hello");
473.    s1.erase(0, 2);
474.    cout << s1.c_str() << endl;//llo
475. 
476.    string s2("world");
477.    s2.erase(1);//如果不给len,那么npos=-1,无符号数为4294967295,删除从位置为1-4294967295的字符,把后面的字符全部删除了
478.    cout << s2.c_str() << endl;//w
479.  }
480. 
481. 
482.  void test_string10()
483.  {
484.    string s1("hello world");
485.    cout << s1.find("rl",6) << endl;//8,从s1中的第6个位置开始查找"rl"字符串
486.  }
487. 
488.  void test_string11()
489.  {
490.    std::string s("hello");
491.    cin >> s;
492.    cout << s << endl;
493.  }
494. }
495. 
496. 
497. int main()
498. {
499.  //delia::test_string1();
500.  //delia::test_string2();
501.  //delia::test_string3();
502.  //delia::test_string4();
503.  //delia::test_string5();
504.  //delia::test_string6();
505.  //delia::test_string7();
506.  //delia::test_string8();
507.  //delia::test_string9();
508.  //delia::test_string10();
509.  delia::test_string11();
510.  return 0;
511. }


二、在线OJ题

替换空格 OJ链接

分析:(1)如果使用string的replace或erase+insert都要挪动数据,效率低

          (2)考虑直接用新串,用新串需要分配多大空间?一个空格占1个字节,‘%20’占3个字节,替换后,每个空格都比原来多两个字节,新串空间应为:原串长度+2*空格数

1. class Solution {
2. public:
3.     string replaceSpace(string s) {
4. 
5.         //1.计算空格数
6.         size_t spaceCount = 0;
7.         for(auto ch:s)
8.         {
9.             if(ch == ' ')
10.             {
11.                 spaceCount++;
12.             }
13.         }
14.       
15.         //2.声明新串
16.         string ret;
17. 
18.         //3.为新串分配空间:从空格变为%20,长度增加2
19.         ret.reserve(s.size()+2*spaceCount);
20. 
21.         //4.将空格替换为%20
22.         for(auto ch:s)
23.         {
24.             if(ch != ' ')
25.             {
26.                 ret += ch;
27.             }
28.             else
29.             {
30.                 ret += "%20";
31.             }
32.         }
33. 
34.         return ret;
35.     }
36. };
相关文章
|
1天前
|
C++ 容器
C++ STL:各类容器的特点和优缺点比较
C++ STL:各类容器的特点、优势、劣势比较
|
3天前
|
存储 算法 C++
C++一分钟之-标准模板库(STL)简介
【6月更文挑战第21天】C++ STL是高效通用的算法和数据结构集,简化编程任务。核心包括容器(如vector、list)、迭代器、算法(如sort、find)和适配器。常见问题涉及内存泄漏、迭代器失效、效率和算法误用。通过示例展示了如何排序、遍历和查找元素。掌握STL能提升效率,学习过程需注意常见陷阱。
20 4
|
7天前
|
算法 前端开发 Linux
【常用技巧】C++ STL容器操作:6种常用场景算法
STL在Linux C++中使用的非常普遍,掌握并合适的使用各种容器至关重要!
33 10
|
23小时前
|
存储 安全 算法
C++的内置数组和STL array、STL vector
C++的内置数组和STL array、STL vector
|
5天前
|
存储 编译器 C++
|
5天前
|
C语言 C++
C++对C的改进和拓展\string类型
C++对C的改进和拓展\string类型
8 1
|
9天前
|
存储 算法 程序员
【C++进阶】深入STL之 栈与队列:数据结构探索之旅
【C++进阶】深入STL之 栈与队列:数据结构探索之旅
18 4
|
9天前
|
存储 算法 程序员
【C++进阶】深入STL之vector:构建高效C++程序的基石
【C++进阶】深入STL之vector:构建高效C++程序的基石
16 1
|
9天前
|
存储 缓存 编译器
【C++进阶】深入STL之list:模拟实现深入理解List与迭代器
【C++进阶】深入STL之list:模拟实现深入理解List与迭代器
10 0
|
9天前
|
C++ 容器
【C++进阶】深入STL之list:高效双向链表的使用技巧
【C++进阶】深入STL之list:高效双向链表的使用技巧
14 0