C++語言程式設計筆記 - 第6章 - 陣列、指標與字串

oddmarmot發表於2020-12-05

第6章 陣列、指標與字串

C++從C繼承來的一個重要特性就是可以直接使用地址來訪問記憶體,指標變數時實現這一特徵的重要資料型別。應用指標,可以方便地處理連續存放的大量資料,以較低的代價實現函式間的大量資料共享,靈活地實現動態記憶體分配。

字元資料可以用來表示字串,這是從C語言繼承的有效方法,但是從物件導向的觀點和安全性的角度來看,用字元資料表示的字串有不足之處,因此標準C++類庫中提供了string類,這是通過類庫來擴充套件資料型別的一個很好的範例。

6.1 陣列

陣列是具有一定順序關係的若干物件的集合體,組成陣列的物件稱為該陣列的元素。

6.1.1 陣列的宣告與使用

陣列型別宣告的一般形式:

資料型別 識別符號[常量表示式1][常量表示式2]...;	//陣列型別宣告的一般形式

陣列中同型別資料的型別由“資料型別”給出,這個“資料型別”可以是整形、浮點型等基本型別,也可以是結構體、類等使用者自定義型別

"識別符號"指定陣列名稱。

[常量表示式1][常量表示式2]...”稱為陣列的界,必須是在編譯時就可求出的常量表示式,其值必須為正整數。有n個下標的陣列稱為n維陣列。陣列的下標的個數稱為陣列的維數。二維陣列被當作一維陣列的陣列,更高維陣列也如此。

如果發生了陣列越界(使用陣列時下標值超過了n),執行時有時會得到提示,有時會沒有任何提示!


6.1.2 陣列的儲存與初始化

陣列元素在記憶體中是順序、連續儲存的。

陣列元素的初始化(宣告陣列時對部分或全部元素賦初值):

int a[3]={1, 1, 1};
int a[]={1, 1, 1};	//與上一行等價
float b[5]={1.0, 2.0, 3.0};	//部分初始化,不能間隔賦初值,元素個數必須給出
int c[][3]={1, 2, 3, 4, 5, 6};	//多維陣列若給出全部初值,第一維可省略
int c[2][3]={{1, 2, 3}, {4, 5, 6}};	//與上一行等價
const float d[5]={1.0, 2.0 3.0};	//宣告為常量的陣列必須給定初值

6.1.3 陣列作為函式引數

使用陣列名傳遞引數時,傳遞的是地址。

實引數組的個數不應少於形引數組的個數。

如果在被調函式中對形引數組元素值進行改變,主調函式中實引數組的相應元素值也會改變。


6.1.4 物件陣列

物件陣列的元素是類的物件,具有資料成員和函式成員。

宣告一個一維物件陣列:

類名 陣列名[常量表示式];

陣列物件的初始化過程,實際上就是呼叫建構函式來對每一個元素物件進行初始化。如果在宣告陣列時給每一個陣列元素指定初始值,在陣列初始化過程中就會呼叫與形參型別相匹配的建構函式。例如Location a[2]={Location(1,2), Location(3,4)};在執行時會呼叫與形參型別相匹配的建構函式分別初始化a[0]a[1]。如果沒有指定陣列元素的初始值,就會呼叫預設建構函式,例如Location a[2]={Location(1,2)};在執行時首先呼叫帶形參的建構函式初始化a[0],然後呼叫預設建構函式初始化a[1]

如果需要建立某個類的物件陣列,在設計類的建構函式時就要充分考慮到陣列元素初始化時的需要:當各元素物件的初值要求為相同的值時,應該在類中定義預設建構函式;當各元素物件的初值要求為不同的值時,需要定義帶形參(無預設值)的建構函式。

當一個陣列中的元素物件被刪除時,系統會呼叫解構函式來完成掃尾工作。

通過物件陣列中元素物件訪問公有成員:

陣列名[下標表示式].成員名

6.2 指標

指標是C++從C中繼承過來的重要資料型別,提供了一種較為直接的地址操作手段。動態記憶體分配和管理離不開指標。

