C++ Primer學習筆記之第三章--字符串,向量和數組

3.1 命名空間的using聲明

1、格式

using namespace::name;

(1)例:

using namespace::std;

3.2 標準庫類型string

1、標準庫類型string的定義包含在#include<string>頭文件中。

2、初始化string的方式
(1)直接初始化(不用等號的初始化)

(2)拷貝初始化

(3)例:

string s1;
string s2(s1);      //直接初始化,上下等價。
string s2=s1;       //拷貝初始化,上下等價。
string s3("value");
string s3="value";
string (n,'c');     //由n個字符c組成的字符串


3.2.2 string的操作
1、string的操作:

1、os<<s;        //輸出
2is>>s;        //輸入
3、getline(is,s);    //從is中讀取一行賦值給s,返回is
4、s.empty();        //s爲返回true,非空返回false
5、s.size();         //返回s中字符的個數
6、s[n]              //返回第n個字符引用,n從0計起
7、s1=s2             //用s2的副本替s1中原來的字符
8、s1==s2            
//s1和s2所含字符完全一樣,則爲true,否則爲false,區分大小寫。
9、s1!=s2            //與s1==s2相反
10、>,<=,>=,<        //利用字符在字典中的排序,區分大小寫

2、string對象會自動忽略開頭的空白(即空格符,換行符,製表符等)

3、讀取未知數量的string對象
(1)例:

string word;
while(cin>>word)        //反覆讀取,直至到達文件末尾
    cout<<word<<endl;

(2)while語句條件負責在讀取時,檢測輸入流的情況:
如果流有效,即沒有遇到文件結束標記或非法輸入,就執行while內部的操作。

4、使用getline讀取一整行
(1)getline函數的參數是一個輸入流和string對象,函數從給定的輸入流讀入內容,直到遇到換行符

(2)換行符被讀入,但不存入string對象

5、word.size()返回的是無符號值,類型爲 string::size_type

6、比較string對象
(1)比較符號:> ; < ; >= ; <=

(2)比較規範:
<1>如果兩個string對象的長度不同,而且較短的string對象的每個字符都與string對象位置上的字符相同,則說較短的string對象小於較長的string對象。
<2>如果兩個string對象在某些位置上不一致,則string對象比較的結果其實是string對象中第一對相異字符比較的結果

7、字符串字面值並不是標準庫類型string的對象,字符串字面值與string是不同的類型。


