一:回顾
(1)c++中的string类是在面试中和笔试中经常考的题目; 工程代码免费下载 string类的自行实现
(2)c++中的string类和fstream类合起来是处理外部数据的利器;
(3)string类经常用到find find_first_of find_first_not_of find_last_of find_last_not_of substr replace等,以及联合使用来达到java中的split和trim
(4) 使用friend 仅仅是在类中进行声明的非内部 却可以访问内部成员的外部函数,而且在外部不再需要friend关键字;它与成员函数的区别是,friend和外部函数不含有this对象指针;本文用到了const 定义的全局最大值最小值变量(代替#define)
(5) 有些函数返回的是MyString& 、Char& 等(引用),MyString、Char 等(传值)这得看你返回的对象是函数的局部变量还是全局变量(或者类当前对象成员变量);前者只能返回一个MyString、Char 等;后者强烈建议返回MyString& 、Char& 等(引用);
(6)有些函数的参数是const MyString& ,有些是MyString& (引用);这是为什么?前者是把外部值传提到子函数内部,且不允许改变;后者是作为函数的返回值传递进去的,返回的结果为函数的处理结果(而不用函数自身返回值了)。
二:下面是简单的实现了一下string类,参照的是STL源码,但是自己理解的还是不够深,难免有一些错误,请各位指教
(1)MyString.h文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
#ifndef MYSTRING_H #define MYSTRING_H #include "MyExcept.h" #include <cstring> #include <iostream> const int INI_MAX = 0x7fffffff; //2^32npos const int INI_MIN = 0x80000000; // -2^32 const int npos = 0xffffffff; // npos using namespace std; class MyString { public : // constructor MyString(); // MyString( const MyString &); // MyString( const char *); MyString( const size_t , const char ); // destructor ~MyString(); // attributes size_t length(); // 字符串长度 bool isEmpty(); // 返回字符串是否为空 const char * c_str(); // 返回c风格的trr的指针 // friend funs // read writer operations friend ostream& operator<< (ostream&, const MyString&); friend istream& operator>> (istream&, MyString&); //add operation friend MyString operator+( const MyString&, const MyString&); // compare operations friend bool operator==( const MyString&, const MyString&); friend bool operator!=( const MyString&, const MyString&); friend bool operator<( const MyString&, const MyString&); friend bool operator<=( const MyString&, const MyString&); friend bool operator>( const MyString&, const MyString&); friend bool operator>=( const MyString&, const MyString&); // 成员函数实现运算符重载,其实一般需要返回自身对象的,成员函数运算符重载会好一些 // index operation char & operator[]( const size_t ); const char & operator[]( const size_t ) const ; // = MyString& operator=( const MyString&); // += MyString& operator+=( const MyString&); // += //MyString operator+=(const MyString&); cannot be overloaded // 成员操作函数 // substr MyString substr( size_t pos, const size_t n); // append MyString& append( const MyString&); //insert MyString& insert( size_t , const MyString&); //assign 替换 MyString& assign(MyString&, size_t , size_t ); // erase 删除 MyString& erase( size_t , size_t ); //find_first_of 查找某一个字符 size_t 是非符号数的,重载 // 查找在字符串中第一个与str中的某个字符匹配的字符,返回它的位置。 //搜索从index开始,如果没找到就返回string::npos int find_first_of( const char * str, size_t index=0); int find_first_of( const char ch, size_t index=0); int find_first_of( const MyString &, size_t index=0); // 在字符串中查找第一个与str中的字符都不匹配的字符,返回它的位置。搜索从index开始。如果没找到就返回string::nops int find_first_not_of( const char * str, size_t index=0); int find_first_not_of( const char ch, size_t index=0); int find_first_not_of( const MyString&, size_t index=0); // swap void swap(MyString& lhs,MyString& rhs); // replace_all MyString& replace_all( const char oldc, const char newc=NULL); MyString& replace( size_t index, size_t num1, size_t num2, const char ch); //find int find( const char * str, size_t index=0); int find( const MyString& str, size_t index=0); int find( const char ch, size_t index=0); //private private : char *p_str; size_t strLength; }; #endif // MYSTRING_H |
(2)MyString.cpp文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
|
#include "MyString.h" #include <cassert> // constructor MyString::MyString():p_str(NULL),strLength(0){} MyString::MyString( const MyString &str) // { if (NULL == str.p_str) { return ; } strLength = str.strLength; p_str = new char [strLength+1]; strcpy (p_str,str.p_str); } MyString::MyString( const char *str) { if (NULL == str) { return ; } strLength = strlen (str); p_str = new char [strLength+1]; strcpy (p_str,str); } MyString::MyString( const size_t len, const char ch) { if (NULL == ch) { return ; } strLength = len; p_str = new char [strLength+1]; for ( size_t i=0;i<strLength;i++) { p_str[i] = ch; } p_str[strLength] = '\0' ; // 因为strset以'\0'结束的 cout << p_str << " &&" << endl; //strset(p_str,ch); //cout << p_str[0] << ",,,"<<strlen(p_str) << "," << strLength << endl; } // destructor MyString::~MyString() { delete [] p_str; } // attributes size_t MyString::length() // 字符串长度 { return strLength; } bool MyString::isEmpty() // 返回字符串是否为空 { return strLength==0? true : false ; } const char * MyString::c_str() { return p_str; } // 为什么不是引用呢??? friend 使用在类里面进行声明的,外面就不需要了,而且友元函数不属于类的成员函数,所以不用MyString:: // ostream ostream& operator<< (ostream& out, const MyString &str) { if (str.p_str != NULL) { out << str.p_str; } return out; } // istream,一个是const另一个不是,根据变还是不变 istream& operator>> (istream& in, MyString& str) { char tmp[100]; // 临时字符串 if (in>>tmp) { delete [] str.p_str; str.strLength = strlen (tmp); str.p_str = new char [str.strLength+1]; strcpy (str.p_str,tmp); } return in; } // + 加 MyString operator+( const MyString& lhs, const MyString& rhs) { MyString ret; ret.strLength = lhs.strLength + rhs.strLength; ret.p_str = new char [ret.strLength+1]; strcpy (ret.p_str,lhs.p_str); strcat (ret.p_str,rhs.p_str); return ret; } // compare operations bool operator==( const MyString& lhs, const MyString& rhs) { return strcmp (lhs.p_str,rhs.p_str)==0? true : false ; } bool operator!=( const MyString& lhs, const MyString& rhs) { return strcmp (lhs.p_str,rhs.p_str)!=0? true : false ; } bool operator<( const MyString& lhs, const MyString& rhs) { return strcmp (lhs.p_str,rhs.p_str)<0? true : false ; } bool operator<=( const MyString& lhs, const MyString& rhs) { return strcmp (lhs.p_str,rhs.p_str)<=0? true : false ; } bool operator>( const MyString& lhs, const MyString& rhs) { return strcmp (lhs.p_str,rhs.p_str)>0? true : false ; } bool operator>=( const MyString& lhs, const MyString& rhs) { return strcmp (lhs.p_str,rhs.p_str)>=0? true : false ; } // 成员函数实现运算符重载 // index operation char & MyString::operator[]( const size_t index) { if (index<0 || index>=strLength) { throw Outofbond() ; } return p_str[index]; } const char & MyString::operator[]( const size_t index) const { if (index<0 || index>=strLength) { throw Outofbond(); } return p_str[index]; } // = 赋值构造函数(判断是否是自身) 为什么要这样删除呢? MyString& MyString::operator=( const MyString& other) { if ( this != &other) { if (strLength<other.strLength) { delete [] p_str; p_str = new char [other.strLength+1]; } strLength = other.strLength; strcpy (p_str,other.p_str); } // 这样可能会产生多余的未释放的空间 return * this ; } // += 相当于返回的是备份的,内部对象的销毁,不影响的 和 下面的完全不一样的 // MyString MyString::operator+=(const MyString& other) // { // if(NULL == other.p_str) // { // return *this; // } // MyString ret; // ret.strLength = strLength + other.strLength; // ret.p_str = new char[ret.strLength+1]; // strcpy(ret.p_str,p_str); // strcat(ret.p_str,other.p_str); // return ret; // } // 返回的是当前对象的引用,当前对象就在调用函数里,所以不会销毁的 // 判断一下是否是自身相加 MyString& MyString::operator+=( const MyString& other) { if (NULL == other.p_str) { return * this ; } if ( this == &other) { MyString copy(* this ); return * this += copy; } // 必须判断是否相等的,而且要+=的,这样相当于调用了自身,但是这次直接下面去了,不进入if的 strLength += other.strLength; //strLength *= 2; char *p_old = p_str; p_str = new char [strLength+1]; strcpy (p_str,p_old); strcat (p_str,other.p_str); delete [] p_old; // 删除旧的空间 return * this ; } // 成员操作函数 // substr 返回应用是不行的,错误的;取从pos开始的n个字符组成的子串 //MyString& MyString::substr(size_t pos,const size_t n) MyString MyString::substr( size_t pos, const size_t n) { if ((pos+n)>=strLength) { throw Outofbond(); } MyString ret; ret.strLength = n; //ret.p_str = new char[n+1]; ret.p_str = new char [ret.strLength+1]; //也可以 for ( size_t i=0;i<n;i++) { ret.p_str[i] = p_str[pos+i]; } ret.p_str[n] = '\0' ; // for(size_t i=0;i<ret.strLength;i++) // { // ret[i] = (*this)[pos+i]; // cout << ret[i] << ",,"; // }// 也行的,利用刚刚重载的【】,这样更好,不用再次判断越界了,不知道为什么,报错误的 // ret[ret.strLength] = '\0'; return ret; } // append 同 += 追加到末尾 MyString& MyString::append( const MyString& other) { * this += other; // 利用刚刚那重载的+= return * this ; } //insert 从pos开始的插入other MyString& MyString::insert( size_t pos, const MyString& other) { if (pos<0 || pos>=strLength) { throw Outofbond(); } char *p_old = p_str; strLength += other.strLength; p_str = new char [strLength+1]; for ( size_t i=0;i<pos;i++) { *(p_str+i) = *(p_old+i); } for ( size_t i=pos;i<other.strLength+pos;i++) { *(p_str+i) = other.p_str[i-pos]; } for ( size_t i=other.strLength+pos;i<strLength;i++) { *(p_str+i) = p_old[i-other.strLength]; } *(p_str+strLength) = '\0' ; return * this ; } //assign 替换 用other的POS开始的n对应替换this的pos开始的 MyString& MyString::assign(MyString&other, size_t pos, size_t n) { // if(pos<0 || pos>=strLength) // { // throw Outofbond(); // } assert (pos>0 && pos<strLength); // assert 的好处 assert (pos+n<other.strLength); if (strLength < pos + n) { char *p_old = p_str; strLength = pos + n; p_str = new char [strLength+1]; for ( size_t i=0;i<pos;i++) { *(p_str+i) = *(p_old+i); } delete [] p_old; } for ( size_t i=pos;i<pos+n;i++) { *(p_str+i) = other.p_str[i]; } *(p_str+pos+n) = '\0' ; return * this ; } // erase 删除 这个方法并不是很好的,并没有释放所erase的空间,请看下面的 // MyString& MyString::erase(size_t pos,size_t n) // { // if((pos+n)>strLength) // { // throw Outofbond(); // } // size_t index = pos + n; // while(*(p_str+index)!='\0') // { // *(p_str+index-n) = *(p_str+index); // ++index; // } // *(p_str+index-n) = '\0'; // return *this; // } // erase 删除 从pos开始的n个字符 MyString& MyString::erase( size_t pos, size_t n) { if ((pos+n)>strLength) { throw Outofbond(); } char *p_old = p_str; strLength -= n; p_str = new char [strLength+1]; for ( size_t i=0;i<pos;i++) { p_str[i] = p_old[i]; } for ( size_t i=pos;i<strLength;i++) { p_str[i] = p_old[i+n]; } *(p_str+strLength) = '\0' ; return * this ; } //find_first_of 查找某一个字符 size_t 是非符号数的 // 查找在字符串中第一个与str中的某个字符匹配的字符,返回它的位置。 //搜索从index开始,如果没找到就返回string::npos int MyString::find_first_of( const char * str, size_t index) { if (NULL == str || index >=strLength) return npos; int tmp_len = strlen (str),j; size_t flag,min_index = INI_MAX; for (j=0;j<tmp_len;j++) { flag = npos; for ( size_t i=index;i<strLength;i++) { if (str[j] == p_str[i]) { flag = i; break ; } } // indexs[j] = flag; if (flag != npos) { min_index = min_index<flag?min_index:flag; } } // for(j=0;j<tmp_len;j++) // { // if(indexs[j]!=npos) // min = min<indexs[j]?min:indexs[j]; // } if (min_index == INI_MAX) { return npos; // min_index = npos; // cout << "---npos----" << min_index << ",,,,"; } return min_index; } int MyString::find_first_of( const char ch, size_t index) { if (NULL == ch || index >=strLength) return npos; int j; size_t flag = npos; for ( size_t i=index;i<strLength;i++) { if (ch == p_str[i]) { flag = i; break ; } } return flag; } int MyString::find_first_of( const MyString& str, size_t index) { if (NULL == str || index >=strLength) return npos; int j; size_t flag,min_index = INI_MAX; for (j=0;j<str.strLength;j++) { flag = npos; for ( size_t i=index;i<strLength;i++) { if (str[j] == p_str[i]) { flag = i; break ; } } if (flag != npos) { min_index = min_index<flag?min_index:flag; } } if (min_index == INI_MAX) { return npos; } return min_index; } // 在字符串中查找第一个与str中的字符都不匹配的字符,返回它的位置。 //搜索从index开始。如果没找到就返回string::nops O(N^2) int MyString::find_first_not_of( const char *str, size_t index) { if (NULL == str || index >=strLength) return npos; size_t i=0,j=0; size_t tmp_len = strlen (str); for (i=index;i<strLength;i++) { for (;j<tmp_len;j++) { if (p_str[i]==str[j]) break ; } if (j==tmp_len) break ; // 根据跳出的内层for的条件判断,找到即结束循环 } if (i==strLength) return npos; // 未找到,// 根据跳出的内层for的条件判断,找到即结束循环 return i; } int MyString::find_first_not_of( const MyString& str, size_t index) { if (NULL == str || index >=strLength) return npos; size_t i=0,j=0; for (i=index;i<strLength;i++) { for (;j<str.strLength;j++) { if (p_str[i]==str[j]) break ; // 如果相等 本轮i就无效了,进行下一轮 } if (j==str.strLength) break ; // 根据跳出的内层for的条件判断,找到即结束循环 } if (i==strLength) return npos; // 未找到,// 根据跳出的内层for的条件判断,找到即结束循环 return i; } int MyString::find_first_not_of( const char ch, size_t index) { if (NULL == ch || index >=strLength) return npos; size_t i=0; for (i=index;i<strLength;i++) { if (p_str[i]!=ch) // 跟上面的略微不同,找一个不等就可以了 break ; } if (i==strLength) return npos; // 未找到,// 根据跳出的内层for的条件判断,找到即结束循环 return i; } // swap 都得变得,所以非const void MyString::swap(MyString& lhs,MyString& rhs) { lhs.strLength ^= rhs.strLength; rhs.strLength ^= lhs.strLength; lhs.strLength ^= rhs.strLength; char *p_tmp = rhs.p_str; rhs.p_str = lhs.p_str; lhs.p_str = p_tmp; } // replace_all 这个东西还是不太好弄的啊,不是很理想 MyString& MyString::replace_all( const char oldc, const char newc) { if (NULL == oldc) { return *( this ); } for ( size_t i=0;i<strLength;i++) { if (p_str[i] == oldc) { p_str[i] = newc; } } return *( this ); } MyString& MyString::replace( size_t index, size_t num1, size_t num2, const char ch) { } // find 函数 int MyString::find( const char * str, size_t index) { assert (str!=NULL&&index<strLength); // kmp 中的getnext函数 size_t len = strlen (str); size_t next[len+1]; size_t j,k; next[0] = npos; j = 0; k = npos; while (j<len) { if (k==npos || str[j]==str[k]) { j++; k++; next[j] = k; } else k = next[k]; } // kmp 算法 k = index; j = 0; while (p_str[k]!= '\0' ) { if (j==0 || p_str[k]==str[j]) { k++; j++; } else { j = next[j]; // 消除指针回溯 } if (str[j] == '\0' ) //匹配成功 return k-j; } return npos; } int MyString::find( const MyString& str, size_t index) { // if(this == &str) // { // MyString other(*this); // find(other,index); // } assert (NULL!=str && index<strLength); // kmp 中的getnext函数 size_t next[str.strLength+2]; size_t j,k; next[0] = npos; j = 0; k = npos; while (j<str.strLength) { if (k==npos || str.p_str[j]==str.p_str[k]) { j++; k++; next[j] = k; } else k = next[k]; } int i; for (i=1;i<=j;i++) cout << next[i] << "," ; // kmp 算法 k = index; j = 0; while (p_str[k]!= '\0' ) { if (j==0 || p_str[k]==str.p_str[j]) { k++; j++; } else { j = next[j]; // 消除指针回溯 } if (str.p_str[j] == '\0' ) //匹配成功,不知道为什么调用自身的str[]重载总是报错的 return k-j; } if (str.p_str[j] == '\0' ) // 同一个字符串 return k-j; return npos; } int MyString::find( const char ch, size_t index) { assert (NULL!=ch && index<strLength); for ( size_t i=index;i<strLength;i++) { if (p_str[i] == ch) return i; } return npos; } |
(3)测试函数main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
#include "MyString.h" #include <iostream> using namespace std; int main() { int n; int choose = 1; int p,l; char cs[100]; MyString s1; MyString s2( "hello" ); MyString s3 = "HELLO" ; cout << "***** welcome *****\n" ; cout << "******* MADE BY zyp **********\n" ; cout << "s1= " << s1 << "s2= " << s2 << "s3= " << s3 << endl; cout << "请输入一个长度小于100的字符串:例如world\n" ; cin >> s1; s1 = s1; //s1 = s1+s1; s1 += s1; MyString s4(s1); s4.append(s1); s2.insert(2,s3); s1.erase(4,4); s1.assign(s2,1,7); cout << "s1= " << s1 << "s2= " << s2 << "s3= " << s3 << "s4= " << s4 << endl; s2 = s4.substr(2,7); cout << "s4[3]= " << s4[3] << s4.length() << (s1>=s2) << "s4.substr() " << s2 << endl; cout << "s1.find_first_of(beLE,2):" << s1.find_first_of( "beLE" ,2) << ",s1.find_first_of(a,3):" << s1.find_first_of( 'a' ,3) << ",s1.find_first_of(s3,2):" << s1.find_first_of(s3,2) << endl; MyString s5(5, 'b' ); s5 += s5; //s5.append(s5);// 不知道为什就是不能append cout << "s5 = " << s5 << "s5.find_first_not_of(aeHLEOl,2):" << s5.find_first_not_of( "aeHLEOl" ,2) << "s5.find_first_not_of(aeHLEOl,0):" << s5.find_first_not_of( "aeHLEOl" ) << endl; cout << "s5.find_first_not_of(s1,2):" << s5.find_first_not_of(s1,2) << "s5.find_first_not_of(b,2):" << s5.find_first_not_of( 'b' ,2) << endl; swap(s1,s5); s5.replace_all( 'a' , 'J' ); MyString s6( "LLO" ); cout << s1 << "," << s5 << "s5.find(LLO,0) " << s5.find( "LLO" ,0) << "s5.find(s6,0) " << s5.find(s5) << endl; cout << npos << endl; return 0; } |
三:感悟
(1)耗时将近2天的实现了它,自己与其从中学到了很多,倒不如说是重新认识了string类;
(2)自己知道这个简单的string类,距离string源代码还差的很远很远;但是它帮助我更好的理解了string类,至少会简单的应用了。
(3)简单的实现了一下string类,参照的是STL源码,但是自己理解的还是不够深,难免有一些错误,请各位指教,万分感谢!
(4)下一步进军list
原文链接:http://blog.csdn.net/u010700335/article/details/40979037