6.2.1 記憶體空間的訪問方式

具有靜態生存期的變數在程式開始執行之前就被分配了記憶體空間。

具有動態生存期的變數是在程式執行時,遇到變數宣告語句時被分配記憶體空間的。

有時使用變數名訪問記憶體空間不夠方便,或者根本沒有變數名可用,這時就需要直接用地址來訪問記憶體單元。


6.2.2 指標變數的宣告

指標是一種資料型別。

指標變數用於存放記憶體單元地址。

宣告指標的語法形式:

資料型別 *識別符號		//宣告指標

比如:

int *ptr;	//定義一個指向int型別資料的指標變數

指標可以指向各種型別,包括基本型別、陣列(資料元素)、函式、物件、指標。


6.2.3 與地址相關的運算“*”和“&

*”稱為指標運算子,也稱解析,表示獲取指標所指的變數的值,是一元操作符。

&”稱為取地址運算子,用於得到一個物件的(儲存單元)地址,是一元操作符。

*”和“&”出現在宣告語句中和執行語句中,其含義是不同的。作為一元運算子和二元運算子時含義也不同。

(1)在宣告語句中的一元運算子“*”和“&

一元運算子“*”出現在宣告語句中,在被宣告的變數之前時,表示宣告的是指標

一元運算子“&”出現在宣告語句中,在被宣告的變數之前時,表示宣告的是引用

(2)在非宣告語句中的一元運算子“*”和“&

一元運算子“*”出現在執行語句中,或在宣告語句的初始化表示式中時,表示訪問指標所指物件的內容

一元運算子“&”出現在執行語句中,或在給變數賦值時出現在等號右邊時,表示取物件的地址


6.2.4 指標的賦值

定義指標後必須先賦值,然後才能引用。

兩種賦值方法:

(1)定義指標的同時進行初始化賦值:

儲存型別 資料型別 *指標名=初始地址;	//定義指標的同時進行初始化賦值

(2)定義指標後,單獨使用賦值語句:

指標名=地址;		//定義指標後,單獨使用的賦值語句

如果使用物件的地址作為指標的初值,或在賦值語句中將物件地址賦給指標變數,該物件必須在賦值之前就宣告過,而且這個物件的型別應該和指標型別一致

多個指標可指向同一個變數。

一個陣列,可以用它的名稱來直接表示它的起始地址。**陣列名實際上就是一個不能被賦值的指標,即指標常量。**如:

int a[10];		//定義int型陣列
int *ptr=a;		//定義並初始化int指標

關於指標的型別,還應注意:

(1)可以宣告指向常量的指標,此時所指物件的值不變指標本身可變(可指向另外的物件):

int a;
const int *p1=&a;	//p1是指向常量的指標

(2)可以宣告指標型別的常量(指標常量),這時指標本身(的值,為地址)不能被改變

int *const p2=&a;	//p2是指標常量

(3)一般情況下,指標的值只能賦給相同型別的指標。但void型別的指標,可以儲存任何型別的物件地址,任何型別的指標都可以賦值給void型別的指標變數。經過使用型別顯示轉換,通過void型別的指標便可以訪問任何型別的資料。

void指標一般只在指標所指向的資料型別不確定時使用。

#include<iosteam>
using namespace std;

int main(){
    //void voidObject;		錯誤,不能宣告void型別的變數
    void* pv;				//對,可以宣告void型別的指標
    int i=5;
    pv=&i;					//void型別指標指向整型變數
    int* pint=static_cast<int*>(pv);  //void型別指標賦值給int型別指標
    cout<<"*pint"<<*pint<<endl;		//執行結果為*pint=5
    return 0;
}

6.2.5 指標運算

指標與整數加減、指標自增自減都表示移動指標所指位置。

*(p1+n1)可寫作p1[n1],都表示p1當前所指位置後方第n1個數的內容。類似也有*(p1-n1),可寫作p1[-n1]

慎用指標的算術運算:對指標進行算術運算時,一定要確保運算結果所指向的地址是程式中分配使用的地址。

兩個相同型別的指標相等,表示指向的是同一地址。

