首页 > 其他分享 >STL-string模拟实现

STL-string模拟实现

时间:2024-02-27 22:36:02浏览次数:13  
标签:const string STL char str return 模拟 size

  1 #pragma once
  2 
  3 #include<iostream>
  4 #include<string.h> 
  5 #include<assert.h>
  6 using std::cout;
  7 using std::endl;
  8 using std::cin;
  9 namespace test {
 10 
 11     class string {
 12         //friend std::ostream& operator<<(std::ostream& out, const string& s);
 13         //friend std::istream& operator>>(std::istream& in, const string& s);
 14 
 15     private:
 16         //char* 和 字符数组char[] 基本等价
 17         //const char* 是字符串常量 "xxxxxxxx"
 18         char* _str; //不能是const,因为要修改_str的内容,
 19         size_t _size; //有效字符长度(不包括\0)
 20         size_t _capacity; //有效容量(不包括'\0'),但真实容量为有效容量+1(包括'\0')
 21 
 22         //member constant (static const size_t npos = -1; //C++手册的写法)
 23         static size_t npos; //静态成员变量不能给缺省值,因为静态成员不走初始化列表,缺省值是辅助初始化列表使用的
 24 
 25         //但是,const修饰的静态整型常量可以给缺省值,仅限整型,double什么的都不可以
 26         //static const size_t N = 1;
 27         //int _a[N]; //可以这么用
 28 
 29     public:
 30         typedef char* iterator;
 31         typedef const char* const_iterator;
 32 
 33 
 34         //iterator
 35         iterator begin()
 36         {
 37             return _str;
 38         }
 39         const const_iterator begin() const
 40         {
 41             return _str;
 42         }
 43         iterator end()
 44         {
 45             return _str + _size;
 46         }
 47         const const_iterator end() const
 48         {
 49             return _str + _size;
 50         }
 51         //反向迭代器暂时不写
 52         //...
 53 
 54         //constructor 
 55 
 56         //string()
 57         //    //无参不能为空,因为一旦访问就会解引用空指针,不符合需求
 58         //    //而且不能赋值为0 ,'\0', -- 
 59         //    :_str(new char[1]) //必须使用new[]
 60         //    , _size(0)
 61         //    , _capacity(0)
 62         //{
 63         //    _str[0] = '\0';
 64         //} // ------------------------通过缺省值合并到带参构造函数
 65         string(const char* s = "") //---支持const char* 隐式转化成string ,走构造+拷贝构造 => 构造
 66             :_size(strlen(s))
 67         {
 68             //如果多个参数有关联,尽量不走初始化列表,防止因为声明位置导致初始化错误
 69             //strlen计算不包含'\0'
 70             //strcpy会拷贝'\0'
 71             //所以需要多开1个空间用于存放'\0'
 72             cout << "string(const char* s) -- 左值,默认构造" << endl;
 73             _capacity = _size == 0 ? 3 : _size;//_capacity不能为0,为什么? 为0就寄,至少会有一个\0,capacity就不可能为0
 74             _str = new char[_capacity + 1]; 
 75             strcpy(_str, s);
 76         }
 77 
 78         //自定义成员swap
 79         void swap(string& tmp)
 80         {
 81             std::swap(_str, tmp._str); //把_str和tmp._str的值交换
 82             std::swap(_size, tmp._size);
 83             std::swap(_capacity, tmp._capacity);
 84             //函数结束后会自动释放
 85         }
 86 
 87         //没写移动构造之前,既接收左值,也接收右值
 88         //copy constructor 
 89         string(const string& s) //拷贝构造的参数是本对象类型的引用
 90             :_str(nullptr)
 91         {
 92             cout << "string(const string& s) -- 左值,深拷贝" << endl;
 93             string tmp(s._str); //复用:走带参构造造一个临时对象tmp,值是拷贝的
 94             swap(tmp);//交换数据,拷贝完成
 95         }
 96 
 97         //移动构造 -- 
 98         string(string&& s) ///------ &&
 99             :_str{ nullptr }
100         {
101             cout << "string(string&& s) -- 移动拷贝" << endl;
102             swap(s);
103         }
104 
105         string& operator=(const string& s)
106         {
107             //if (this != &s)
108             //{
109                 //考虑极端复杂情况
110                 //1._str本身很大,s很小,如果不缩容,则会浪费很多空间
111                 //2._str本身很小,s很大,则必须扩容,需要扩容很多次
112                 //干脆直接开新空间,拷贝过去
113                 /*char* tmp = new char[s._size + 1];
114                 strcpy(tmp, s._str);
115                 delete[] _str;
116                 _str = tmp;
117                 _size = s._size;
118                 _capacity = s._capacity;*/
119 
120                 cout << "string& operator=(string s) -- 深拷贝赋值" << endl;  //移动拷贝和深拷贝测试
121                 string tmp(s);
122                 swap(tmp);
123             //}
124             return *this;
125         }
126 
127         string& operator=(string&& s)
128         {
129             cout << "string& operator=(string&& s) -- 移动赋值" << endl;  //移动拷贝和深拷贝测试
130             swap(s);
131             return *this;
132         }
133 
134 
135 
136         //destructor
137         ~string()
138         {
139             delete[] _str;
140             _str = nullptr;
141         }
142 
143 
144 
145 
146 
147        //Capacity
148         size_t size() const
149         {
150             return _size;
151         }
152         size_t capacity() const
153         {
154             return _capacity;
155         }
156         void reserve(size_t n)
157         {
158             if (n > _capacity)
159             {
160                 char* tmp = new char[n + 1];
161                 strcpy(tmp, _str);
162                 delete[] _str;
163                 _str = tmp; //指针,可以直接赋值,指向新的对象
164                 _capacity = n;
165             }
166         }
167         void resize(size_t n, char ch = '\0')
168         {
169             if (n <= _size)
170             {
171                 _str[n] = '\0'; //只要放'\0',后面都识别不出来了
172                 _size = n;
173             }
174             else
175             {
176                 reserve(n);
177                 //memset();
178                 size_t begin = _size;
179                 while (begin != n)
180                 {
181                     _str[begin] = ch;
182                     ++begin;
183                 }
184                 _size = n;
185                 _str[_size] = '\0';
186             }
187         }
188         void clear()
189         {
190             _str[0] = '\0';
191             _size = 0;
192         }
193 
194         //Element access
195         char& operator[](size_t pos)//必须返回真实数据地址
196         {
197             assert(pos < size()); //size_t无符号数不需要判断小于0
198             return _str[pos];
199         }
200         const char& operator[](size_t pos) const
201         {
202             assert(pos < _size);
203             return _str[pos];
204         }
205 
206 
207 
208         //Modifiers
209         void push_back(char ch)
210         {
211             /*
212             if (_size >= _capacity) //满了
213                 reserve(_capacity * 2);
214             _str[_size] = ch;
215             ++_size; //不需要给[_size] = '\0' , 因为\0在[capacity+1]处而不是在[_size]处
216             _str[_size] = '\0'; // \0不算有效字符,不用++_size
217             */
218             insert(_size, ch);
219         }
220         void append(const char* s)
221         {
222             /*
223             size_t len = strlen(s);
224             if (_size+len > _capacity)//需要的容量大于现有容量
225                 reserve(_size+len);
226             strcpy(_str+_size, s); //不用strcat:strcat底层需要自己找\0(如果很长则浪费),strcpy不用找,直接一步到位
227             _size += len;
228             */
229 
230             insert(_size, s);
231         }
232         string& operator+=(char ch) //char 和char &基本一样,但函数内传参引用的引用最好不用, s1+=' '+=""时出错
233         {
234             push_back(ch);
235             return *this;
236         }
237         string& operator+=(const char* s)
238         {
239             append(s);
240             return *this;
241         }
242 
243         string operator+(char ch)
244         {
245             string tmp(*this);
246             tmp += ch;
247             return tmp;
248         }
249 
250         string operator+(const char* s)
251         {
252             string tmp(*this);
253             tmp += s;
254             return tmp;
255         }
256 
257         string& insert(size_t pos, char ch)//插入字符
258         {
259             assert(pos <= _size); //pos在'\0'处也可以插入
260             if (_size - 1 > _capacity) //满了
261                 reserve(_capacity * 2);
262             for (size_t i = _size + 1; i > pos; --i) //当size_t i减到0时,--i会变成最大整数,导致奔溃,所以i只减到1
263             {
264                 _str[i] = _str[i - 1];
265             }
266             _str[pos] = ch;
267             ++_size;
268             //_str[_size] ='\0';   //\0第一次循环就拷贝过去了
269             return *this;
270         }
271         string& insert(size_t pos, const char* s)//插入字符串
272         {
273             assert(pos <= _size);
274             size_t len = strlen(s);
275             if (_size + len > _capacity)//需要的容量大于现有容量
276                 reserve(_size + len);
277             for (size_t i = _size + len; i > pos + len - 1; --i) //当i==0时,i--会变成最大整数,错位一下
278             {
279                 _str[i] = _str[i - len];
280             }
281             strncpy(_str + pos, s, len);
282             _size = _size + len;
283             //_str[_size] = '\0';
284             return *this;
285         }
286         string& erase(size_t pos, size_t len = npos)//起始位置,删除长度 --删1个,删多个都满足
287         {
288             assert(pos < _size); //此处不为_size原因是,删除\0没有意义,没必要加上去
289             if (len == npos || pos + len >= _size) //超出长度 ,前条件不能省略 , 因为后条件超出最大值后可能会溢出
290             {
291                 _str[pos] = '\0';
292                 _size = pos; //size = \0的下标
293             }
294             else
295             {
296                 for (size_t i = pos; i <= _size - len; i++)
297                 {
298                     _str[i] = _str[i + len];
299                 }
300                 _size = _size - len;
301             }
302             return *this;
303         }
304 
305         size_t find(char ch, size_t pos = 0)
306         {
307             assert(pos < _size);//加不加无所谓
308             for (size_t i = pos; i < _size; ++i)
309             {
310                 if (ch == _str[i])
311                 {
312                     return i;
313                 }
314             }
315             return npos;
316         }
317         size_t find(const char* s, size_t pos = 0)
318         {
319             assert(pos < _size);
320 
321             //return strstr(_str + pos, s) - _str;
322             char* p = strstr(_str + pos, s);//原理是BF暴力匹配match ,还有KMP(纸老虎) , BM(最实用)
323             //if (p == nullptr)
324             //    return -1;
325             //else
326             //    return p - _str;
327             return p == nullptr ? npos : p - _str;
328         }
329 
330 
331 
332         //String operator
333         const char* c_str()
334         {
335             return _str;
336         }
337 
338         //Non-member function overloads
339 
340 
341         //relational operators
342         //必须加上const
343         bool operator<(const string& s) const
344         {
345             return strcmp(_str, s._str) < 0;
346         }
347         bool operator==(const string& s) const
348         {
349             return strcmp(_str, s._str) == 0;
350         }
351         bool operator!=(const string& s) const
352         {
353             return !(*this == s);
354         }
355         bool operator<=(const string& s) const
356         {
357             //如果没加const,此时s(const)调用==,s即为==的左操作数*this,==中左操作数为非const,即const调用非const函数,权限放大
358             //return s > *this && s == *this;
359             return *this < s || *this == s;
360         }
361         bool operator>(const string& s) const
362         {
363             return !(*this <= s);
364         }
365         bool operator>=(const string& s) const
366         {
367             return !(*this < s);
368         }
369 
370     };
371 
372     size_t test::string::npos = -1; //类型 (域::)变量名 = 值;
373 
374 
375     test::string to_string(int value)
376     {
377         bool flag = true; //干嘛用的,标记位,说明是正数还是负数,true是正数
378         if (value < 0)
379         {
380             flag = false;
381             value = 0 - value; //为什么要0-value ,使负数变成正数.负负得正,计算机可以实现 
382         }
383         test::string str;
384         while (value > 0)
385         {
386             int x = value % 10;
387             value /= 10;
388             str += std::move('0' + x); //C++支持字符加整型可以合并拼接成字符串,也可以使用atoi 
389                                     //move是因为拼接后的值是将亡值,编译器也会自动加上
390         }
391         if (flag == false)
392         {
393             str += '-';
394         }
395         std::reverse(str.begin(), str.end()); //显然,是倒过来拼接的,需要逆置
396         
397         //return std::move(str); //不需要上,编译器会自动加上
398         return str;
399     }
400 
401 
402     //流插入 和 流提取 (不是必须是友元函数,不是友元也可以 -- 重修,流插入需要支持什么功能?)
403     //Extract string from stream
404     std::ostream& operator<<(std::ostream& out, const string& s)
405     {
406         for (auto ch : s)    //string类要打印到size
407         {
408             out << ch;
409         }
410         //out << s.c_str();  //1.非友元函数,需要调用接口 2.不能直接打印字符串,遇到\0就终止
411         return out;
412     }
413     std::istream& operator>>(std::istream& in, string& s) //此处string要修改,不能加const
414     {
415         s.clear();//每次流提取都需要清掉旧数据。
416 
417         //一定是不能使用C语言的流,因为C和C++的缓冲区是不一样的。getchar什么的都不允许使用
418         char ch = in.get();  // get()是in的成员函数
419         char buff[128];
420         size_t i = 0;
421         while (ch != ' ' && ch != '\n')
422         {
423             buff[i] = ch;
424             ++i;
425             if (i == 127)
426             {
427                 buff[127] = '\0'; //流插入不会给'\0', 会直接覆盖掉原本的'\0',而字符数组也不会给\0,所以需要手动给
428                 s += buff; //+=字符数组(字符串)底层是insert("字符串"),每次都只扩容一次,一步到位,避免了频繁扩容
429                 i = 0;
430             }
431             ch = in.get();
432         }
433         if (i !=0) //0和127都可以,0更好一点,127会比0多走一步无用操作,插一个\0
434         {
435             buff[i] = '\0';
436             s += buff;
437         }
438         return in;
439     }
440 
441     //测试用例
442     /*
443     void Print(const string& s)
444     {
445         string::const_iterator it = s.begin();
446         while (it != s.end())
447         {
448             cout << *it;
449             ++it;
450         }
451 
452         //for (size_t i = 0; i < s.size(); ++i)
453         //{
454         //    cout << s[i];
455         //}
456 
457         cout << endl;
458     }
459 
460     void test1_string()
461     {
462         string s1;
463         string s2("hello");
464         string s3(s2);
465         s2[0] = 'a';
466         string s4 = s3;
467         cout << s1.c_str() << endl;
468         cout << s2.c_str() << endl;
469         cout << s3.c_str() << endl;
470         cout << s4.c_str() << endl;
471     }
472 
473     void test2_string()
474     {
475         string s1("hello world!");
476         string s2(s1);
477         //for (size_t i = 0; i < s1.size(); ++i)
478         //{
479         //    ++s1[i];
480         //}
481         //for (size_t i = 0; i < s1.size(); ++i)
482         //{
483         //    cout << s1[i] << "";
484         //}
485         Print(s1);
486 
487         //string::iterator it = s2.begin();
488         //while (it != s2.end())
489         //{
490         //    cout << ++*it;
491         //    ++it;
492         //}
493         //cout << endl;
494         //for (auto ch : s2)
495         //{
496         //    cout << ch;
497         //}
498     }
499     void test3_string()
500     {
501         string s1 = "hello";
502         string s2 = "Hello";
503         cout << (s1 == s2) << endl;
504         cout << (s1 != s2) << endl;
505         cout << (s1 > s2) << endl;
506         cout << (s1 >= s2) << endl;
507         cout << (s1 < s2) << endl;
508         cout << (s1 <= s2) << endl;
509         cout << s1 << s2 << endl;
510         cout << (s1 == "hello") << endl;
511 
512     }
513     void test4_string() //modifiers
514     {
515         //白盒测试
516         string s1 = "0123456789";
517         //(s1 += ' ');
518         //s1+= "world";
519         //cout << s1 << endl;
520         //s1.insert(0, 'a');
521         //s1.insert(s1.size(), 'a');
522         //s1.insert(0, "aaa");
523         //s1.insert(s1.size(), "aaa");
524         //s1.erase(s1.size());
525         //s1.erase(s1.size() - 2, 1);
526         int ret = s1.find("23");
527         cout << ret << endl;
528         cout << s1 << endl;
529     }
530 
531     void test5_string()
532     {
533         string s1 = "0123456789";
534         s1.resize(3, 'x');
535         s1.resize(6, 'x');
536         s1.resize(15, 'x');
537         s1.resize(7, 'x');
538     }
539 
540     void test6_string()
541     {
542         string s1 = "01234 56789";
543         cout << s1 << endl;
544         cin >> s1;
545         cout << s1 << endl;
546     }
547     
548     */
549 }

 

