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. 图片三像素问题如何解决css

    一.提出问题 在浏览器中,图片有一个下间隙问题,有人也称之为图片3像素BUG 1.这并不是什么浏览器bug,而只是英文字母书写时有个基线的问题,基线决定了图片的对其方式.这才是造成浏览器中图片下间隙的 ...

  2. HEVC扩展备用安装方法

    这个玩意微软商店免费但是下架了,购买需要RMB 安装 转到 https://store.rg-adguard.net/ 在左侧的下拉菜单选择"ProductId" 把链接中&quo ...

  3. TienChin-课程管理-创建工程

    创建方式与之前一样,如下奉上 generateCourse 代码. @Test void generateCourse() { String path = "E:\\Desktop\\Tie ...

  4. TienChin 活动管理-修改活动接口

    前端 activity.js 直接替换现有的,最求速度了,后面在详细一个个记录,不在过多解释了. import request from '@/utils/request' /** * 查询活动列表 ...

  5. 第三届人工智能,大数据与算法国际学术会议 (CAIBDA 2023)

    第三届人工智能,大数据与算法国际学术会议 (CAIBDA 2023) ​ 大会官网:http://www.caibda.org/ 大会时间:2023年6月16-18日 大会地点:中国郑州 截稿日期:2 ...

  6. Python 解析JSON实现主机管理

    JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它以易于阅读和编写的文本形式表示数据.JSON 是一种独立于编程语言的数据格式,因此在不同的编程语言中都有对 ...

  7. 如何控制Tomcat的catalina.out的大小

    catalina.out文件,数据主要来源为:System.out 和 System.err 在控制台上直接输出的信息. 编码时应避免使用System.out.println()和e.printSta ...

  8. ElasticSearch7.3学习(七)----Mapping映射入门

    1.mapping映射 概念:自动或手动为index中的_doc建立的一种数据结构和相关配置,简称为mapping映射.插入几条数据,让es自动为我们建立一个索引 PUT /website/_doc/ ...

  9. iSCSI的客户端messages频繁报错问题解决

    问题现象: 在自己的工作站中安装的RAC测试环境,使用了iSCSI模拟共享存储,环境运行OK,但是在messages信息中频繁报错如下: [root@db01rac2 ~]# tail -20f /v ...

  10. Excel快速调整单元格行高和列宽

    之前使用的是鼠标双击的方法,但是只适用于少量调整时. 今天给同事编辑公众号文章,有一大篇表格在word中,直接从word中复制到公众号的话,格式会有一定程度的错位. 于是先粘贴到excel中处理,但到 ...