字符串類——字符串類的創建(上)


1,歷史遺留問題:

       1,C 語言不支持真正意義上的字符串;

       2,C 語言用字符數組和一組函數實現字符串操作;

              1,字符數組模擬字符串;

              2,字符數組以 \0 來結束就是合法字符串;

              3,C 語言中沒有單獨類型支持字符串,要么是字符數組,要么是 char* 指針;

       3,C 語言不支持自定義類型,因此無法獲得字符串類型;

      

2,從 C 到 C++ 的進化過程引入了自定義類型;

     在 C++ 中可以通過類完成字符串類型的定義;

     C++ 中的原生類型不包含字符串類型,要兼容 C 語言;

  

3,C++ 中通過庫來支持字符串類型:

       1,stl 中就有 string 類,這是官方承認的 string 類型;

    2,Qt 中提供 QString;

    3,MFC 中提供 CString;

  

4,數據結構庫中也應該包含字符串庫,否則拿出去應該沒人使用的,本文就是設計自己的 String 類;

 

5,DTLib 中字符串類的設計:

 

       1,所以字符串類的設計方案基本一致,只不過是出自不同廠商,對應的類型不同,在對應的類型下面使用設計的方法確是一樣的;

       2,繼承自 Object,依賴 C 語言關於字符串函數的一個包(函數集);

       3,要注意設計的成員函數的先后秩序;

      

6,DTLib 中字符串類的實現(本博文中實現了包括KMP算法應用在內的一系列字符串功能函數,包括后續“字符串類——字符串類的創建(下)”、“字符串類———KMP子串查找算法”、“字符串類——KMP算法的應用”博文中的內容,不限於下圖):

 

      

7,實現時的注意事項:

       1,無縫實現 String 對象與 char* 字符串的互操作;

       2,操作符重載函數需要考慮是否支持 const 版本;

       3,通過 C 語言中的字符串函數實現 String 的成員函數;

      

8,C 語言中的字符串類型:

       1,常量字符串:

              1,const char* string,string 指向字符數組首地址;

              2,char string[],string[] 為字符數組;

       2,即要么是字符串數組本身,要么是指向字符數組首地址(一般的是這樣)的 char*;

       3,本質還是字符數組;

 

