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::cin
和std::cout
對string
進行讀寫操作。
讀取未知量的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
進行遞增或者解引用操作 - 使用了迭代器的迴圈體後,不能在迴圈體中向容器新增元素!!!
vector
和string
支援的運算規則:
iter+n
向後指向的第n
個,或者指向end
ietr-n
向前指向的第n
個,或者是begin
iter-=n
和iter+=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的格式
如果對陣列名使用了&
符號,那麼需要指標的定義維數與陣列的維數一致,否則去掉第一維。