不同型別的指標之間或指標與非0整數之間的關係運算是無意義的。

但指標變數可以和整數0作比較,0專用於表示空指標,即一個不指向任何有效地址的指標

給一個指標變數賦值為0,表示該指標是一個空指標,不指向任何地址。

int *p;		//宣告一個int型別的指標p
p=0;		//將p設定為空指標,不指向任何地址

除0以外,賦給指標變數的值不能是其他整數,必須是地址常量(如陣列名)或地址變數。

空指標也可以用NULL來表示。

int *p=NULL;	//將int型的指標初始化為空指標

NULL是一個在很多標頭檔案中都有定義的巨集,被定義為0。

如果不便於用一個有效地址給一個指標變數賦初值,那麼應當用0作為它的初值,從而避免指向不確定地址的指標出現。


6.2.6 用指標處理陣列元素

比如對於:

int array[5];	//宣告一個存放了5個int型別數的一維陣列

陣列名array就是陣列的首地址(第一個元素的地址),即array&array[0]相同。陣列中下表為i的元素就是*(陣列名+i),例如*array就是陣列元素array[0]*(array+3)就是陣列元素array[3]

把陣列作為函式的形參,等價於把指向陣列元素型別的指標作為形參。例如,下面三種寫法出現在形參列表中是等價的:

void f(int p[]);
void f(int p[3]);
void f(int *p);

6.2.7 指標陣列

指標陣列:如果一個陣列的每個元素都是指標變數,這個陣列就是指標陣列。指標陣列的每一個元素都必須是同一型別的指標。

宣告一維指標陣列:

資料型別 *陣列名[下標表示式];	//宣告一維指標陣列,指標陣列的每個元素都是指標變數

其中,陣列名是指標陣列的名稱,同時也是這個陣列的首地址。

由於指標陣列的每個元素都是一個指標,必須先賦值後引用,因此宣告指標陣列必須賦初值

#include<iostream>
using namespace std;

int main(){
    int line1[]={1, 0, 0};
    int line2[]={0, 1, 0};
    int line3[]={0, 0, 1};
    
    //定義整型指標陣列
    int *pLine[3]={line1, line2, line3};
    
    for(int i=0; i!=3; ++i){
        for(int j=0; j!=3; ++j){
            cout<<pLine[i][j]<<" ";
            //上行中等價於*(pLine[i]+j)
        }
        cout<<endl;
    }
    return 0;
}

上例輸出結果為矩陣:

1 0 0 
0 1 0
0 0 1

6.2.8 用陣列作為函式引數

如果以指標作為函式的形參,在呼叫時實參將值傳遞給形參,也就是使實參和形參指標變數指向同一個記憶體地址。這樣在子函式執行過程中,通過形參指標對資料值的改變會影響實參指標所指向的資料值

在C語言中,用指標作為函式的形參有三個作用:

(1)使形參和實參指向相同的記憶體空間,以達到引數雙向傳遞的目的,即通過在被調函式中直接處理主調函式中的資料,來將函式的處理結果返回其呼叫者。C++中用引用實現這一功能(見第3章)。

(2)減少函式呼叫時資料傳遞的開銷C++中有時可用引用實現這一功能,有時還是需要用指標。

(3)通過指向函式的指標傳遞函式程式碼的首地址

注意如果函式體中不需要通過指標改變指標所指物件的內容,應在參數列中將其宣告為指向常量的指標,這樣使得常物件被取地址後也可作為該函式的引數。

在設計程式時,當某個函式中以指標或引用作為形參都可以達到同樣的目的時,使用引用可以使程式的可讀性更好些。


6.2.9 指標型函式

指標可以作為函式的返回值。

指標型函式:返回值為指標型別的函式。

使用指標型函式,最主要的目的是在函式結束時把大量的資料從被調函式返回到主調函式中。而通常非指標型函式呼叫結束後,只能返回一個變數或物件。

指標型函式的定義:

資料型別 *函式名(參數列){		//定義指標型函式
    函式體		//返回值為指標型別
}

6.2.10 指向函式的指標(函式指標)