9,字符串類 String 的實現:

  1,String.h 的實現:

  1 #ifndef DTSTRING_H
  2 #define DTSTRING_H
  3 
  4 #include "Object.h"
  5 
  6 namespace DTLib
  7 {
  8 
  9 class String : public Object // 庫規則中每個類都要繼承自頂層父類 Object
 10 {
 11 protected:   // 面相對象的技術封裝 C 語言中的字符串實現。
 12     char* m_str;  // 指向字符串,字符串的表現形式是字符數組;
 13    int m_length;  // 當前字符串的長度;
 14 
 15     void init(const char* s);  // 初始化函數
 16     /* 比對函數,為 startWith() 服務,前兩個參數為字符數組的首地址,第三個參數是字符數組長度;長度范圍內字符數組對應元素都相等,返回真; */
 17     bool equal(const char* l, const char* r, int len) const;
 18     static int* make_pmt(const char* p);
 19    static int kmp(const char* s, const char* p);
 20 
 21 public:
 22     String();
 23     String(char c);
 24     String(const char* s);
 25    String(const String& s); //  拷貝構造函數;
 26 
 27     int length() const;  // 得到字符串長度;
 28    const char* str()  const; // 字符串對象與傳統字符串進行互操作的轉換函數
 29 
 30     /* 判斷當前的字符對象是否以 s 開頭,判斷當前的字符對象是否以 s 結束 */
 31     bool startWith(const char* s) const;
 32     bool startWith(const String& s) const;
 33     bool endOf(const char* s) const;
 34    bool endOf(const String& s) const;
 35 
 36     /* 將字符串 s 插入到對象下標為 i 處,返回 String& 是為了鏈式操作,返回字符串自己 */
 37     String& insert(int i, const char* s);
 38    String& insert(int i, const String& s);
 39 
 40     /* 去掉字符串中的空格 */
 41    String& trim();
 42 
 43     int indexOf(const char* ) const;
 44    int indexOf(const String& s) const;
 45 
 46     /* 刪除字符串中的子串 s */
 47     String& remove(int i, int len); // 刪除下標 i 處指定長度 len 的長度;
 48     String& remove(const char* s);
 49    String& remove(const String& s);
 50 
 51     /* 用 s 替換字符串中的 t */
 52     String& replace(const char* t, const char* s);
 53     String& replace(const String& t, const char* s);
 54     String& replace(const char* t, const String& s);
 55    String& replace(const String& t, const String& s);
 56 
 57     /* 提取以 i 為起點去長度為 len 的子串 */
 58    String sub(int i, int len) const; // 因為這里不會改變當前字符串狀態,所以為 const 成員函數;
 59 
 60     /* 字符串對象應該能夠像字符數組一樣,通過每一個下標來訪問每一個字符; */
 61     char& operator [] (int i); // 引用意味着可以被賦值,可以出現在賦值符號左邊(此時是對象),給沒有被 const 修飾的版本用;
 62    char operator [] (int i) const;  // 不能作為左值,所以不能返回引用對象;給 const 修飾的常對象版本使用
 63 
 64     /* 比較操作符重載函數,兼容字符串對象與 C 語言中 const char* 所代表的字符串中的邏輯操作;封裝 strcmp 函數完成 ;const 版本給被 const 修飾的常對象使用的,未被 const 修飾的對象也可以作為參數被調用;*/
 65     bool operator == (const String& s) const;
 66     bool operator == (const char* s) const;
 67     bool operator != (const String& s) const;
 68     bool operator != (const char* s) const;
 69     bool operator > (const String& s) const;
 70     bool operator > (const char* s) const;
 71     bool operator < (const String& s) const;
 72     bool operator < (const char* s) const;
 73     bool operator >= (const String& s) const;
 74     bool operator >= (const char* s) const;
 75     bool operator <= (const String& s) const;
 76    bool operator <= (const char* s) const;
 77 
 78     
 79    /* 加法操作符重載函數 */
 80     String operator + (const String& s) const;
 81     String operator + (const char* s) const;
 82     String& operator += (const String& s); 
 83    String& operator += (const char* s);
 84 
 85     /* 減法操作符重載函數 */
 86     String operator - (const String& s) const;
 87     String operator - (const char* s) const;
 88     String& operator -= (const String& s);  //成員會改變的,所以不能用const修飾了
 89    String& operator -= (const char* s);
 90 
 91     /* 賦值操作符重載函數 */
 92     String& operator = (const String& s);
 93     String& operator = (const char* s);
 94    String& operator = (char c);  // 加上一個字符
 95 
 96     ~String();
 97 };
 98 
 99 }
