1. 程式人生 > >2. 字串、向量和陣列

2. 字串、向量和陣列

第三章 字串、向量和陣列

標頭檔案中不應該使用using的宣告,因為標頭檔案的內容會拷貝到所有引用它的檔案中去,若標頭檔案中使用了using,則每個引用該標頭檔案的檔案都會有這個宣告。

std::string

std::string的初始化方式:

#include <string>
using std::string;
string s1;
string s2(s1);      // s2是s1的副本
string s2=s2;       // s2(s1)等價
string s3="value";  // s3是字面值value的副本,等價s3("value")
string
s4(5,'c'); // 等價 s4="ccccc";

使用等號的是拷貝初始化,不用等號的是直接初始化。

使用std::cinstd::coutstring進行讀寫操作。

讀取未知量的string物件,遇見檔案結束標誌後停止:

int main(){
    string word;
    while(cin>>word){
        cout<<word<<endl;
    }
    return 0;
}

使用getline讀取整行,但是不包括結尾的換行符!

int main(){
    string line;
    while(getline(cin,.line)
)
{ cout<<line<<endl; } }

string::size_type是表示string大小的資料型別,表示式中出現了string::size()時,不要使用int,而是使用auto自動推導。

string s("12345");
auto len=s.length();  // len是string::size_type型別

字串運算:

  • ==:長度和對應字元完全相同
  • >=<=><比較第一個相異字元的字典序
  • +,兩個字串拼接,也可以和字面值進行拼接,s+"value";但是字面值不能直接相加

遍歷字串使用for

string s("1234567");
for(auto c:s){  // 在這裡的c是拷貝的,不是引用!
    cout<<c;
}
for(auto &c:s){ // 這裡是引用!
    cout<<toupper(c);  // 原來的也變成了大寫
}

也可以按照下表訪問:

string s("1234567890");
// 注意使用型別推導
for(decltype(s.size()) index=0;index!=s.size();++index){
    s[index]=toupper(s[index]);  // 字母改成大寫
}

std::vector

vector對於型別一般是引用的方式,而非拷貝賦值,因此不存在對vector的引用!

初始化的方法:

vector<T> v1;           // 空的vector
vector<T> v2(v1);       // v2包含v1的所有副本
vector<T> v3=v1;        // 等價於v3(v1)
vector<T> v4(n,val);    // v4包含n個val
vector<T> v5{a,b,c};    // v5包含a b c
vector<T> v6={a,b,c};   // v6和v5等價
vector<T> v7(10);       // v7包含10個元素,初始值由元素的型別確定
vector<string> v8{10,"hi"};  // 包含10個hi

使用vector::push_back向末尾新增元素,這種做法是最高效的。

其他的一些比較操作類比於string。使用size時,要注意指定型別:

vecotr<int>::size_type; // 這是正確的
vector::size_type;      // 這是錯誤的!!!!!!!

下標規則參照string

迭代器

所有的標準庫成員都有迭代器,begin()是第一個元素,end()是最後一個元素的後一位,實際意義僅僅是結束的標記。空的容器的迭代器的begin()==end()

vector<int> v;
auto b=v.begin(), e=v.end();    // 使用自動推導
auto cb=v.cbegin(),ce=v.cend(); // 區別在於不能通過迭代器修改元素

一般規則:

  • *iter 返回迭代器指向元素的引用
  • iter->mem(*item).mem等價,是訪問某元素的mem成員
  • ++iter指向下一個元素
  • --iter指向上一個元素
  • iter1==iter2或者iter1!=iter2判斷是否指向同一個元素。
  • 不能對end進行遞增或者解引用操作
  • 使用了迭代器的迴圈體後,不能在迴圈體中向容器新增元素!!!

vectorstring支援的運算規則:

  • iter+n向後指向的第n個,或者指向end
  • ietr-n向前指向的第n個,或者是begin
  • iter-=niter+=n是對本身的操作
  • >= > <= <比較的是相對位置

迭代器位置做差,返回的是difference_type的型別,一般用auto自動推導:

// 二分查詢
auto b = text.begin(), e = text.end();
auto mid = text.begin() + (e - b) / 2;

while(mid != e && *mid != goal) {  // goal是目標值
    if(goal < *mid) {
        e = mid;
    } else {
        b = mid + 1;
    }
    mid = b + (e - b) / 2;
}

陣列

初始化的方式:

const unsigned sz=3;
int a1[sz]={0,1,2};     // 0 1 2 三個元素
int a2[]={1,2,3};       // 0 1 2 三個元素
int a3[5]={0,1,2,};     // 0 1 2 0 0 
string a4[3]={"hello","world"}; // "hello" "world" ""
int a5[2]={0,1,2}; // 錯誤,初始值過多

預設情況下,陣列型別從右向左依次繫結。陣列名是地址,指向第一個元素。陣列的下表是size_t型別。陣列的指標也是迭代器,滿足迭代器的一般運算規則,陣列迭代器同時滿足vector的標準。

現代的C++程式應該儘量使用vector而非陣列,使用string而非C風格的字串。

兩種不同的for索引多維陣列的方式:

constexpr size_t rowCnt = 3, colCnt = 4;

int ia[rowCnt][colCnt];

for(size_t i = 0; i != rowCnt; ++i) {
    for(size_t j = 0; j < colCnt; ++j) {
        ia[i][j] = i * colCnt + j;
    }
}

size_t cnt = 0;
for(auto &row : ia) {
    for(auto &col : row) {
        col = cnt;
        cnt++;
    }
}

若使用上述第二種方式遍歷,除了最內層的迴圈外,其他所有迴圈的控制變數都必須是引用型別!!!!如果不想更改資料,可以新增const限定!

多維陣列的陣列名是指標的指標:

int a[3][4];    // 大小為3的陣列,每個元素都是含有4個整數的陣列
int (*p)[4] = a;  // 指向含有4個整數的陣列
int *ip[4];     // 整形指標的陣列

int arr[10];
int (*p1)[10] = &arr;   // 注意這裡的取地址符號

對於高維陣列 ,類似的方式:

int a[3][4][5];
int (*p)[4][5] = a;
int (*p1)[3][4][5] = &a;   // 注意p1的格式

如果對陣列名使用了&符號,那麼需要指標的定義維數與陣列的維數一致,否則去掉第一維。