STL-string模拟实现

  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 }

 

posted @ 2024-02-27 22:22  HJfjfK  阅读(16)  评论(0编辑  收藏  举报