100 
101 #endif // DTSTRING_H

 2,String.cpp 的實現:

  1 #include <cstring>
  2 #include <cstdlib>
  3 #include "DTString.h"
  4 #include "Exception.h"
  5 
  6 using namespace std;
  7 namespace DTLib
  8 {
  9 
 10 /* 建立指定字符串的 pmt(部分匹配表)表 */
 11 int* String::make_pmt(const char* p)  //  O(m),只有一個 for 循環
 12 {
 13     int len = strlen(p);
 14    int* ret = static_cast<int*>(malloc(sizeof(int) * len));
 15 
 16     if ( ret != NULL )
 17     {
 18         int ll = 0; //定義 ll,前綴和后綴交集的最大長度數,largest length;第一步
 19         ret[0] = 0;  // 長度為 1 的字符串前后集都為空,對應 ll 為 0;
 20         for(int i=1; i<len; i++)  // 從第一個下標,也就是第二個字符開始計算,因為第 0 個字符前面已經計算過了; 第二步
 21         {
 22             /* 算法第四步 */
 23             while( (ll > 0) && (p[ll] != p[i]) ) // 當 ll 值為零時,轉到下面 if() 函數繼續判斷,最后賦值與匹配表,所以順序不要錯;
 24             {
 25                 ll = ret[ll - 1];  // 從之前匹配的部分匹配值表中,繼續和最后擴展的那個字符匹配
 26             }
 27 
 28             /* 算法的第三步,這是成功的情況 */
 29             if( p[ll] == p[i] ) // 根據 ll 來確定擴展的種子個數為 ll,而數組 ll 處就處對應的擴展元素,然后和最新擴展的元素比較;
 30             {
 31                 ll++;   // 若相同(與假設符合)則加一
 32             }
 33 
 34             ret[i] = ll;   // 部分匹配表里存儲部分匹配值 ll
 35         }
 36    }
 37 
 38     return ret;
 39 }
 40 
 41 /* 在字符串 s 中查找子串 p */
 42 int String::kmp(const char* s, const char* p)  // O(m) + O(n) ==> O(m+n), 只有一個 for 循環
 43 {
 44     int ret = -1;
 45     int sl = strlen(s);
 46     int pl = strlen(p);
 47    int* pmt = make_pmt(p);
 48 
 49     if( (pmt != NULL) && (0 < pl) && (pl <= sl) ) // 判斷查找條件
 50     {
 51         for(int i=0, j=0; i<sl; i++)  // i 的值要小於目標竄長度才可以查找
 52         {
 53             while( (j > 0) && (s[i] != p[j]) ) // 比對不上的時候,持續比對,
 54             {
 55                 j = pmt[j-1];  //移動后應該繼續匹配的位置,j =j-(j-LL)= LL = PMT[j-1]
 56             }
 57 
 58             if( s[i] == p[j] )  // 比對字符成功
 59             {
 60                 j++;   // 加然后比對下一個字符
 61             }
 62 
 63             if( j == pl )  // 這個時候是查找到了,因為 j 增加到了 pl 的長度;
 64             {
 65                 ret = i + 1 - pl; // 匹配成功后,i 的值停在最后一個匹配成功的字符上,這樣就返回匹配成功的位置
 66                 break;
 67             }
 68         }
 69    }
 70 
 71    free(pmt);
 72 
 73     return ret;
 74 }
 75 
 76 /* 通過參數 s,具體產生當前字符串對象當中的數據,供構造函數使用;實現的方法就是封裝 */
 77 void String::init(const char* s)
 78 {
 79    m_str = strdup(s);  // 當前字符串當中的數據通過 m_str 指針指向;
 80 
 81     if( m_str )  // 復制失敗會返回空指針;
 82     {
 83         m_length = strlen(m_str);  // 獲取長度;
 84     }
 85     else
 86     {
 87         THROW_EXCEPTION(NoEnoughMemoryException, "No memory to creat String object ...");
 88     }
 89 }
 90 
 91 /* 比對函數,為 startWith() 服務,前兩個參數為字符數組的首地址,第三個參數是字符數組長度;長度范圍內字符數組對應元素都相等,返回真; */
 92 bool String::equal(const char* l, const char* r, int len) const
 93 {
 94     bool ret = true;
 95     for(int i=0; i<len && ret; i++)  // 這里的 ret 看似沒用,實則只要有元素不相等就停止循環、非常重要;
 96     {
 97         ret = ret && (l[i] == r[i]);  // 如果有一個位置的字符不相等,則結束比較,返回 false;
 98    }
 99 
100     return ret;
101 }
102 
103 String::String()
104 {
105     init("");
106 }
107 
108 String::String(const char* s)
109 {
110     init(s ? s : ""); // 將空指針轉換為空字符串,s 正確了返回 s,錯誤了返回 “”(這是空字符串)
111 }
112 
113 String::String(const String& s)
114 {
115     init(s.m_str);
116 }
117 
118 /* 字符作為初始值創建字符串對象 */
119 String::String(char c)
120 {
121     char s[] = {c, '\0'};  // 用字符數組模擬字符串的初始化方式,這是兩種初始化中的一種;
122     init(s);
123 }
124 
125 int String::length() const
126 {
127     return m_length;
128 }
129 
130 /* 字符串對象與傳統字符串進行互操作的轉換函數 */
131 const char* String::str() const  // 直接返回字符串首地址
132 {
133     return m_str; // 字符串對象本身就是通過字符指針來指向的,這樣就可以直接轉換;
134 }
135 
136 /* 判斷當前的字符對象是否以 s 開頭 */
137 bool String::startWith(const char* s) const
138 {
139    bool ret = (s != NULL);
140 
141     if( ret )
142     {
143         int len = strlen(s);
144         ret = (len < m_length) && equal(m_str, s, len);  // 如果參數字符串 s 長度比當前字符串長度更長,直接返回 false;
145    }
146 
147     return ret;
148 }
149 
150 bool String::startWith(const String& s) const
151 {
152     return startWith(s.m_str); // 代碼復用了上面的
153 }
154 
155 bool String::endOf(const char* s) const  // s 這個字符串是否是以字符開始
156 {
157    bool ret = (s != NULL);
158 
159     if( ret )
160     {
161         int len = strlen(s);
162         char* str = m_str + (m_length - len);  // 計算最后 n 個字符表示的字符串;
163         ret = (len < m_length) && equal(str, s, len);  // 如果參數字符串 s 長度比當前字符串長度更長,直接返回 false;
164    }
165 
166     return ret;
167 }
168 
169 bool String::endOf(const String& s) const
170 {
171     return endOf(s.m_str);  // 代碼復用了上面的
172 }
173 
174 /* 第 i 個位置插入字符串 s,返回字符串對象是為了實現鏈式操作 */
175 String& String::insert(int i, const char* s)
176 {
177     if( (0 <= i) && (i <= m_length) )
178     {
179         if( (s != NULL) && (s[0] != '\0') )  // 不為空和空字符串;
180         {
181             int len = strlen(s);
182             char* str = reinterpret_cast<char*>(malloc(m_length + len + 1)); 
183 
184             if( str != NULL )
185             {
186                 strncpy(str, m_str, i); // 當前字符串的前 i 個字符拷貝出來到 str
187                 strncpy(str + i, s, len);//將參數字符串s全部拷貝到 str + i 上去
188                 strncpy(str + i + len, m_str + i, m_length - i); // 將最后字符串拷貝出來
189                 str[m_length + len] = '\0';  // 最后添加結束符
190                 free(m_str);  // 釋放當前字符串的堆空間
191                 m_str = str;  // 使用申請出來的堆空間中的字符串;
192                 m_length = m_length + len;
193             }
194             else
195             {
196                 THROW_EXCEPTION(NoEnoughMemoryException, "No memory to insert String value ...");
197             }
198         }
199     }
200     else
201     {
202         THROW_EXCEPTION(IndexOutOfBoundsException, "Parameter i is invalid ...");
203    }
204 
205     return *this;
206 }
207 
208 String& String::insert(int i, const String& s)
209 {
210     return insert(i, s.m_str);
211 }
212 
213 String& String::trim()
214 {
215     int b = 0;
216    int e = m_length - 1;
217 
218     while( m_str[b] == ' ' ) b++;  // 確定中間字符串開始位置;
219    while( m_str[e] == ' ' ) e--;  // 確定中間字符竄結束位置;
220 
221     if( b == 0 )
222     {
223         m_str[e + 1] = '\0';  // 最開始沒有空格的時候;
224         m_length = e + 1;
225     }
226     else
227     {
228         for(int i=0, j=b; j<=e; i++, j++)
229         {
230             m_str[i] = m_str[j];  // 將當前的含有的非空字符挪到前面去;
231         }
232         m_str[e - b + 1] = '\0';  // 添加結束符
233         m_length = e - b + 1;  // 合法的字符個數;
234    }
235 
236     return *this;   // 實現鏈式調用
237 }
238 
239 int String::indexOf(const char* s) const // 子串查找,返回下標
240 {
241     return kmp(m_str, s ? s : "");
242 }
243 
244 int String::indexOf(const String &s) const
245 {
246     return kmp(m_str, s.m_str);
247 }
248 
249 /* 刪除下標 i 處長度為 len 的字符串 */
250 String& String::remove(int i, int len)   // 和 insert() 返回的是相同的函數,還可以以字符串類繼續訪問,如查看刪除后的字符串等
251 {
252     if( (0 <= i ) && (i < m_length) )
253     {
254         int n = i;
255         int m = i + len; // 在 (n, m) 范圍之內的字符都要刪除掉
256 
257         while( (n < m) && (m < m_length) ) // 刪除的字符串長度是不能大於當前的長度的,否則沒有意義
258         {
259             m_str[n++] = m_str[m++];   // 很經典
260         }
261 
262         m_str[n] = '\0';  //因為n是不斷增加的,直到 m 為等於 length
263         m_length = n; 
264    }
265 
266     return *this;
267 }
268 
269 String& String::remove(const char *s) // 刪除子串
270 {
271     return remove(indexOf(s), s ? strlen(s) : 0);  
272 }
273 
274 String& String::remove(const String &s) // 刪除子串
275 {
276     return remove(indexOf(s), s.length());
277 }
278 
279 /* 用 s 替換字符串中的 t */
280 String& String::replace(const char* t, const char* s)
281 {
282    int index = indexOf(t); // 查找 t 的位置
283 
284     if( index >= 0 ) // t 存在於當前的字符串中
285     {
286         remove(t);  // 不要復制粘貼代碼,要復用
287         insert(index, s);
288    }
289 
290     return *this;
291 }
292 
293 String& String::replace(const String& t, const char* s)
294 {
295     return replace(t.m_str, s);
296 }
297 
298 String& String::replace(const char* t, const String& s)
299 {
300     return replace(t, s.m_str);
301 }
302 
303 String& String::replace(const String& t, const String& s)
304 {
305     return replace(t.m_str, s.m_str);
306 }
307 
308 String String::sub(int i, int len) const  // 查找當前字符串中第 i 個位置長度為 len 的字符串
310 {
311    String ret;
312 
313     if( (0 <= i) && (i < m_length) )
314     {
315         if( len < 0 ) len = 0; // 當小於零時候,不可能,要歸一化到 0
316         if(len+i > m_length) len = m_length - i; // 只能夠提取這么長的長度
317         char* str = reinterpret_cast<char*>(malloc(len + 1));
318 
319         if( str != NULL )
320         {
321             strncpy(str, m_str + i, len); // 從 m_str + i 位置拷貝 len 長度的字符串,這里 m_str 是字符串起始位置
322         }
323 
324         str[len] = '\0';
325         ret = str;  // 返回子串
326 
327         free(str);
328     }
329     else
330     {
331         THROW_EXCEPTION(IndexOutOfBoundsException, "Parameter i is invaid ...");
332    }
333 
334     return ret;
335 }
336 
337 char& String::operator [] (int i)
338 {
339     if( (0 <= i) && (i < m_length) )
340     {
341         return m_str[i];   // 封裝;
342     }
343     else
344     {
345         THROW_EXCEPTION(IndexOutOfBoundsException, "Parameter i is invalid ...");
346     }
347 }
348 
349 char String::operator [] (int i) const
350 {
351    return (const_cast<String&>(*this))[i]; // 在當前版本當中對非 const 代碼復用上面
352 }
353 
354 /* 直接封裝 C 語言中的相關函數,直接封裝 C 語言庫中 strcmp() 函數即可 */
355 bool String::operator ==(const String& s) const
356 {
357     return (strcmp(m_str, s.m_str) == 0);  // strcmp 的函數原型為 int strcmp(const char* s1, const char* s2)
358 }
359 
360 bool String::operator ==(const char* s) const
361 {
362     return (strcmp(m_str, s ? s : "") == 0);  // 三目運算符是為了防止 s 為空指針。
363 }
364 
365 bool String::operator != (const String& s) const
366 {
367     return !(*this == s);
368 }
369 
370 bool String::operator != (const char* s) const
371 {
372     return !(*this == s);
373 }
374 
375 bool String::operator > (const String& s) const
376 {
377     return (strcmp(m_str, s.m_str) > 0);
378 }
379 
380 bool String::operator > (const char* s) const
381 {
382     return (strcmp(m_str, s ? s : "") > 0);
383 }
384 
385 bool String::operator < (const String& s) const
386 {
387     return (strcmp(m_str, s.m_str) < 0);
388 }
389 
390 bool String::operator < (const char* s) const
391 {
392     return (strcmp(m_str, s ? s : "") < 0);
393 }
394 
395 bool String::operator >= (const String& s) const
396 {
397     return (strcmp(m_str, s.m_str) >= 0);
398 }
399 
400 bool String::operator >= (const char* s) const
401 {
402     return (strcmp(m_str, s ? s : "") <= 0);
403 }
404 
405 bool String::operator <= (const String& s) const
406 {
407     return (strcmp(m_str, s.m_str) <= 0);
408 }
409 
410 bool String::operator <= (const char* s) const
411 {
412     return (strcmp(m_str, s ? s : "") <= 0);
413 }
414 
415 String String::operator + (const String& s) const
416 {
417     return (*this + s.m_str);   
418 }
419 
420 String String::operator + (const char* s) const
421 {
422     String ret;  // 定義一個兩個字符串拼接后結果的字符串對象;
423     int len = m_length + strlen(s ? s : "");  // 三目運算符是為了防止 s 為空指針;
424    char* str = reinterpret_cast<char*>(malloc(len + 1));  // 堆空間申請內存,並將 void* 重新解釋為 char*
425 
426     if( str )
427     {
428         strcpy(str, m_str);  // 當前對象的字符串拷貝到申請的堆空間中
429         strcat(str, s ? s : "");  // 將要添加的字符串拼接到對象字符串后面
430 
431         free(ret.m_str);  // 這里歸還之前指針所指的內存;
432 
433         ret.m_str = str;  // 這里又重新賦值了指針;
434         ret.m_length = len;
435     }
436     else
437     {
438         THROW_EXCEPTION(NoEnoughMemoryException, "No memory to add String values");
439    }
440 
441     return ret;
442 }
443 
444 String& String::operator += (const String& s)
445 {
446     return (*this = *this + s.m_str); //這里用到了賦值操作符,因此必須實現其重載;
447 }
448 
449 String& String::operator += (const char* s)
450 {
451     return (*this = *this + s);
452 }
453 
454 String String::operator - (const String& s) const  // 字符串自身會被改變
455 {
456     return String(*this).remove(s);  // 直接調用構造函數產生一個新的臨時字符串對象,值和當前字符串對象值相同,然后調用臨時對象的remove() 函數將子串刪除,最后將刪除結果返回,但是當前的字符串沒有被改變,因為是拷貝賦值
457 }
458 
459 String String::operator - (const char* s) const  // 字符串自身會被改變
460 {
461     return String(*this).remove(s);  // 同上
462 }
463 
464 String& String::operator -= (const String& s) // 字符串自生不會被改變
465 {
466     return remove(s);
467 }
468 
469 String& String::operator -= (const char* s)
470 {
471     return remove(s);
472 }
473 
474 String& String::operator = (const String& s)
475 {
476     return(*this = s.m_str);
477 }
478 
479 String& String::operator = (const char* s)
480 {
481     if( m_str != s )
482     {
483         char* str = strdup(s ? s : "");  // 復制一份字符串;
484 
485         if( str )  // 復制是否成功
486         {
487             free(m_str);
488             m_str = str;
489             m_length = strlen(m_str);
490         }
491         else
492         {
493             THROW_EXCEPTION(NoEnoughMemoryException, "No memory to assign new String value...");
494         }
495    }
496 
497     return *this;
498 }
499 
500 String& String::operator = (char c)
501 {
502     char s[] = {c, '\0'};
503     return (*this = s);
504 }
505 
506 String::~String()
507 {
508     free(m_str);
509 }
510 
511 }

 