函式指標:專門用來存放函式程式碼首地址的變數。

呼叫函式的通常形式“函式名(參數列)”的實質就是“函式程式碼首地址(參數列)”。一旦函式指標指向了某個函式,它與函式名便具有同樣的作用。可以像使用函式名一樣使用指向函式的指標來呼叫函式

宣告函式指標:

資料型別 (*函式指標名)(形參表)	//宣告函式指標

函式指標在使用前要先賦值,使指標指向一個已經存在的函式程式碼的起始地址:

函式指標名=函式名;

6.2.11 物件指標

6.2.11.1 物件指標的一般概念

物件指標:用於存放物件地址的變數。

物件所佔據的記憶體空間只是用於存放資料成員的,函式成員不在每一個物件中儲存副本。

宣告物件指標:

類名 *物件指標名;

例如:

Point *pointPtr;	//宣告Point類的物件指標變數pointPtr
Point p1;	//宣告Point類的物件p1
pointPtr=&p1;	//將物件p1的地址賦給物件指標pointPtr

可以通過物件指標訪問物件的成員,語法形式為:

物件指標名->成員名  //通過物件指標訪問物件的成員
(*物件指標名).成員名	//上一行的等價形式

6.2.11.2 this指標

this指標是一個隱含於每一個**類的非靜態成員函式(包括建構函式和解構函式)**中的特殊指標。

類的靜態成員函式中沒有this指標。

this指標用於指向正在被成員函式操作的物件。

this指標實際上是類成員函式的一個隱含引數。在呼叫類的成員函式時,目的物件的地址會自動作為this指標的值,傳遞給被呼叫的成員函式,這樣被調函式就能夠通過this指標來訪問目的物件的資料成員

this是一個指標常量,對於常成員函式,this同時又是一個指向常量的指標

在成員函式中,可以使用*this來標識正在呼叫該函式的物件。

當區域性作用域中宣告瞭與類成員同名的識別符號時,對該識別符號的直接引用代表的是區域性作用域中所宣告的識別符號,這時,為了訪問該類的成員,可以通過this指標(比如this->x)。


6.2.11.3 指向類的非靜態成員的指標

可將類的成員(變數、函式、物件等)的地址存放到一個指標變數中,這樣,可以通過這些指標直接訪問物件的成員。

宣告指向類的成員的指標:

型別說明符 類名::*指標名;	//宣告指向資料成員的指標
型別說明符 (類名::*指標名)(參數列);	//宣告指向函式成員的指標

對指向類的成員的指標賦值:

指標名=&類名::資料成員名;		//對資料成員指標賦值
指標名=&類名::函式成員名;		//對函式成員指標賦值

通過指向類的成員的指標(以及物件指標)訪問成員:

物件名.*類資料成員指標名		//訪問類的資料成員
物件指標名->*類資料成員指標名		//訪問類的資料成員
(物件名.*類成員函式指標名)(參數列)		//訪問類的函式成員
(物件指標名->*類成員函式指標名)(參數列)		//訪問類的函式成員

例子:

int main(){
    Point a(4,5);
    Point *p1=&a;	//定義物件指標並初始化
    //定義成員函式指標並初始化
    int (Point::*funcPtr)()const=&Point::getX;
    //使用成員函式指標和物件名訪問成員函式
    cout<<(a.*funcPtr)()<<endl;
    //使用成員函式指標和物件指標訪問成員函式
    cout<<(p1->*funcPtr)()<<endl;
    //使用物件名訪問成員函式
    cout<<a.getX<<endl;
    //使用物件指標訪問成員函式
    cout<<p1->getX()<<endl;
    return 0;
}

6.2.11.4 指向類的靜態成員的指標

對類的靜態成員的訪問是不依賴於物件的。因此,可以用普通的指標來指向和訪問靜態成員


6.3 動態記憶體分配

動態記憶體分配可以使程式在執行過程中按照實際需要申請適量的記憶體,使用結束後還可以釋放。申請和釋放過程一般稱為建立刪除

堆物件:在程式執行過程申請和釋放的儲存單元。

