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 }
本文来自博客园,作者:HJfjfK,原文链接:https://www.cnblogs.com/DSCL-ing/p/18038567