10,本節課字符串類測試代碼:

 1 #include <iostream>
 2 #include "DTString.h"
 3 
 4 using namespace std;
 5 using namespace DTLib;
 6 
 7 void test_1()
 8 {
 9     cout << "test_1() begin ..." << endl;
10     String s;
11    s = 'D';
12 
13     cout << s.str() << endl;
14     cout << s.length() << endl;
15     cout << (s == "D") << endl;
16    cout << (s > "CCC") << endl;
17 
18    s += " Delphi Tang ";
19 
20     cout << s.str() << endl;
21     cout << s.length() << endl;
22     cout << (s == "D Delphi Tang ") << endl;
23     cout << "test_1() end ..." << endl;
24 }
25 
26 void test_2()
27 {
28    cout << "test_2() begin ..." << endl;
29 
30     String a[] = {"E", "D", "C", "B", "A"};  // 字符串的比較遵循字典當中的順序。
31    String min = a[0];
32 
33     for(int i=0; i<5; i++)
34     {
35         if( min > a[i] )
36         {
37             min = a[i];
38         }
39 }
40 
41    cout << "min = " << min.str() << endl;
42 
43     cout << "test_2() end ..." << endl;
44 }
45 
46 int main()
47 {
48     test_1();
49    test_2();
50 
51     return 0;
52 }

 

11,字符串類實現的實質是:

       1,使用面向對象的技術來對原來所使用過的 C 語言字符串相關函數進行合理封裝,方便開發;

       2,封裝達到的最終目的是代碼復用;

      

12,小結:

       1,C/C++ 語言本身不支持字符串類型;

       2,C 語言通過字符數組和一組函數支持字符串操作;

              1,不方便也不利於代碼復用

       3,C++ 通過自定義字符串類型支持字符串操作;

              1,通過面向對象的技術來封裝 C 語言中的函數,封裝后的結果是我們擁有一個完整的字符串類型,並且這個字符串類型在實際工程開發中非常方便;

       4,字符串類型通過 C 語言中的字符串函數實現;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM