C++Primer第5版學習筆記(二)第三章的重難點內容 你可以點擊這裡回顧第一、二章的內容 這篇文章只是C++初學者的學習筆記...。書接前文,第三章主要講這麼五個概念: 1.using聲明,我知道挺多同學寫代碼練手都要在源文件前幾句直接加using namespace std;然而us...
C++Primer第5版學習筆記(二)
第三章的重難點內容
你可以點擊這裡回顧第一、二章的內容
這篇文章只是C++初學者的學習筆記...。書接前文,第三章主要講這麼五個概念:
1.using聲明,我知道挺多同學寫代碼練手都要在源文件前幾句直接加using namespace std;然而using語句並不是什麼情況都這麼使用的,稍後我們將會看到詳細的用法。
2.標準庫類型string,和C味的字元數組有區別的string,到底是怎麼個構造,這章將會講述。
3.標準庫類型vector,vector和數組區別很大,這裡將會提到,並引入一個“容器”的重要概念。
4.迭代器,迭代器用來代替下標這種傳統方式訪問容器或一些支持迭代器的類型。
5.數組和多維數組,經典概念。配合指針食用更佳。
下麵是這一章的知識點:
知識點1:P75,3.1,頭文件和using語句
儘管我們可以在各種文件里都使用using namespace std;或者using std::endl;這種語句,但是,在頭文件包含命名空間可能產生各種意外。因此,頭文件不應包含using聲明。
知識點2:P76,3.2.1,定義和初始化string對象
以下幾種初始化語句被string支持:
string s1;//創建了一個空的字元串,對象名為s1,類型為string類型。
string s2(s1);//是s2的值與s1的值相等。
string s2=s1;//同上一句,拷貝初始化。
string s3("value");//直接用字元串字面值初始化string類型的對象。
string s3="value";//字元串字面值轉化為string類型變數並賦值給string。
string s4(n,'c');//直接初始化string,操作後s4擁有10個字元,每個字元的值都是'c'。
最後,其實string s5={"value"}和string s3="value"一樣,也是合法的。
知識點3:P77,3.2.2,string支持的操作
1.輸入流中獲取字元串 首先要強調是cin>>string的操作,這種操作就是從輸入流中讀字元串,值得註意的是這個過程會忽略掉開頭輸入的各種空白(我們說空白時是在說 空格,換行符,製表符),讀取輸入流直到遇到字元後的第一個空白為止,另一種getline(cin,string);的操作則可以讀一行,也就是讀入輸入流的數據(包括空格,製表符),直到遇到換行符為止,這裡輸入流中的換行符本身已經被讀過了,但是字元串里不保存這個換行符。下次再從輸入流里讀什麼數據至少也要從這個換行符後面對輸入流進行操作了。
2.string::type_size 為了更抽象,脫離機器特性,調用每個string對象的size成員函數,返回值都是一個string::type_size類型,這個類型擁有無符號整形數的一些性質。在string對下標的支持中,[ ]中的數字也會被轉換為string::type_size類型。
3.string對象與字元串字面值相加 字元串字面值是字元數組類型,字元串字面值和string類型的對象在一起計算時會被自動轉換為string類型。
4.其他支持的操作:包括下標運算符[ ]、重載的+、==、!=、<、>、<=、>=。
知識點4:P82,3.2.3,範圍for(range for)語句
範圍for語句用於遍歷元素。形如:for(一個用於訪問序列中基礎元素的變數a : 被訪問的序列對象b){statement..... blabla;}
首次初始化,變數a的值會被初始化為對象b序列中的第一個元素,迭代之後每次訪問下一個元素,直到序列被完全訪問結束。
可以使用auto &a的方式聲明變數a,使變數綁定到具體的序列元素上,從而進行更改。如在for(auto a : str){}中,每次把a初始化的行為實質上是使a獲得str每個元素的副本(拷貝),而for(auto &a : str){}這樣的語句則使a成為了str對應的每個元素的"別名",從而可以修改str。
使用範圍for迴圈遍歷多維數組,為了不手動打類名,也為了防止外層數組的名被auto成指針,要在對外層數組的訪問上都加上&綁定。
知識點5:P82,3.2.3,處理每個字元的頭文件cctype
我們可以通過引用頭文件<cctype>的形式處理每一個字元。這個頭文件包含很多方便處理字元的函數。列舉如下:
isalnum(c); //當c是字母或者數字時為真 isalpha(c); //當c是字母時為真
iscntrl(c); //當c是控制字元時為真
isdigit(c); //當c是數字時為真 isgraph(c); //當c不是空格但是可列印時為真
islower(c); //當c是小寫字母為真
isprint(c); //當c可列印時為真 isupper(c); //當c是大寫字元時為真
isxdigit(c); //當c是16位數字時為真
ispunct(c); //當c是標點符號時為真(一個字元除了控制字元,字母,數字,可列印空白就是標點符號)
isspace(c); //當c是空白時為真(空白包括空格,橫向/縱向製表符,回車符,換行符,進紙符)
tolower(c); //把大寫字元轉換為小寫字元,本來就是小寫字元的不變,返迴轉換後的字元
toupper(c); //把小寫字元轉換為大寫字元,本來就是大寫字元的不變,返迴轉換後的字元
知識點6:P87,3.3,類模板、容器和實例化
當我們在C++裡面談論容器這個概念時,我們應該知道容器是用來存儲和組織一類特定對象的集合。下麵提到的標準庫類型vector,就是一個容器。
類模板一般用於按照模板規定好的規則生成不同的類。我們無需很麻煩的一個一個寫類的定義,只需使用模板,給出指定的少量信息,類模板就會幫助我們自動生成一個我們可以直接使用的類。vector也是一個類模板。
通過類模板創建類的過程,或者通過類型創建對象的過程,就叫做實例化。
知識點7:P87,3.3.1,定義和初始化vector對象
與string的定義和初始化一樣,我們也可以使用多種方式定義和初始化一個vector對象。
以下幾種初始化語句被string支持:
vector<Type> v1;//創建了一個空的vector容器,這個容器是Type類型對象的集合,這個集合名字叫做為v1,執行預設初始化。
vector<Type> v2(v1);//創建了一個叫做v2的vector容器,這個容器的內容和v1相同。
vector<Type> v2=v1;//同上一句,拷貝初始化。
vector<Type> v3{a,b,c};//v3包含了初始值個數的元素。
vector <Type> v4={a,b,c};//同上。
vector <Type> v5(n,val);//直接初始化這個容器,操作後v5擁有n個元素,每個元素的值都是val。
vector <Type> v5(n);//直接初始化這個容器,操作後v5擁有n個元素,每個元素的值都被預設初始化。
當我們使用圓括弧()初始化對象時,IDE會認為我們在通過語句“構建”這個對象 ;當我們使用花括弧{ }初始化對象時,IDE會認為我們在初始化對象。
當我們使用等號=初始化對象時,我們就執行了“拷貝初始化”;當我們不使用=初始化對象時,我們就執行了“直接初始化”。
但是當我們在花括弧裡面給一個不符合對象類型的值,系統就會認為我們正在構建而非初始化對象,一個體現就是,vector<string> s1{10}; 這個語句中,10不能轉換為string,因此被系統理解為“這個string容器里有10個元素”。當然,像vector <string>s1={10};這樣的語句是錯誤的,因為=就應該是拷貝初始化了,然而10並不能夠被轉化為string因此也無法賦值。
知識點8:P90,3.3.2,vector支持的操作
1.向容器的後面添加元素:已存在vector<T> v;,可以使用 v.push_back(vector<T> a)的方式在集合v的尾部添加元素。
2.empty和size函數成員:已存在vector<T> v;,可以使用 v.empty()的方式判斷v是否為空,可以使用v.size()的方式返回v的大小。
3.重載的運算符:vector支持的運算符包括下標運算符[ ]、重載的+、==、!=、<、>、<=、>=。這一點和string類似。
知識點9:P95,3.4.1,迭代器
為了訪問容器的元素(有些容器可能不支持下標運算符),因此C++提供了迭代器這個概念來訪問容器中的指定元素。
支持迭代器的類都會提供名為begin和end的函數成員來供我們獲取迭代器。如已定義vector<int> i1(10);這時使用auto ben=i1.begin();這個語句獲取指向第一個字元的迭代器,使用auto end=i1.end();獲取指向i1容器最後一個元素的下一個元素的迭代器,術語“尾後迭代器”。
當使用vector <int> 創建類時,這個類的命名空間就是vector <int>,命名空間中的迭代器類型寫作vector<int>::iterator。因為這個叫做"vector<int>::iterator"的迭代器類型名太長了也不好記,這裡我們使用auto推導這個類型。用成員函數cbegin和cend可以推導出底層const迭代器,就是這個迭代器對迭代器指向的內容只讀不寫。第6章會詳細說明。
迭代器 對 迭代器指向的容器內容 可以像 指針 對 指針指向的數組元素一樣使用。
知識點10:P102,3.5.1,一維數組的定義和初始化
一維數組聲明形式:類型名 數組名[一個常量]。比如int a[15];這裡這個數組的名字是a,有15個元素,每個元素都是int型的。再比如int *a[15];這裡a數組的15個元素都是int *型的,即指向int的指針,這樣的指針有15個,構成了一個數組。雖然有指針數組,但是不存在元素都是引用類型的數組。
一維數組的初始化方式就是花括弧初始化,形如int a[n]={1,2,3};大括弧裡面的內容就是初始化列表,n為數組大小,可以預設,預設時數組長度由初始化列表的元素個數決定。當初始化列表的值的個數比數組長度小,數組剩下的元素被初始化為預設的值,比如對於有10個元素的int型數組,如果只給出第一個元素的值,後幾個元素將被初始化為0。
當我們聲明int a[]的時候代表通過數組名a訪問這個數組。我們也可以定義指向數組的指針和指向數組的引用來間接訪問這個數組。已有int arr[10];的情況下,int (*ptr) [10]=&arr;這條語句可以使指針ptr指向arr這整個數組。int (&ref)[10]=arr;則會使ref作為整個arr數組的引用。int *(&ref)[10]=arr;這個語句則是說ref是arr的引用,這個被引用的數組的類型是指針數組。
auto a=一個數組名,a的類型將會是這個指針,指針指向的類型就是數組元素的類型。用decltype(一個數組名) a;這樣的形式,a將會是和數組名屬性一致的數組。
知識點11:P111,3.5.5,用數組初始化vector對象 和 用string對象賦值字元數組
作為與舊代碼的介面,C++提供了方便的把數組轉化為vector對象的方法。在聲明vector對象時,我們可以通過迭代器用一個數組初始化vector。在已經存在int oldarray[10];的情況下,聲明的語句形如:vector<int> arr( begin(oldarray) , end(oldarray) );可以把arr初始化為oldarray。begin和end這兩個函數在<iterator>頭文件里,作用是返回數組的首元素/尾後指針。這種初始化接受兩個參數:拷貝開始部分指針和結束部分的指針。我們也可以寫形如int arr[10]={0}; vector <int> newarr( arr+1 , arr+4 );這種方式拷貝數組arr的第2~第5號元素,並用它們初始化newarr。
類似地,我們可以通過string a("23333333\n"); const char *b=a.c_str();這樣的語句使string型的a被賦值給字元數組指針b。返回結果是const是為了確保我們不會通過這個指針改動返回的字元數組的值。可能我們如果通過這個返回的指針改動這個字元數組,也就改動了字元串的值,因此這個操作不被允許。就是這樣。