建立堆物件:運算子new,其功能是動態記憶體分配,或者稱為動態建立堆物件。

new 資料型別(初始化引數列表);	//用new動態建立堆物件

上述語句在程式執行過程中申請分配用於存放指定型別資料的記憶體空間,並根據初始化引數列表中給出的值進行初始化。

如果記憶體申請成功,new運算便返回一個指向新分配記憶體首地址的型別的指標,可以通過這個指標訪問堆物件;如果申請失敗,會丟擲異常(見第12章)。

用new建立物件或物件陣列時要呼叫相應的建構函式。

刪除堆物件:運算子delete,其功能是刪除由new建立的物件,釋放指標所指向的記憶體空間。

delete 指標名;	//用delete刪除由new建立的物件

用delete刪除動態物件時,系統首先為該動態物件呼叫解構函式,再釋放其記憶體。

注意用new分配的記憶體,必須用delete加以釋放,否則會導致動態分配的記憶體無法回收,使得程式佔據的記憶體越來越大,這叫做“記憶體洩露”。

new運算子動態建立一維陣列:

new 型別名[陣列長度];	//用new動態建立一維陣列

如果是用new建立的陣列,用delete刪除時在指標名前面要加“[]”:

delete[] 指標名;	//用delete加“[]”刪除由new建立的一維陣列

6.4 用vector建立陣列物件

無論是靜態陣列,還是用new建立的動態陣列,都難以檢測下標越界的額錯誤。

C++標準庫提供了被封裝的動態陣列——vector,這種被封裝的陣列可以具有各種型別

vector是一個類别範本,不是類。

用vector定義動態陣列的形式:

vector<元素型別>陣列物件名(陣列長度);	//用vector定義動態陣列

尖括號中的型別名錶示陣列元素的型別。陣列長度是一個表示式,表示式中可以包含變數。比如:

int x=10;
vector<int>arr(x);	//定義一個大小為10的int型動態陣列物件arr

用vector定義的陣列物件的所有元素都會被初始化。另外,初值也可以自行指定,但只能為所有元素指定相同的初值

vector<元素型別>陣列物件名(陣列長度,元素初值);

訪問vector陣列物件的元素:

陣列物件名[下標表示式]	//訪問vector陣列物件的元素

vector陣列物件的名字表示的就是一個陣列物件,而非陣列的首地址,因為陣列物件不是陣列,而是封裝了陣列的物件。

vector的詳細特性將在第10章介紹。


6.5 深複製與淺複製

隱含的複製建構函式完成的只是淺複製

預設的複製建構函式將兩個物件的對應資料簡單複製後,pointsArray1的成員points和pointsArray2的成員points具有相同的值,也就是說兩個指標指向的是同一記憶體地址,表面上好像完成了複製,但是並沒有形成真正的副本。因此,當程式中移動pointsArray1中的點時,也影響到了pointsArray2。這種效果就是“淺複製”。

淺複製還有更大的弊病,在程式結束之前pointsArray1和pointsArray2的解構函式會自動被呼叫,動態分配的記憶體空間會被釋放。由於兩個物件共用了同一塊記憶體空間,因此兩次呼叫解構函式,將該記憶體空間釋放了兩次,於是導致執行錯誤

解決“淺複製”問題的方法是編寫複製建構函式,實現“深複製”。


6.6 字串

C語言中用字元型陣列來存放字串,C++中依然可以沿用這一方法。不僅如此,C++標準庫還預定義了string類

6.6.1 用字元陣列儲存和處理字串

字串常量是用一對雙引號括起來的字元序列。比如“abcd”,“This is a string.”。在記憶體中按串中字元的排列次序順序存放,每個字元佔一個位元組,並在末尾新增空字元(null character)’\0’作為結尾標記。這實際上是一個隱含建立的型別為char的陣列,一個字串常量就表示這樣一個陣列的首地址。因此,可以把字串常量賦給字串指標,由於常量值是不能修改的,所以應將字串常量賦給指向常量的指標。比如:

const char* STRING1="This is a string.";

這時,可以直接對STRING1進行輸出:

cout<<STRING1;

如果建立一個char陣列,每個元素存放字串的一個字元,在末尾放置一個’\0’,便構成了C++字串。它的儲存方式與字串常量無異,但由於是程式設計師建立的陣列,因此可以改變其內容,因而這就是字串變數了。

對字元陣列進行初始化賦值時,初值的形式可以是以逗號分隔的ASCII碼或字元常量,也可以是整體的字串常量(這時末尾的’\0’是隱含的)。下面三種建立字串變數的寫法等價:

char str[8]={'p', 'r', 'o', 'g', 'r', 'a', 'm', '\0'};	//C風格字串
char str[8]="program";	//字串字面值,結尾不顯示地加'\0'
char str[]="program";	//讓編譯器自動計數

**字元常量用單引號括起;字串常量用雙引號括起,用雙引號括起的字串隱式地包含結尾的空字元。**字元常量(如’S’)是字串編碼的簡寫表示,而”S”不是字元常量,它表示字元’S’和’\0’組成的字串。

然而,用字元陣列表示字串後,執行很多字串操作比較麻煩,需要藉助cstring標頭檔案中的字串處理函式,比如strcpy函式(將一個字串的內容複製到另一個字串)、strcat函式(將兩個字串連線起來)、strcmp函式(按辭典順序比較兩個字串的大小)。另外,當字串長度很不確定時,需要用new來動態建立字元陣列,最後還要用delete釋放,這些都相當繁瑣。

C++對這些繁瑣的操作進行了封裝,形成了string類,可以更加方便地操作字串。


6.6.2 string類

