C++學習2 字符串、向量和數組
string表示可變長的字符串序列,vector存放的是某種給定類型對象的可變長序列。
命名空間的using聲明
域作用符(::)用來將命令空間中的函數或對象進行調用。
可以使用using聲明來使得可以直接訪問命名空間或者命名空間中的某個函數。
using std:cin; //這樣就可以直接調用cin函數
可變長字符序列之string庫
使用string庫之前必須先包括其頭文件和作用域。
#include<string>
using std::string;
定義和初始化string對象
分為直接初始化和拷貝初始化.
string s1; //默認為空字符串
string s2(s1); //s2為s1的副本
string s3="liangzi"; //拷貝初始化
string s4(s3); //s4時s3的副本
string s4=s3;//與上一條指令等價
string s5("liangzi");//直接初始化
string s6(100,'cc'); //將cc重復100遍得到的字符串
string對象上的操作
os<<s //將s中的字符串寫到輸出流os之中,返回os
is>>s //與上一條指令相似,將輸入的字符串存儲到s之中
is=getline(is, s) //該函數將從is中讀取一行賦值給s,之后返回is
s.empty() // 判斷字符串是否為空
s.size() //返回字符串中字符的個數
s[n] //返回字符串中的第n個字符,從0開始
s1+s2 //兩個字符串進行連接
s1==s2 //二者完全一致
<, <=, >, >= //利用字符在字典中的順序進行比較
下面給出一個讀寫string對象的簡單例子。
int main() {
string s;
cout << "please input a string" << endl;
cin >> s;
cout << s << endl;
return 0;
}
使用getline讀取一整行
與cin不同,如果一開始的輸入是一個換行符,getline函數讀取之后會得到一個空字符。
使用示例
int main() {
//使用C++讀取文本文件的示例
string file_path, line;
fstream read_file; //文件流類型,以后會詳細介紹
cout << "please input the need read path" << endl;
cin >> file_path;
read_file.open(file_path);
while (!read_file.eof())
{
getline(read_file,line);
cout << line << endl;
}
read_file.close();
system("pause");
return 0;
}
字符串常量和string對象相加
必須注意C++中“+”左右至少有一個string對象,即兩個字符串常量不能進行相加操作。
string s1="liangzi", s2="zuishuai";
string ss=s1+s2; //正確
string sss=s1+"---------->>"+s2; //正確
string ssss="liangzi"+"zuishuai"; //錯誤
對string對象中的字符進行處理
首先介紹一下c語言庫中的一些常用函數
cctype(或ctype.h)
#incldue<cctype>
using std::cctype;
isalnum(c) //當c為字母或者數字時為真
isalpha(c) //當c為字母時為真
iscntrl(c) //當c為控制字符時為真
isdigit(c) //當c為數字時為真
isgraph(c) //當c不是空格但是可以打印時為真
islower(c) //當c是小寫字母時為真
isprint(c) //當c是可打印字符時為真
ispunct(c) //當c是標調符號時為真
isspace(c) //當c是空白時為真
isupper(c) //當c為大寫字母時為真
isxdigit(c) //當c是十六進制數字時為真
tolower(c) //若c為大寫字母,將其變為小寫字母,否則不變
toupper(c) //若c為小寫字母,將其變為大寫字母,否則不變
使用基於范圍的for語句來遍歷string對象中的每一個字符
范圍for語句(range for)是c++11中提出的,這種語句可以實現對給定序列中的每一個值進行處理。
//例如,輸出一個字符串的每一個字符
sting s("liangzid");
for(auto i:s)
{
cout<<i<<endl;
}
再給出一個統計標點符號的示例。
string s("liangzi zi zui shuai ! ");
i=0;
for (auto &c:s)
{
if (ispunct(c))
{
++i;
}
}
cout<<" the number of punctuation characters is : "<<i<<endl;
標准庫類型vector
標准庫類型vector表示同一類型的若干對象的集合,且集合中的每一個對象都有一個對應的索引。因為該向量(vector)容納着這些對象,所以說vector也被稱作是容器(container)。后面將會對容器進行更詳細的介紹。
C++中既有類模板(class template),也有函數模板。模板本身不是類和函數,模板相當於是編譯器為了生成類或者函數而編寫的一份說明。編譯器根據模板創建類或者函數的過程被稱為實例化(instantiation)。
對於類模板,需要添加一些額外信息以確定模板到底實例化成什么樣的類,一般來說,提供信息的方式為:再模板后面跟一對尖括號,里面放上相應的信息。
vector是一個類模板。
注釋:在c++11中,兩層vector的話必須在最后的兩個尖括號處放置空格,即vector<vector<int> >而非vector<vector<int>>。
定義和初始化vector對象
與string的定義和初始化類似,vector的用法如下:
vector<Type> v1 //v1是一個空的容器,執行Type類型的默認初始化
vector<Type> v2(v1) //v2是v1的副本
vector<Type> v3=v1 //直接初始化
vector<Type> v4(n,val) //v4包括了n個重復的均為val的元素
vector<Type> v5{a,b,c,...} //v5中容納的元素包括a,b,c,...
vector<Type> v6={a,b,c,...}//同上
向vector對象中添加元素
使用 push_back() 函數來將一個元素壓入vector對象的尾端。
//示例:初始化一個數列,里面儲存着從0到99的整數
vector<int> sequence;
for(int i=0;i !=100;i++)
{
sequence.push_back(i);
}
//使用示例:將之前讀取的沒一行作為一個元素,實現對任何不定長文件的動態儲存。
int main() {
string file_path, line;
fstream read_file;
vector<string> all_lines;
cout << "please input the need read path" << endl;
cin >> file_path;
read_file.open(file_path);
while (!read_file.eof())
{
getline(read_file,line);
all_lines.push_back(line);
}
read_file.close();
system("pause");
return 0;
}
vector的其他使用注意
和使用string一樣,也可以使用范圍for語句進行處理。
比如對前面讀取得到的vector向量進行逐元素的輸出,則可以使用range for語句。
int main() {
string file_path, line;
fstream read_file;
vector<string> all_lines;
cout << "please input the need read path" << endl;
cin >> file_path;
read_file.open(file_path);
while (!read_file.eof())
{
getline(read_file,line);
all_lines.push_back(line);
}
read_file.close();
for (auto &cc : all_lines)
{
cout << cc << endl;
}
system("pause");
return 0;
}
同樣地,我們可以使用下標索引來訪問vector<Type> CC中的每一個元素,但是無法用下標來創造一個新的元素。
迭代器(iterator)
迭代器是一種機制(可以看作是一種類型),類似於指針類型,迭代器提供了對對象進行間接訪問的方式。迭代器可以間接訪問容器中的某個元素或者string對象中的某一個字符。迭代器可以訪問某個元素,也可以從一個元素移動到另外一個函數。迭代器有有效無效之分,與指針相同,當迭代器指向某個元素或者尾元素的下一個位置時迭代器有效,其他情況均為無效。
使用iterator
一般利用begin和end來進行對容器的指向。begin成員負責返回第一個元素(或字符)的迭代器,end用來返回指向容器尾元素的下一個位置的迭代器——即該迭代器標記了容器的一個根本不存在的”尾后“(off the end)元素,用來表示我們已經處理完了容器中的所有元素。end成員返回的迭代器通常被稱為**尾后迭代器(off-the-end iterator)**或尾迭代器(end iterator)。如果容器為空,則begin與end均返回尾迭代器。
詳細見示例。
vector<int> liangzi{1,2,3,4,5};
auto begin_liangzi=liangzi.begin();
auto end_liangzi=liangzi.end();
//之所以使用auto,是因為我們也不確定到底其指定的是什么類型
iterator運算符
//通過以下運算符可以看出迭代器和指針的類似之處
*iter //返回迭代器iter所指向元素的引用
iter->mem //解引用iter並獲取該元素的名為mem的成員,等價於(*iter).mem
++iter //令iter指向容器中的下一個元素
--iter //令iter指向容器中的上一個元素
iter1==iter2 //若相等,指它們指向了容器中的同一個元素
iter1 != iter2 //同上
迭代器的運算部分
略
數組
數組是一種類似於vector的數據結構,但是數組的長度是固定的。數組在提高了一點性能的同時喪失了一點靈活性。
定義和初始化內置數組
定義數組時必須指定數組的類型(不能使用auto),定義數組時中括號里的量一定要是常量或者常量表達式。不能使用一個數組對另外一個數組進行初始化。
int i=100; //不是常量表達式
constexpr int ii=100;
int array1[100]; //正確
int *parray[100]; //正確,數組中的每個元素都是指向整型數據的指針
string array2[i]; //錯誤
string array3[ii]; //正確
int a[ ]={1,2,3};
int aa[ ]=a; //錯誤
int &liangzi[100]={1,2,3,...}; //錯誤,引用不是一個對象,不能被數組儲存
int *pliangzi[100]; //正確,數組中的每個圓度都是指針
int (*pliangzii)[100]=&array1; //正確,定義了一個指針,指向一個有100個元素的數組
int (&liangzia)[100]=array; //正確,是對一個數組的引用
/* 可以記住一句口訣:從右向左,從內向外*/
訪問數組元素
使用下標訪問
指針和數組的關系
與C語言中相似,數組名即是一個指向數組中第一個元素的指針。試圖定義一個指針指向數組中的某一個元素,可以使用
int a[100];
int *p=&a[20];
也可以使用
int *p2=a;
對於指針也相當於迭代器,c++11特別引入了兩個函數begin()和end()來描述一個數組的首端和尾后。有
int array[10];
int *parray=&array[10]; //尾后指針,不能進行解引用,也不能進行遞增
int *begin_array=begin(array); //下指向首元素的指針,相當於array
int *end_array=end(array); //尾后指針
多維數組
嚴格來說並不存在多維數組,多維數組表達的意義是數組的嵌套。比如說二維數組就是一個數組的數組。
在理解多維數組時,越靠近自變量名字的下標代表越基本的元素分布。比如int a[2][3][4];就代表有一個數組,長度為2,其中的每一個元素都包含着三個元素,這三個元素每一個都是一個長度為4的數組。
多維數組的初始化
與python的numpy類似,但是此處使用花括號。
int ia[3][4]={
{1,2,3,4},
{5,6,7,8},
{1,2,3,4}
};
當然,由於多維數組本質上是由1維數組表示的,所以也可以采用直接的一長串來表示。
多維數組的下標引用
可以使用下標運算符來訪問多維數組的元素。當下標的數目少於數組本身的維度時,返回的是一個差值維度的數組。
int ra[3][4];
int arr[3][4][5];
ra[2][3]=arr[0][0][0]; //元素之間的賦值操作
int (&row)[4]=ra[1]; //row是對一個長度為4的數組的引用
利用range for 處理多維數組
以二維數組為例進行說明。
//實現對二維數組中的所有元素進行求和
sum=0;
for (auto &i:array)
for (auto &j:i)
{
sum+=j;
}
