標籤:調用 npos 應用 ascii 方式 comm dev term not
好久沒有更新部落格了,這段時間一直在忙影像處理的項目,最近空了下來,也是時候整合C++的相關內容,靜心感受下程式設計語言的魅力,和大家共同探討學習。我將以標頭檔的形式展開學習,且只講述相關介面的應用,至於內部具體的實現,鑒於本人水平有限,不敢獻醜。經過考慮,決定先從和資料結構相關的標頭檔開始,因為這些標頭檔裡的內容在OJ裡經常要用到。今天要學習的是string標頭檔,這裡的string是C++裡的string,而C裡的string在C++裡仍然保留,標頭檔名為cstring,這裡不做展開。在此說明:以下的代碼都是在VS 2015編譯環境下運行。
Some Detailsbasic_string
實際上,string標頭檔裡並不只有string這個類型,這個標頭檔裡最主要的是一個叫basic_string的類模板,這個類模板的聲明如下:
template < class charT, class traits = char_traits<charT>, class Alloc = allocator<charT> > class basic_string;
上面的類模板構造有點複雜,我們沒有必要深究,只需要知道我們可以在basic_string這個類中自訂三種類型作為member type就行了。其中的charT (character type)是最重要的模板參數,它直接說明了字串中的元素類型,另外兩個類型均採用預設參數(以charT為參數構建的類)即可。
為什麼要提basic_string這玩意兒呢,因為string類型正是basic_string的special type,在實際操作中,最常使用的是char類型(字元型),故只需將上面的charT轉換為char便是string類型了。string類型的構造如下:
typedef basic_string<char> string; //元素為8bits字元類型
類似的,在c++內建類型中,還有wstring、u16string和u32string(後兩個是在c++11標準下),我們可以根據實際需要選取字串類型,它們的構造如下:
typedef basic_string<wchar_t> wstring; //16位或32位typedef basic_string<char16_t> u16string; //16位typedef basic_string<char32_t> u32string; //32位
String versus Character Array
之前看到過一篇文章,講到了string和char*,我在此做個總結。
- string記憶體由系統進行管理,自動實現記憶體的申請和釋放;而char* 則需要自己管理。如果你要使用的記憶體較大,則string系統自動申請的記憶體可能不夠使用。因此,在使用記憶體大小知道的情況下,建議使用char* ,而當使用記憶體具體大小未知,但記憶體不大時可以使用string。
-
string相比char* 有一個優點,c++標準庫對string類進行了封裝,裡面的各種成員函數處理字串相當方便,這也是我常用string的一個原因。
至於string和char類型的vector有什麼區別,我倒還沒有深究過,大概是成員函數不同吧,知道的小夥伴可以在下面留言下。
ElaborationMember Functionsstring::string
string(); //(1)預設建構函式,構造一個Null 字元串,字串長度為0string(const string& str); //(2)拷貝建構函式string(const string& str, size_t pos, size_t len = npos);//(3)複製str一部分,pos是起始位置,len是複製的字串長度(預設值是到字串底部)這裡要注意str的第一個字元pos=0string(const char* s); //(4)複製C模式下s指標指向的字元數組(字串)string(const char* s, size_t n); //(5)複製前n個字元數組元素string(size_t n,char c); //(6)複製n個連續的字元ctemplate <class InputIterator> string(InputIterator first, InputIterator last);//(7)利用iterator複製字元序列,範圍為[first,last),注意最後一個不包括string(initializer_list<char> il); //(8)將初始化列表il轉換為stringstring(string&& str) noexcept; //(9)右值引用str,並且不拋出異常資訊。
關於右值引用和move語義這塊內容,是C++11的新特性,我當時琢磨了很久也還是雲裡霧裡,估計是自己悟性不夠高,我覺得有兩篇文章寫得還不錯,貼在這裡。另外,不知右值和臨時變數有何不同,知道的小夥伴也可以在下面留言。
https://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/
https://my.oschina.net/letiantian/blog/470921
Demonstration: //為簡略,今後代碼將省略cout,自己操作時應加上#include<iostream>#include<string> //調用string標頭檔#include<initializer_list> //(8)中要用到using namespace std;string test(const string& x) //(9)的測試函數,產生右值{ return x;}int main(){ string s4("string now"); //(4) string s1; //(1) string s2(s4); //(2) string s3a(s4, 7, 3); //(3)設定長度 string s3b(s4, 7); //(3)預設長度,也就是到底部 string s5("string now", 6); //(5) string s6(3, ‘6‘); //(6) string s7(s4.begin(), s4.begin() + 6); //(7)begin函數後面會提到,可以產生iterator initializer_list<char> s0 = {‘s‘, ‘t‘, ‘r‘, ‘i‘, ‘n‘, ‘g‘}; //產生initializer_list string s8(s0); //(8) string s9(test(s4)); //(9)}//自行將注釋中的序號與上面的序號對照進行學習
結果如下,看看是不是和你們想象中的一樣呢。
string::operator=
string& operator= (const string& str); //(1)賦值string類型變數string& operator= (const char* s); //(2)賦值c風格字串string& operator= (char c); //(3)賦值一個字元,字串長度為1string& operator= (initializer_list<char> il); //(4)賦值初始化列表ilstring& operator= (string&& str) noexcept; //(5)利用右值引用賦值
Demonstration:#include<iostream>#include<string> //調用string標頭檔#include<initializer_list> //(4)中要用到using namespace std;string test(const string& x) //(5)的測試函數,產生右值{ return x;}int main(){ string s1, s2, s3, s4, s5; //為把operator=和建構函式分開,使用預設建構函式 s2 = "string now"; //(2) s1 = s2; //(1) s3 = ‘6‘; //(3) initializer_list<char> s0 = {‘s‘, ‘t‘, ‘r‘, ‘i‘, ‘n‘, ‘g‘}; s4 = s0; //(4) s5 = test(s2); //(5)}
Some More Detailsstring類型對象是以’\0’結尾的嘛?
估計很多人跟我一樣都想過這個問題,因為在C語言中,系統預設字串都是以’\0’截尾的。我們不妨今天來編個小程式驗證一下。
#include<iostream>#include<string>using namespace std;int main(){ string s0 = "string now"; //初始化s0 if (s0[10] == ‘\0‘) cout << "Yes." << endl; //如果末尾是‘\0‘,則輸出Yes. else cout << "No." << endl; //如果末尾不是‘\0‘,則輸出No. return 0;}
在這裡,我們得到了一個令人滿意的答案,說明C++風格的string仍然是以’\0’結尾的,通常情況下,末尾的’\0’我們可以忽略不計。
編碼類別型對string的影響
提到這個問題,是因為我注意到了這麼一句話,我在這裡引用一下。
Note that this class handles bytes independently of the encoding used: If used to handle sequences of multi-byte or variable-length characters (such as UTF-8), all members of this class (such as length or size), as well as its iterators, will still operate in terms of bytes (not actual encoded characters).
這句話的意思概括起來就是說,不管你使用哪種類型的編碼,string類型的操作都將按一個位元組的方式來處理。我們不禁想到了中文,因為中文是在GB2312中編碼的,而且一個中文字元佔用兩個位元組,這樣會引起什麼問題呢?我們也來測試一下。
#include<iostream>#include<string>using namespace std;int main(){ string s0 = "測試"; //用中文初始化s0 cout << s0 << endl << endl; //查看能否輸出中文 for (int i = 0; i < 2; ++i) //輸出前2個位元組,看看是否會輸出測試兩個字 cout << s0[i] << endl; return 0;}
剛開始,我們將整個字串輸出,看看是否會顯示“測試”這兩個字,結果是這兩個字顯示了,說明c++是可以識別GB2312編碼的。而當我們嘗試將字串按單個位元組輸出的時候,就發現了問題,因為輸出台輸出的是空白,這就說明string類型並不能直接辨別這個是什麼編碼,它只能按單個位元組進行讀取,這就會導致該位元組沒有預設的ASCII編碼可以對應,因此也就不會有內容輸出了。
string的內容實在有點多,為了保持頁面的精簡,方便瀏覽,我將把下半部分內容放到下篇博文中。如有錯誤,歡迎指正!
凡人視角C++之string(上)