使用string類必須包含標頭檔案string(#include<string>)。string定義在名稱空間std中。

嚴格地說,string並非一個獨立的類,而是類别範本basic_string的一個特定化例項。不過對使用者來說,其特點與一個類無異。

(注:以下內容大多參考《C++ Primer(第5版)》及《物件導向程式設計——C++語言描述(原書第2版)》)

6.6.2.1 初始化string物件

string s1;
string s2(s1);	//等價於string s2=s1;
string s3("value");	//s3是字面值"value"的副本,不用加'\0'
string s4(n, 'c');	//用連續n個字元c組成的字串初始化s4

6.6.2.2 讀取和輸出string物件

用操作符>>來輸入string型別字串會自動忽略開頭的空格,並從第一個真正的字元開始讀起,直到遇見下一處空格為止(不儲存任何空格)。

操作符<<可用來輸出字串。

比如:

string s;
cin>>s;	//若輸入是“   Hello  World!”
cout<<s<<endl;	//則輸出是“Hello”

讀取未知數量的string物件:

string word;
while(cin>>word)	//反覆讀取,沒遇到檔案結束標記或非法輸入
    cout<<word<<endl;	//逐個輸出單詞,每個單詞後面緊跟一個換行

6.6.2.3 getline讀取一整行

如果希望從鍵盤讀入字串,直到行末為止,不以中間的空格作為輸入的分隔符,可以使用標頭檔案string中定義的getline:

getline(cin, s2);	//從鍵盤讀入字串並賦給s2,不以空格作為輸入的分隔符

getline函式從輸入流中讀入字元,然後將它們儲存到string變數中,直到出現以下情況為止:

(1)讀入了檔案結束標誌;

(2)讀到一個新行,該新行將從流中移除,但沒有儲存到變數中;

(3)到達字串的最大允許長度(一般為4294967295)。

如果getline沒有讀入字元,它將返回false,該條件可用於判斷檔案是否結束,以終止應用程式。

getline還允許在輸入字串時增加其他分隔符,方法是把作為分隔符的字元作為第3個引數傳遞給getline:

getline(cin, s2, ',');

6.6.2.4 範圍for語句

範圍for語句遍歷給定序列中的每個元素並對序列中每個值執行操作。

for(declaration:expression)
	statement

比如:

string str("some string");
for(auto c:str)
	cout<< c <<endl;	//對於str中每個字元,依次輸出並緊跟一個換行符

將範圍for語句中的迴圈變數定義為引用型別來改變string物件中字元的值:

string str("some string");
for(auto &c:str)
    c=toupper(c);	//若是小寫,變為大寫
cout<< c <<endl;	//輸出"SOME STRING"

6.6.2.5 empty函式判斷string物件是否為空

根據string物件是否為空返回一個對應的布林值。

比如,只輸出非空的行:

while(getline(cin,line))
    if(!line.empty())	//不為空,則執行
        cout<<line<<endl;

6.6.2.6 size/length函式返回string物件的長度

size函式(或length函式)返回string物件的長度(字元的個數),返回的是一個無符號整型數。

比如,只輸出超過80個字元的行:

while(getline(cin, line))
    if(line.size>80)
        cout<<line<<endl;

6.6.2.7 erase函式刪除子串

第一個引數是需要刪除的子串開始的位置,第二個引數是子串的長度。字串第一個字元的索引為0。

如果不指明第二個引數,erase函式將刪除從索引的指定開始位置到字串末尾的所有字元。

如果兩個引數都不指定,erase函式將刪除所有的字元,最終得到一個空串。

string s="Ray Dennis Steckler";
s.erase(4,7);
cout<<s<<endl;	//輸出為"Ray Steckler"

6.6.2.8 substr函式提取子串

substr函式返回一個子串,第一個引數是子串首位字元的索引,第二個引數是子串的長度。

string s1="Ray Dannis Steckler";
string s2;
s2=s1.substr(4,6);
cout<<s1<<endl;	//輸出為"Ray Dannis Steckler"
cout<<s2<<endl;	//輸出為"Dannis"

6.6.2.9 find和rfind函式查詢子串

find函式:

s1.find(s2, ind)

如果s2在s1中的位置大於或等於ind,則find返回大於或等於ind的索引值。如果沒有找到s2,則find將返回無窮大(通常為429496795)。如果第二個引數沒有給出,則其預設值為0。引數s2可以是一個string字串、一個C風格字串或一個char字元。

rfind函式:

rfind函式類似於find函式,只不過是返回小於或等於ind的索引值或為無窮大。若引數s2未給出,則預設為無窮大。


6.6.2.10 insert函式插入字串

insert函式在某個string字串的指定位置插入另一個字串,第一個引數給出插入位置,第二個引數給出要插入的字串。


6.6.2.11 replace函式代替子串

replace函式用一個指定的字串來代替一個字串,第一個引數給出要代替的子串的首位字元的位置,第二個引數說明子串有多少個字元,第三個引數是要代替子串的字串,第三個引數可以是string字串或C風格的字串。

如果第一個引數在範圍內,用來替換的字串長度是下述兩個值中的最小值:第二個實參的值,字串長度減去第一個引數的值。


6.6.2.12 swap函式調換兩個字串

s1.swap(s2); //實現字串s1和s2互換

6.6.2.13 c_str函式轉換成C風格字串

C風格字串:以NULL結束的char型別陣列。

c_str()函式返回一個const的NULL結尾的char型別陣列的指標。

string filename="infile.text";
ifstream infile;
infile.open(filename.ctr());

6.6.2.14 string類的操作符

注意:string類不支援“-”操作符。

操作符示例註釋
+s+t連線成新串
=s=t用t更新s
+=s+=t等價於s=s+t
==s==t判斷s與t是否相等
!=s!=t判斷s與t是否不相等
<s<t判斷s是否小於t
<=s<=t判斷s是否小於或等於t
>s>t判斷s是否大於t
>=s>=t判斷s是否大於或等於t
[]s[i]訪問串中下標為i的字串

注意:這裡所說的對兩個字串的大小的比較,是根據字典順序的比較(大小寫敏感)。假設有兩個字串s1與s2,二者大小的比較規則如下:

(1)若兩個string長度不同,但較短的和較長的對應位上字元相同,則短<長;

(2)若兩個string某位置上字元開始相異,則實際比較第一個相異的字元。

之所以能通過上面的操作符來操作string物件,是因為string類對這些操作符進行了過載(第8章)。

相關文章