3.2.3 處理string對象中的字符
1、使用cctype頭文件中的函數(#include<cctype>

1、  isalnum(c)      //當c是字母或數字時爲真
2、  isalpha(c)      //當c是字母時爲真
3、  iscntrl(c)      //當c是控制符時爲真
4、  isdigit(c)      //當c是數字時爲真
5、  isgraph(c)      //當c不是空格但可打印時爲真
6、  islower(c)      //當c是小寫字母時爲真
7、  isupper(c)      //當c是大寫字母時爲真
8、  isprint(c)      //當c是可打印字符時爲真
9、  ispunct(c)      //當c是標點符號時爲真
10、 isspace(c)      //當c是空白(空格,橫向製表符,縱向製表符,回車符,換行符,進紙符)時爲真
11、 isxdigit(c)     //當c是十六進制數字時爲真
12、tolower(c)       //如果c是大寫字母,輸出對應的小寫字母,否則原樣輸出小寫字母。
13、toupper(c)       //如果c是小寫字母,輸出對應的大寫字母,否則原樣輸出大寫字母。

2、使用基於範圍的for語句處理string對象的每個字符
(1)C++11標準爲我們提供了範圍for(range for)語句
(2)語法形式:

for(declaration:expression)
    statement;

//①expression:是一個對象,用於表示一個序列。
//②declaration:負責定義一個變量,該變量將用於訪問序列中的基礎元素。

(3)例:

string str("some string");
for(auto c:str)
    cout<<c<<endl;

(4)使用範圍for語句改變字符串中的字符
①把循環變量定義成引用類型即可
②range for循環的是declaration中的變量,而不是expression的變量。

(5)只處理一部分字符的方法
<1>訪問string對象中單個字符的方式
①使用下標運算符[]
②使用迭代器

<2>下標運算符([])接受的輸入參數是string::size_type類型的值,返回值是該位置上字符的引用。

<3>例:

string s("some string");
for(decltype(s.size) index=0;
    index!=s.size()&&!isspace(s[index]);
    ++index)
    s[index]=toupper(s[index]);

3.2.3 練習

練習3.6:編寫一段程序,使用範圍for語句將字符串內的所有字符用x代替

string str="abc123";
for(auto &c:str)
    c='x';
cout<<str<<endl;

練習3.10:編寫一段程序,讀入一個包含標點符號的字符串,將標點符號去除後輸出字符串剩餘的部分

string str;
getline(cin,str);
for (auto c : str)
    if (!ispunct(c))
        cout << c;
cout << endl;

3.3 標準庫類型vector

1、標準庫vector表示對象的集合,也稱對象的容器(container),其中具有的對象類型都相同。

2、使用vector需要頭文件#include

3、vector是一個類模版

4、實例化(instantiation):指編譯器根據模版創建類或函數的過程。

5、vector是模版而非類型,由vector生成的類型必須包含vector中元素的類型。

6、引用不是對象,所以不存在包含引用的vector。

7、vector的使用例子,需提供額外的信息是vector內所存放對象的類型:

vector<int> ivec    //ivec保存int類型對象
vector<Sales_item> Sales_vec;   //保存Sales_item類型對象
vector<vector<string>> file;    //該vector的元素是vector對象

3.3.1 定義和初始化vector對象
1、初始化vector對象的方法

1vector<T> v1;     //v1是個空vector,潛在元素爲T類型,執行默認初始化。
2vector<T> v2(v1);     //v2包含v1所有元素的副本
3vector<T> v2=v1;      //等價於v2(v1)
4vector<T>  v3(n,val);     //v3包含了n個值爲val的重複元素。
5vector<T> v5{a,b,c...};   //v5包含廚師個數的元素,每個元素賦以相應的初始值。
6vector<T> v5={a,b,c...}   //等價於v5{a,b,c...}

2、如果vector對象中的元素類型不支持默認初始化,我們必須提供初始化的元素值。

3、用值初始化和列表初始化vector對象的區別
(1)用()是值初始化,用()提供的值是用來構造(ocnstruct)vector對象的。

(2)用{}提供的值是vector指定類型對象的初值,但類型需一致。


3.3.3 其他vector操作
1、vector支持的操作

1、v.empty();        //如果v不含有任何元素,返回真,否則返回假
2、v.size();         //返回v中元素的個數
3、v.push_back(t);   //向v的尾端添加一個值爲t的元素
4、v[n];             //返回v中第n個位置上元素的引用
5、v1=v2;            //用列表中元素的拷貝替換v1中的元素
6、v1={a,b,c...};    //用列表中元素的拷貝替換v1中的元素
7、v1==v2;           //v1和v2相等,元素數量相同且對應位置的元素值都相同爲真,否則爲假
8、v1!=v2;           //與v1==v2相反
9、>;<;>=;<=         //以字典順序進行比較     

2、vector.size()返回值類型爲vector定義的size_type類型。要使用size_type,需指定它是由那種類型定義的:

vector<int>::size_type;     //正確
vector::size_type;          //錯誤

3、vecotr對象(以及string對象)的下標運算符可用與訪問已存在的元素,而不能用於添加元素。

4、vector只能對取值已存在的元素執行下標操作

vector<int> ivecl
    cout<<ivec[0]<<endl;
    //錯誤,ivec不包含任何元素!!!

3.3.2 節練習

練習3.14:編寫一段程序,用cin讀入一組整數並將它們存入vector對象:

int a;
vector<int> v1;
while(cin>>a)
    v1.push_back(a);
for(auto i:v1)
    cout<<i<<endl;

練習3.15:改寫3.14,不過這次讀入字符串

string str;
    vector<string> st;

    while (getline(cin, str) && str != "quit")
        st.push_back(str);

3.3.3 節練習
練習3.17:編寫一段程序,從cin讀入一組詞並把它們存入一個vector對象,然後設法把所有詞都改爲大寫形式。輸出改變後的結果,每個詞佔一行:

string str;
    vector<string> st;

    while (getline(cin, str) && str != "quit")
        st.push_back(str);
for (decltype(st.size()) i = 0;i < st.size();i++)
{
    for (auto c : st[i])
        if (islower(c))
            cout <<static<char>(toupper(c));
    cout << endl;
}

練習3.20:讀入一組整數並把它們存入一個vector對象,將每對相鄰整數的和輸出出來。改寫你的程序,這次要求線輸出第一個和最後一個元素的和,接着輸出第二個和倒數第二個元素的和,以此類推:

1int i;
    vector<int> i1;
    while (cin >> i)
        i1.push_back(i);
    for (decltype(i1.size()) j = 0;j < i1.size();j++)
    {
        cout<<i1[i]+i1[i+1]<<endl;
    }

(2int i;
    vector<int> i1;
    while (cin >> i)
        i1.push_back(i);
    for (decltype(i1.size()) j = 0;j < i1.size();j++)
    {
        if (j < i1.size() - 1)
            cout << "Neighbor number sum :" << i1[j] + i1[j + 1] << endl;
        else
            cout << "Neighbor number sum :" << i1[j - 1] + i1[j] << endl;
        if (j < i1.size()/ 2)
            cout << "Head and End number sum£º" << i1[j] + i1[i1.size() - j - 1] << endl;
    }

3.4 迭代器介紹

1、string對象不屬於容器類型。
2、迭代器(iterator)也是提供對對象的間接訪問。

3.4.1 使用迭代器
1、例:

auto b=v.begin(),e=v.end(); //b和e的類型相同

(1)begin成員函數負責返回指向第一個元素的迭代器

(2)end成員函數負責返回指向容器尾元素的下一個位置的迭代器

(3)該迭代器只是的是容器一個本不存在的“尾後(off the end)”

(4)end成員函數返回的迭代器也被稱作尾後迭代器(off the end iterator)

2、如果容器爲空,begin成員函數和end成員函數返回的是同一個迭代器,都是尾後迭代器

3、迭代器運算符
(1)==和!=比較兩個合法迭代器是否相等

(2)如果兩個迭代器指向的元素相同或都是同一個容器的尾後迭代器,則它們相等,否則就說兩個迭代器不相等。

(3)標準容器迭代器的運算符

1*iter       //返回迭代器所指元素的引用
2、  iter->mem   //解引用iter並該元素名爲mem的成員,等價於(*iter).mem
3++iter        //令iter只是容器中的下一個元素
4--iter        //令iter只是容器中的上一個元素
5、iter1==iter2  //迭代器相等
6、iter1!=iter2  //迭代器不相等

4、因爲end返回的迭代器並不實際指示某個元素,所以不能對其進行遞增或解引用操作

5、迭代器和迭代器類型
(1)迭代器三種不同的含義
<1>迭代器概念本身
<2>指容器定義的迭代器類型
<3>某個迭代器對象

(2)每個容器類定義一個名爲iterator的類型,該類型支持迭代器概念所規定的一套操作。

6、begin和end運算符
(1)begin和end返回的具體類型由對象是否常量決定的
<1>如果是常量,begin和end返回類型爲const_interator
<2>不是常量,則返回類型爲iterator

(2)便於專門得到const_interator類型返回值,C++11引入兩個新函數
<1>cbegin
<2>cend


3.4.2 迭代器運算
1、vector和string迭代器支持的運算

1、iter+n        //前移n個元素(前移方向爲begin到end的方向)
2、iter-n        //後移n個元素
3、iter+=n       
4、iter-=n
5、iter1-iter2       //兩迭代器的距離(指向同一容器或尾後迭代器),類型爲difference_type()

2、迭代器中關係運算符的使用(>、>=、<、<=)
(1)要指向同一個容器或尾後迭代器

(2)關係運算符可以比較迭代器所指位置的前後
例:

it=iv.begin()+1;
mid=iv.begin()+iv.size()/2;
if(it<mid)
    ....

3.5 數組

1、數組是複合類型

2、數組的聲明範例:

int *ptrs[10];      //ptrs是含有10個整型指針數組

int (*ptarray)[10]; //ptarray是一個指向含有10個整型數組的指針

int (& arrRef)[10]=arr; //arrRef引用一個含有10個整型數的數組

3.5.2 訪問數組元素
1、數組使用下標[]時,下標類型爲size_t(在頭文件cstddef定義)

3.5.3 指針和數組
1、在大多數表達式中,使用數組類型的對象其實是使用一個指向該數組首元素的指針。

2、兩個指針相減的結果是一種名爲ptrdiff_t的標準庫類型(帶符號類型)

3、下標和指針
例:

int ia[]={0,2,4,6,8}'
int i=ia[2];        
int *p=ia;
i=*(p+2);       //等價於i=ia[2]

int *q=&ia[2];  
int j=q[1];     //q[1]等價於*(q+1),即ia[3]
int k=q[-2];    //q[-2]等價於*(p-2),即ia[0]

4、內置類型的下標運算符所用的索引值是有符號類型,該點與string和vector不同。

3.5.4 C風格字符串
1、C風格字符串不是一種類型,而是爲了表達和使用字符串而形成的一種俗成的寫法
(1)字符串存放在字符數組中以空字符結束(’\0’)。

2、C標準庫String函數(C對應string.h頭文件,C++對應cstring頭文件)

1、strlen(p);        //返回p的長度,不計'\0'。
2、strcmp(p1,p2);    //比較p1和p2相等性。p1==p2,返回0;p1>p2,返回正值;p2>p1;返回負值
3、strcat(p1.p2);    //將p2附加到p1之後返回p1
4、strcpy(p1,p2);    //將p2拷貝給p1,返回p1


3.5.4 節練習
練習3.39:編寫一段程序,比較兩個string對象。再編寫一段程序,比較兩個C風格字符串的內容:

string a="abced",b="lkkjilk";
if(a.size()>b.size())
    cout<<"string a>b"<<endl;
else 
    cout<<"string a<b"<<endl;

//C風格字符串
char a[]="abced",b[]="lkkjilk";
if(strlen(a)>stlen(b))
    cout<<"string a>b"<<endl;
else 
    cout<<"string a<b"<<endl;

練習3.40:編寫一段程序,定義兩個字符數組並用字符串字面值初始化它們,接着再定義一個字符數組存放前兩個數組連接的後果。使用strcpy和strcat把前兩個數組的內容拷貝到第三個數組中:

char a[] = "kingoliver";
char a2[] = "Archlinux";
char *a1 = strcat(a, a2);
cout <<"strcat(a,a2)="<< a1 << endl;
a1 = strcpy(a, a2);
cout << "strcpy(a,a2)=" << a1 << endl;

3.5.5 與舊代碼的接口
1、混用string對象和C風格字符串
(1)允許使用空字符結束的字符數組初始化string對象或爲string對象賦值,但反過來不行。

2、string提供了名爲c_str的成員函數,可以將string對象轉換爲C風格字符串
(1)例:

string s=("Hello World");
char *str=s;        //錯誤,不能用string對象初始化字符數組char *
const char *str=s.c_str();  //正確

(2)c_str函數返回值爲C風格的字符串,即返回一個指向常量字符數組的指針。

3、使用數組初始化vector對象
(1)不允許使用vector對象初始化數組
(2)可用數組初始化vector對象:只需指定拷貝區域的首元素地址和尾後地址。

(3)例:

int int_arr[]={0,1,2,3,4,5};
vector<int> ivec(begin(int_arr),end(int_arr));

//用於初始化vector對象的值也可能僅是數組的一部分
vector<int> subVec(int_arr+1,int_arr+4);


3.5.5 節練習
練習3.41:編寫一段程序,用整型數組初始化一個vector對象:

int a[5] = { 1,2,3,4,5 };
vector<int> arrvec(std::begin(a), std::end(a));
cout << "Array to vector£º";
for (auto i = arrvec.cbegin();i != arrvec.cend();++i)
    cout << *i;
cout << endl;

練習3.42:編寫一段程序,將含有整數元素的vector對象拷貝給一個整型的數組:

int a;
vector<int> arrv;
while (cin >> a)
    arrv.push_back(a);
int arr[5];
cout << "Array number: ";
auto j = arrv.begin();
for (auto i = begin(arr);
i != end(arr) && j != arrv.end();++i, ++j)
{
        *i = *j;
        cout << *i << " ";
}
cout << endl;

3.6 多維數組

1、多維數組的初始化
(1)理解方法:例

int ia=[3][4];      //大小爲3的數組,每個元素爲是含有四個整型的數組,3爲行,4爲列
int ia[10][20][30]; //大小爲10的數組,它的每個元素均是大小爲0的數組,這些數組的元素是含有30個整型的數組。
自右向左分別爲列,行。高。

(2)初始化多維數組的每一行分別用花括號括起來
<1>例:

int ia[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};

2、多位數組的下標引用
<1>例:

//ia是大小爲3的數組,每個元素都半酣4個整型的數組
//用arr的首元素爲ia最後一行的最後一個元素賦值:
int ia[3][4]=a[0][0][0];
int (&row) [4]=ia[1];
//把row綁定到第二個4元素數組上。

3、使用範圍for語句處理多維數組

int ia[3][4];
size_t cnt=0;
for(auto &row:ia)      //對於外層數組的每一個元素
    for(auto &col:row)      //對於內層數組的每一個元素
    {
        col=cnt;
        ++cnt;
    }

4、要使用範圍for語句處理多維數組,除了最內層的循環之外,其他所有循環控制變量都應該是引用類型。

5、指針和多維數組
(1)多維數組實際爲數組的數組,多維數組的組名爲指向第一個內層數組的指針。
<1>例:

int ia[3][4];
int (*p) [4]=ia;        //指向含有4個整數的數組,ia爲&ia[0]
p=&ia[2];   //p指向ia的尾元素

(2)下屬聲明,括號不可少

int *ip[4];         //整指針數組,元素爲4個
int (*ip)[4];       //指向含有4個整型的數組的指針。

6、類型別名簡化多維數組指針
(1)例:

int ia[3][4];
using int_array=int[4];
//等價於上面的聲明: typedef int int_array[4];
for(int_array *p=ia;p!=ia+3;++p)
    {
    for(int *q=*p;q!=*q+4;++q)
        cout<<*q<<' ';
    cout<<endl;
    }


3.6 節練習
練習3.43:編寫3個不同版本的程序,令其均能輸出ia的元素。版本1使用範圍for語句管理迭代過程;版本2和版本3都是用普通的for語句,其中版本2要求使用下標運算符,版本3要求用指針。此外,在所有3個版本的程序中都要直接寫出數據類型,而不能使用類型別名、auto關鍵字或decltype關鍵字:

int ia[3][4];
size_t cnt = 0;
int j = 0;
for (auto & row : ia)
{
    int i = 0;
    for (auto &col : row)
    {

        col = cnt;
        ++cnt;
        cout<<"ia["<<j<<"]"<<"["<<i<<"]="
            <<col<<" ";
        ++i;
    }
    ++j;
    cout << endl;
}

//初始化二維數組

版本1for (int(&p)[4] : ia)
{
    for (const int(&q) : p)
        cout << q << ' ';
cout << endl;
}

版本2for (size_t i = 0;i < 3;++i)
{
    for (size_t j = 0;j < 4;++j)
        cout << ia[i][j] << ' ';
    cout << endl;
}

版本3for (int(*p)[4] = ia;p != end(ia);++p)
{
    for (int *q = *p;q != end(*p);++q)
        cout << *q << ' ';
    cout << endl;
}

術語表總結

1、緩衝區溢出(buffer over flow):一種嚴重的程序故障,主要原因是視圖通過一個越界的索引訪問容器內容。

2、容器(container)是一種類型,其對象容納了一組給定類型的對象。vector是一種容器類型。

3、difference_type:由string和vector定義的一種帶符號整數類型,表示迭代器之間的距離。

4、實例化(instantiation):編譯器生成一個指定的模版類或函數的過程。

5、ptr_diff_t:是cstddef頭文件中定義的一種與機器實現有關的帶符號整數類型,它的空間足夠大,能夠表示數組中任意兩個指針之間的距離。

6、size_t:是cstddef頭文件中定義的一種與極其實現有關的無符號整數類型,它的空間足夠大,能夠表示任意數組的大小

7、size_type:是string和vector定義的類型的名字,能存放任意string對象或vector對象的大小。在標準庫中,size_type被定義爲無符號類型

發佈了42 篇原創文章 · 獲贊 55 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章