标签:const,string,STL,char,str,return,模拟,size
From: https://www.cnblogs.com/DSCL-ing/p/18038567

相关文章

  • C++ STL 容器 forward_list类型
    C++STL容器forward_list类型介绍std::forward_list是C++标准模板库(STL)中的一个单向链表容器。与std::list不同,std::forward_list只允许从头部到尾部的单向迭代,不支持反向迭代。因此,std::forward_list在某些操作上可能比std::list更高效,尤其是在插入和删除元素时......
  • STL-vector模拟实现
    #pragmaonce#include<assert.h>#include<iostream>usingstd::cout;usingstd::endl;usingstd::cin;namespacetest{//#include<algorithm>//模板参数省略:1.作为时2.作为类型名template<classT>//数组名:类型名:xx数组classvector......
  • STL-list模拟实现
    #pragmaonce#include"16my_Itetator.h"//测试用#include<iostream>//测试用usingstd::cout;usingstd::endl;usingstd::cin;namespacetest{//struct默认权限是public,一般也不会加权限,class才会(需要封装时使用class)//结点使用struct的好处是开放结点,......
  • STL-stack模拟实现
    #pragmaonce#include<assert.h>#include<list>#include<vector>#include<deque>#include<iostream>usingstd::cout;usingstd::endl;usingstd::cin;namespacetest{//template<classT,classContainers=std::vector&......
  • STL-queue模拟实现
    #include<list>#include<assert.h>#include<deque>#include<iostream>usingstd::cout;usingstd::endl;usingstd::cin;namespacetest{//template<classT,classContainers=std::list<T>>template<classT,c......
  • 从面向对象的角度看集合类,以List<String> list = new ArrayList<>()为例
    心血来潮琢磨了这么一个问题:Listlist=newArrayList<>();这句代码挺常用的,但是如果要从面向对象的角度去讲讲这句代码背后的逻辑,该怎么讲?好像真不容易一口气说完整,于是便有了本文的梳理。从关系图我们可以知道,ArrayList继承自一个抽象类AbstractCollection,它们都实现了Lis......
  • 2024.2.27模拟赛T2题解
    题目有一个神奇的结论\(\foralla<b<c,a\oplusc>min(a\oplusb,b\oplusc)\)然后就可以写出\(n^2\)dp,再用TRIE树优化即可code#include<bits/stdc++.h>usingnamespacestd;#defineN200005#defineintlonglongintn,k1,k2;inta[N],fl[2];constintm......
  • C++ STL 容器 list类型
    C++STL容器list类型list对于异常支持很好,要么成功,要么不会发生什么事情以下是std::list在异常处理方面表现良好的几个原因:动态内存管理:std::list使用动态内存分配来存储元素,这意味着它会在需要时自动分配内存,并在不再需要时释放内存。这种自动管理可以减少内存泄漏和悬......
  • C++ STL 容器-Deque
    C++STL容器-Dequestd::deque(双端队列)是C++标准模板库(STL)中的一个容器,它支持在序列的两端快速插入和删除元素。与std::vector和std::list等其他序列容器相比,std::deque在某些特定场景下具有独特的优势。元素的访问和迭代比vector慢,迭代器不是普通的指针。以下是std::deque的一......
  • 安卓模拟器
    安卓模拟器种类雷电夜神逍遥雷神模拟器本质安卓模拟器的本质是一台小型的虚拟机以雷电9模拟器为例在雷电9模拟器的安装目录下有一个vms文件,里面存储着虚拟机的信息 随便点开一个虚拟机分析文件目录下的三个vmdk文件,与安卓手机的文件系统一一对应!data存储用户......