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 }

STL-string模拟实现的更多相关文章

  1. STL string 模拟

    下面的代码来自c++ primer plus第5版第12章,书中代码写的非常好: // string1.h -- fixed and augmented string class definition ...

  2. 深入剖析 linux GCC 4.4 的 STL string

    转自: 深入剖析 linux GCC 4.4 的 STL string 本文通过研究STL源码来剖析C++中标准模板块库std::string运行机理,重点研究了其中的引用计数和Copy-On-Wri ...

  3. 格式字符串分配stl::string

    代码非常easy,不解释,直接在代码: #include <cstdio> #include <cstdarg> #include <iostream> using ...

  4. 浅谈C++ STL string容器

    浅谈C++ STL string容器 本篇随笔简单讲解一下\(C++STL\)中\(string\)容器的使用方法及技巧. string容器的概念 其实\(string\)并不是\(STL\)的一种容 ...

  5. C++标准模板库Stand Template Library(STL)简介与STL string类

    参考<21天学通C++>第15和16章节,在对宏和模板学习之后,开启对C++实现的标准模板类STL进行简介,同时介绍简单的string类.虽然前面对于vector.deque.list等进 ...

  6. 转C++之stl::string写时拷贝导致的问题

    前几天在开发某些数据结构到文件的 Dump 和 Load 功能的时候, 遇到的一个 bug . [问题复现] 问题主要出在 Load 过程中,从文件读取数据的时候, 直接使用 fread 的去操作 s ...

  7. stl string

    10.2.1 STL的string 1String概念 ²  string是STL的字符串类型,通常用来表示字符串.而在使用string之前,字符串通常是用char*表示的.string与char*都 ...

  8. 洛谷 P1739 表达式括号匹配【STL/stack/模拟】

    题目描述 假设一个表达式有英文字母(小写).运算符(+,-,*,/)和左右小(圆)括号构成,以"@"作为表达式的结束符.请编写一个程序检查表达式中的左右圆括号是否匹配,若匹配,则返 ...

  9. [转载] C++ STL string的Copy-On-Write技术

    原文: http://coolshell.cn/articles/12199.html stl的string是经过严格优化的, 深入理解对以后编程过程中应用string非常有益处, 感谢左耳朵耗子的精 ...

  10. [转] 深入剖析 linux GCC 4.4 的 STL string

    本文通过研究STL源码来剖析C++中标准模板块库std::string运行机理,重点研究了其中的引用计数和Copy-On-Write技术. 平台:x86_64-redhat-linux gcc ver ...

随机推荐

  1. C# 使用字典将枚举转换为String

    枚举 public enum ColorType { Red = 10, Blue = 20, Green = 30, Yellow = 40, } String var A1 = "AAA ...

  2. C#对象属性浅拷贝和深拷贝

    对象属性和字段拷贝的几种方式 微软提供了浅拷贝 对于值类型,修改拷贝的值不会影响源对象 对于引用类型,修改拷贝后的值会影响源对象,但string特殊,它会拷贝一个副本,互相不会影响 自己实现深拷贝,我 ...

  3. 脑科学与人工神经网络ANN的发展历程与最新研究

    本文深入研究了ANN的基本概念.发展背景.应用场景以及与人脑神经网络的关系. 关注TechLead,分享AI全维度知识.作者拥有10+年互联网服务架构.AI产品研发经验.团队管理经验,同济本复旦硕,复 ...

  4. .NET 6 使用 System.Drawing.Common 出现 The type initializer for ‘Gdip’ threw an exception 异常的解决办法

    出现问题的原因 在Linux环境部署.NET Core程序时,如果要到System.Drawing.Common引用会出现该问题,目前大量的第三方组件使用该Windows专用库,尤其是涉及图片处理.W ...

  5. paddle DeBug 三步定位PARL飞桨报错原因,快速解决程序问题

    相关文章: [一]-环境配置+python入门教学 [二]-Parl基础命令 [三]-Notebook.&pdb.ipdb 调试 [四]-强化学习入门简介 [五]-Sarsa&Qlea ...

  6. python函数:匿名函数,闭包,装饰器

    匿名函数 可以只有一个入参或多个入参,但返回值只能是一个函数 #普通函数 def sum(a,b): return a+b #等价的匿名函数 add = lambda a,b: a+b  闭包 举一个 ...

  7. SpringBoot2.7集成Swagger3

    1.引入pom坐标 <!--swagger--> <dependency> <groupId>io.springfox</groupId> <ar ...

  8. MySQL拓展知识

    一:视图 1:创建视图 1 --格式:CREATE VIEW 视图名称 AS SQL语句 2 CREATE VIEW v1 AS 3 SELET nid, 4 name 5 FROM 6 A 7 WH ...

  9. Swift中指针UnsafePointer的常见用法

    指针类型 //基本指针 UnsafePointer<T> const T * UnsafeMutablePointer T * //集合指针 UnsafeBufferPointer con ...

  10. ElasticSearch7.3学习(一)----采用restful风格 基本的增删查改语句

    1 .新建图书索引 首先建立图书索引 book 语法:put /index PUT /book 结果 2.新增图书 :新增文档 语法:PUT /index/type/id PUT /book/_doc ...