1. 標(biāo)志庫類型string
1.1 string的初始化
string的初始化方式有5種,詳細(xì)見下面程序1。在c++標(biāo)準(zhǔn)規(guī)定了對象初始化分為直接初始化和拷貝初始化,在string對象中有體現(xiàn),詳見程序2。那問題來了什么時(shí)候使用直接初始化或者拷貝初始化?如果初始單個(gè)值,無論使用哪種初始化都行,如果初始是多個(gè)值,推薦使用直接初始化,這樣可以減少拷貝成本。
程序1
string s; //默認(rèn)初始化,初始的值為空串
string s2(s1); //s2是s1的副本
string s3 = s1; // 與s2(s1)等價(jià)
string s4 = "hello"; // s4是字面值“hello”的副本
string s5("hello"); //與s4="hello"等價(jià)
string s6(10,'h'); //把s6初始化為10個(gè)h字符
程序2
拷貝初始化:它是執(zhí)行拷貝完成初始化操作的,因此他最大的特點(diǎn)就是帶有=號(hào);
直接初始化:與拷貝初始化相反,沒有等號(hào);
string s7="hello2"; //拷貝初始化
string s8("hello2"); //直接初始化
string s9(s7); //直接初始化
string s10(10,'c'); //直接初始化
1.2 string的操作
1.2.1 string的讀寫
string的寫通常只有一種輸出方式,就是通過cout輸出,見程序3:
程序3:
string s = "hello";
cout << s << endl;
string的讀有兩種方式,一種是從輸入流讀取當(dāng)個(gè)字符串給string對象,另一種是從輸入流讀出一行給string對象。詳細(xì)區(qū)別見程序4:
程序4:
string s;
cin >> s; // 在讀取的過程中,string對象會(huì)自動(dòng)忽略開頭的空白(空格符、換行符、制表符等),并從第一個(gè)字符讀起,
//直到遇到下一個(gè)空白為止。讀取的字符對象不包含結(jié)尾空白,同時(shí)讀取流的緩沖區(qū)有不包含結(jié)尾的空白
//例如,如果程序輸入" hello ",輸入實(shí)際字符串值為:"hello"
getline(cin,s); //在讀取的過程中,它會(huì)最開始輸入字符(包含空白)開始,直到遇到換行符為止;讀取的
//字符對象不包含結(jié)尾的換行符,但是讀取流的緩沖區(qū)有包含結(jié)尾的換行符的,這有時(shí)會(huì)影響下一次字符串讀取
1.2.2 string::size_type類型
string::size_type是string的size函數(shù)的返回值,由于string的下標(biāo)是非負(fù)的,因此該類型為無符號(hào)類型的值。
1.2.3 string對象的比較
>、=、<、<=、>=是string對象比較大小的關(guān)系運(yùn)算符,都是按照字典順序比較大小的
1.2.4 string對象賦值和相加
string對象允許把一個(gè)對象的值賦值另外一個(gè)對象,并且允許兩個(gè)string對象相加,相加string就等價(jià)于兩個(gè)字符串的拼接,同時(shí)也支持字面值和string對象相加,但是不允許兩個(gè)字面值相加。見程序5:
程序5
string s = s1+","; //正確,字面值會(huì)自動(dòng)轉(zhuǎn)換成string對象,再執(zhí)行string加法運(yùn)算
string s2 = "hello"+"kuang"; //錯(cuò)誤,兩個(gè)都是字面值,字面值無法轉(zhuǎn)換,無法執(zhí)行string加法運(yùn)算,報(bào)錯(cuò)
1.3 string中字符處理
string對象對字符可以對整個(gè)string對象全部字符處理,通常采用for循環(huán)的方法;也可對單獨(dú)、特殊字符進(jìn)行處理,通常采用下標(biāo)或者迭代器,采用下標(biāo)運(yùn)算符訪問當(dāng)個(gè)元素,在string對象下標(biāo)運(yùn)算符[]返回值是字符的引用。注意:下標(biāo)可以訪問或修改string對象已有字符,但不能通過下標(biāo)添加字符到string對象,vector下標(biāo)符號(hào)也是一樣的。見程序6:
程序6
//訪問string對象內(nèi)字符
string str("hello kuang");
for(auto c:str)
{
cout << c << endl;
}
//修改string對象內(nèi)字符
string str("hello kuang");
for(auto &c:str) //auto &c = char,把變量c變成單個(gè)字符的引用
{
c = toupper(c); //小寫轉(zhuǎn)大寫
}
//采用下標(biāo)運(yùn)算符訪問當(dāng)個(gè)元素,在string對象下標(biāo)運(yùn)算符[]返回值是字符的引用
str[0]='y'; //str的值會(huì)變成"yello kuang"
2. vector
vector表示對象的集合,其中包含的對象類型相同。vector是對象的集合,而引用不是對象,在c++內(nèi)置對象中引用和指針區(qū)別中講過,所以不存在包含引用的vector。
2.1 vector的初始化
vector初始化的方法有7中,詳細(xì)見程序7。一般情況下對vector對象初始一般用括號(hào),如果要對vector對象包含值初始化,通常是使用花括號(hào),采用初始化列表的方式進(jìn)行初始化。有時(shí)這兩種情況會(huì)出現(xiàn)等價(jià)的情況,當(dāng)vector集合包含的對象不是int,vector<T> v1(10) 和 vector<T> v1={10}是等價(jià)的,造成這種情況是由c++首先判斷10能不能用來作為列表初始值
,顯然不能,c++編譯器就會(huì)把它作為初始化對象個(gè)數(shù)的參數(shù),如果還不滿足就報(bào)錯(cuò)。當(dāng)vector集合包含的對象是int,就必須嚴(yán)格按照規(guī)定來初始化,因?yàn)榇藭r(shí)編譯器無法判斷這個(gè)整形參數(shù)是列表初始值還是元素?cái)?shù)量。詳細(xì)見程序8:
程序7
vector<T> v1; //默認(rèn)初始化
vector<T> v2(v1); // v2包含v1的副本
vector<T> v2 = v1; //等價(jià)于 v2(v1)
vector<T> v3(n,val) //v3包含了n個(gè)重復(fù)元素,其中n為int,val為T類型
vector<T> v4(n); // v4包含了n個(gè)具有默認(rèn)值重復(fù)元素
vector<T> v5{a,b,c}; //a、b、c類型為T,v5包含了3個(gè)具有不同初值的元素
vector<T> v5={a,b,c}; //v5{a,b,c}等價(jià),其中a、b、c類型為T
程序8
//輸入元素的個(gè)數(shù)
vector<int> v2(99);
cout << v2.size() << endl; // 99
vector<int> v3(22,100);
cout << v3.size() << endl; // 22
vector<int> v4{33};
cout << v4.size() << endl; // 1
vector<int> v5{11,33};
cout << v5.size() << endl; // 2
vector<string> v6{1000};
cout << v6.size() << endl; // 1000
vector<string> v7{44,"he"};
cout << v7.size() << endl; // 44
2.2 vector操作
vector的操作與string操作大致相同,那咱們說vector獨(dú)有方法,v.push_back(t)方法是vector添加元素的方法,還有vector對象比較和string對象比較也類似,如果兩個(gè)vector對象容量不同,但在相同位置上元素都相等的話,則元素多的大,如果元素的值有區(qū)別,則vector對象大小有第一個(gè)開始不等元素大小決定。vector比string不同的是,只有vector包含元素是能夠比較的,才能夠進(jìn)行比較。最后一點(diǎn),vector不能用下標(biāo)形式添加元素,這與string對象一致。
3. 迭代器
在c++標(biāo)準(zhǔn)庫中容器對象和string對象都具有(包含)迭代器對象,所謂的迭代器對象類似于指針,它提供了對象元素的訪問,其對象元素比如說:容器中的元素或者string對象中的字符。那么迭代器對象該如何獲得呢?所有的容器對象和string對象可通過自己成員函數(shù)begin和end獲得,begin函數(shù)返回指向第一個(gè)元素的迭代器;end函數(shù)返回指向最后一個(gè)元素下一個(gè)位置的迭代器(尾迭代器),這是本身就容器或者string對象末尾的一個(gè)標(biāo)志,并不指向容器或者string內(nèi)任何一個(gè)元素。
3.1 標(biāo)準(zhǔn)迭代器運(yùn)算符號(hào)
程序 | 解釋 |
---|---|
*iter | 返回迭代器指向元素的引用 |
iter->mem | 解引用iter并獲取該元素對象的mem成員,等價(jià)于(*iter).mem |
++iter | 令iter指向容器或string對象的下一個(gè)元素 |
--iter | 令iter指向容器或string對象的上一個(gè)元素 |
iter1 == iter2 | 判斷兩個(gè)迭代器是否相等,如果兩個(gè)迭代器指向的是同一個(gè)元素或者他們同時(shí)指向尾迭代器,則相等,反之,不相等 |
注:從標(biāo)準(zhǔn)的迭代運(yùn)算符號(hào)表來看,迭代器跟指針太像了
3.2 迭代器類型(類似于指針類型)
迭代器為了更好控制程序訪問元素讀寫權(quán)限,迭代器引入了iterator和const_iterator兩種類型,其中iterator類型的迭代器可以對元素可讀可寫,而const_iterator只能讀取,不能寫。這兩種迭代器類型是由其對象(容器或string)決定的,如果它們是常量,它們的迭代器類型就是const_iterator;如果它們不是是常量,它們的迭代器類型可以是兩種類型的任何一種,但通常它們調(diào)用begin或者end函數(shù)返回的迭代器類型一定是iterator。
vector<int>::iterator it; //it指向元素可讀可寫
vector<int>::const_iterator ct;//it指向元素可讀不可寫
3.3 string或vector支持迭代器運(yùn)算
程序 | 解釋 |
---|---|
iter+n | 迭代器指示由當(dāng)前元素向后移動(dòng)n個(gè)元素 |
iter-n | 迭代器指示由當(dāng)前元素向前移動(dòng)n個(gè)元素 |
iter1-iter2 | 返回兩個(gè)迭代器指向元素之間相差的元素,這個(gè)是有正負(fù)的,正的表示iter1比iter2在容器中位置靠后,負(fù)的反過來 |
>、>=、<、<= | 比較迭代器指向的位置,位置越小代表的值越小 |
4. 數(shù)組
數(shù)據(jù)與vector類似,也是保存一系列對象,引用不是對象,因此也不存在引用數(shù)組,這一點(diǎn)與vector一樣的;它們之間的區(qū)別,數(shù)據(jù)大小在初始化時(shí)候固定不變的,而vector大小是隨時(shí)可變的,同時(shí)vector具有良好動(dòng)態(tài)性能,適合動(dòng)態(tài)添加元素。
4.1 數(shù)組初始化
通用數(shù)組初始化。
unsigned cnt = 42; //不是常量表達(dá)式
constexpr unsigned sz = 42; //常量表達(dá)式
int arr[sz]; //正確
int arr2[cnt]; //錯(cuò)誤,在定義數(shù)組形如a[d]時(shí)候,d只能是常量表達(dá)式、字面值、整形常量
const unsinged yz = 3;
int ial[yz] = {0,1,2}; //采用列表初始化數(shù)組
int a2[] = {0,2}; //等價(jià)于維度為2的數(shù)組
int a3[4] = {0,1}; //等價(jià)于a3={0,1,0,0}
int a4[2]={0,1,2}; //錯(cuò)誤:當(dāng)定義數(shù)組的大小要比列表初始化值個(gè)數(shù)要小時(shí)候,報(bào)錯(cuò)
字符數(shù)組初始化在使用列表初始化和使用字面值初始化是不同的,字符串?dāng)?shù)組使用字面值初始化會(huì)自動(dòng)在字面值字符串后面加上空字符('\0'),這是因?yàn)樽址置嬷翟赾++底層就是字符數(shù)組,并且在末尾帶有'\0',而使用列表初始化時(shí)候不會(huì)加。注意,使用字面值初始化字符數(shù)組要加上空字符,因此字符數(shù)據(jù)長度=可見字符數(shù)目+1,見程序
char a1[] = {'c','h','i','n','a'} ; //列表初始化字符數(shù)組,沒有空字符
char a2 = "china" ; //字面值初始化字符數(shù)組,其實(shí)相當(dāng)于拷貝初始化,帶有空字符'\0'
在標(biāo)準(zhǔn)c++當(dāng)中,數(shù)組之間是不允許拷貝和賦值的。注意,有些編譯器上可以行的,我們一般建議采用c++標(biāo)準(zhǔn),這樣的代碼具有更強(qiáng)的兼容性
int a[] = {1,2,3};
int b[] = a; //錯(cuò)誤,不能使用一個(gè)數(shù)組采用拷貝初始化方式初始化數(shù)據(jù)
b = a; //錯(cuò)誤,數(shù)組之間不能賦值
復(fù)雜數(shù)組聲明:對于復(fù)雜的數(shù)據(jù)聲明我們安裝向內(nèi)向外,再從右到左的順序閱讀,理解含義。
int (*Parray)[10] = &arr; // 首先看最里面括號(hào)部分,*Parray是一個(gè)指針,然后在看外層右邊,[10]可知Parray是個(gè)指向大小為10的數(shù)組指針,
//最后看到外層左邊int,最終可知:Parray是個(gè)指向大小為10的整形數(shù)組指針
int *(&array)[10] = ptrs; //array是個(gè)數(shù)組引用,這個(gè)數(shù)組包含10個(gè)int型的指針
4.2 數(shù)組元素的訪問
數(shù)組元素的訪問是采用下標(biāo)訪問的,同時(shí)也支持for循環(huán),大致與string對象元素訪問類似,不同一點(diǎn)是,string對象可以采用迭代器訪問元素。
4.3 指針和數(shù)組
c++編譯器會(huì)把數(shù)組名自動(dòng)替換成指向數(shù)組首元素的指針,數(shù)組操作等同于指針操作。
int ia[] = {0,1,2,3,4,5}
int *p = ia;
int *p1 = &ia[0]; //兩種寫法等價(jià)
指針也是迭代器,數(shù)組的指針也支持運(yùn)算,運(yùn)算方式也迭代器類似,同時(shí)為了數(shù)組指針能夠更好的獲得首指針和尾后指針(最后一個(gè)元素的下一位置),c++提供了begin(數(shù)組名)和end(數(shù)組名)兩個(gè)函數(shù),例如:
int a[] = {1,2,3,4};
int *beg = begin(a);
int *end = end(a);
for(int *b = beg;b!=end;++b)
{
cout << *b << endl;
}
數(shù)組的指針和string、vector都支持下標(biāo)運(yùn)算,不同的是指針的下標(biāo)運(yùn)算可以為負(fù)值下標(biāo)。
int a=[0,2,4,6,8}
int *p = &a[2];
int j = p[1]; // j = a[3]
int x = p[-2]; // x = a[0]
4.4 C風(fēng)格的字符串與標(biāo)準(zhǔn)c++的string的區(qū)別
c風(fēng)格字符串在c中是使用字符數(shù)組存儲(chǔ)的,c為了表達(dá)字符,通常在存放字符數(shù)組的最后加上'\0'字符,而標(biāo)準(zhǔn)string不用。例如:
char ca[] = {'a','b','c'};
cout << strlen(ca) << endl; //嚴(yán)重錯(cuò)誤,ca數(shù)組沒有空字符結(jié)束,strlen函數(shù)無法判斷c字符串的長度
字符串比較的差別,標(biāo)準(zhǔn)字符串string通過>、<、==號(hào)就比較,而c風(fēng)格的字符串只能通過strcmp函數(shù)來實(shí)現(xiàn)。標(biāo)準(zhǔn)字符串可通過加號(hào)實(shí)現(xiàn)string對象和string對象或者string對象和字符串字面值實(shí)現(xiàn)字符串的拼接,而c風(fēng)格的字符串只能通過strcat()和strcpy()實(shí)現(xiàn)字符串的拼接,而且目標(biāo)字符串的需要程序員自己調(diào)節(jié),一旦目標(biāo)字符串的元素個(gè)數(shù)設(shè)置不合理,就會(huì)導(dǎo)致程序出錯(cuò)。
4.5 標(biāo)準(zhǔn)庫提供給與舊代碼的接口
string的c_str()函數(shù),能夠把標(biāo)準(zhǔn)庫的string對象轉(zhuǎn)換為字符數(shù)組;c++標(biāo)準(zhǔn)不允許用vector初始化數(shù)組,但數(shù)組可以初始化vector對象:
int a = {0,2,3,4};
vector<int> ivec = (begin(a),end(a)); //通過首數(shù)組指針和尾后指針來初始化vector對象
5 多維數(shù)組
二維數(shù)組的第一個(gè)維度稱為行,第二個(gè)維度稱為列。
5.1 多維數(shù)組的初始化
int a[3][4] = {
{0,1,2,3},
{4,5,6,7},
{8,9,10,11}
}; //初始化 3行*4列數(shù)組
int a2[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11}; //與上一句等價(jià)
int a3[3][4] = { {0},{4},{8}}; //顯示初始化每行的首元素