C++ 笔记 2 (C++ primer)
第七章
常量成員函數
將const關鍵字放在成員函數的參數列表之后時,緊跟在參數列表后面的const表示this是一個指向常量的指針。像這樣使用const的成員函數被稱作常量成員函數。
類作用域和成員函數
成員函數體可以隨意使用類中的其他成員而無需在意這些成員出現的順序。
this指針
個人感覺this就是指向該類本身的指針
構造函數
構造函數的名字和類名相同,但是構造函數沒有返回類型
類可以包含多個構造函數,和其它的重載函數差不多,不同的構造函數之間必須在參數數量或參數類型上有所區別
不同于其它成員函數的是,構造函數不能被聲明成const。當我們創建類的一個const對象時,直到構造函數完成初始化過程,對象才能真正取得其“常量屬性”。因此構造函數在const對象的構造過程中可以向其寫值。
默認構造函數
如果類中沒有顯示的定義構造函數,那么編譯器就會為我們隱式的定義一個默認構造函數
編譯器創建的構造函數又被稱為合成的默認構造函數
某些類不能依賴于合成的默認構造函數
①編譯器只有在發現類不包含任何構造函數的情況下才會替我們生成一個默認的構造函數。一旦我們定義了一些其他的構造函數,那么除非我們再定義一個默認的構造函數,否則類將沒有默認構造函數。
②合成的默認構造函數可能執行錯誤的操作。
③有的時候編譯器不能為某些類合成默認的構造函數。
定義構造函數
①默認構造函數
定義這個構造函數的目的僅僅是因為我們既需要其他形式的構造函數,也需要默認的構造函數。這個函數的作用完全等同于編譯器自動生成的默認構造函數
②構造函數初始值列表
其中的bookNo(s)相當于是將傳入構造函數的參數s用來初始化類的成員bookNo
③在類的外部定義構造函數
在類的外部定義構造函數時直接 類名::類名 創建函數體即可
class或struct關鍵字
class和struct的訪問權限不同
如果使用struct關鍵字,則定義在第一個訪問說明符之前的成員是public的;如果使用class關鍵字,則這些成員是private的
通常情況下,當我們希望定義的類的所有成員是public的時,使用struct;反之,如果希望成員是private的,使用class
友元
類可以允許其他類或者函數訪問它的非公有成員,方法是令其他類或者函數成為它的友元。(關鍵字是 friend)
如果一個類指定了友元類,則友元類的成員函數可以訪問此類包括非公有成員在內的所有成員。(包括private)
令成員函數作為友元
可以單獨指定友元中的某個函數
可變數據成員
有時會發生一種情況,我們希望能修改類的某個數據成員,即使是在一個const成員函數內。可以通過在變量的聲明中加入mutable關鍵字做到這一點。
就是可以使用mutable來設置想要在const類型的函數里也可以改變的變量
委托構造函數
此例中,第一個構造函數是被委托的構造函數
抑制構造函數定義的隱式轉換(explicit)
此時,沒有任何構造函數能用于隱式的創建Sales_data對象
關鍵字explicit只對一個實參的構造函數有效。需要多個實參的構造函數不能用于執行隱式轉換,所以無須將這些構造函數指定為explicit的。只能在類內聲明構造函數時使用explicit關鍵字,在類外部定義時不應重復:
explicit構造函數只能用于直接初始化
為轉換顯示的使用構造函數
盡管編譯器不會將explicit的構造函數用于隱式轉換過程,但是我們可以使用這樣的構造函數顯式的強制進行轉換
聚合類
聚合類使得用戶可以直接訪問其成員,并且具有特殊的初始化語法形式。
當一個類滿足以下條件時,我們說它是聚合的:
所有成員都是public的
沒有定義任何構造函數
沒有類內初始值
沒有基類,也沒有virtual函數
以下是一個聚合類:
以下是初始化的方式:
初始值的順序必須與聲明的順序一致,也就是說,第一個成員的初始值要放在第一個,然后是第二個,以此類推。
聚合類中,如果初始值列表中的元素個數少于類的成員數量,則靠后的成員被值初始化。初始值列表的元素個數絕對不能超過類的成員數量
值得注意的是,顯式的初始化類的對象的成員存在三個明顯的缺點:
類的靜態成員
聲明靜態成員可以通過關鍵字static
使用類的靜態成員:
定義靜態成員
可以在類的內部定義也可以在類的外部定義靜態成員,在類的內部定義靜態成員的方法如上所示,如需在類的外部定義靜態成員則如下所示(如果不懂就到時候再細查吧)
?
第八章
IO類
標準庫定義了很多不同的IO類型,分別定義在三個獨立的頭文件中:iostream定義了用于讀寫流的基本類型,fstream定義了讀寫命名文件的類型,sstream定義了讀寫內存string對象的類型。
寬字符
寬字符版本的類型和函數的名字以一個w開始。例如,wcin、wcout和wcerr是分別對應cin、cout和cerr的寬字符版對象。
?
IO對象無拷貝或賦值,就是說我們不能對IO對象進行拷貝或賦值,因此我們也不能講形參或返回類型設置為流類型。
?
條件狀態
IO操作一個與生俱來的問題是可能發生錯誤。一些錯誤是可恢復的,而其他錯誤則發生在系統深處,已經超出了應用程序可以修正的范圍。
流的當前狀態(rdstate)
cin.rdstate();//cin的當前狀態
管理輸出緩沖
導致緩沖刷新(即,數據真正寫到輸出設備或文件)的原因有很多:
程序正常結束,作為main函數的return操作的一部分,緩沖刷新被執行
緩沖區滿時,需要刷新緩沖,而后新的數據才能繼續寫入緩沖區
我們可以使用操縱符如endl來顯式刷新緩沖區
在每個輸出操作之后,我們可以用操縱符unitbuf設置流的內部狀態,來清空緩沖區。默認情況下,對cerr是設置unitbuf的,因此寫到cerr的內容都是立即刷新的
一個輸出流可能被關聯到另一個流。在這種情況下,當讀寫被關聯的流時,關聯到的流的緩沖區會被刷新。例如,默認情況下,cin和cerr都關聯到cout。因此,讀cin或寫cerr都會導致cout的緩沖區被刷新
刷新輸出緩沖區
endl:完成換行并刷新緩沖區的工作
flush:刷新緩沖區,但不輸出任何額外的字符
ends:向緩沖區插入一個空字符,然后刷新緩沖區
unitbuf操縱符
如果想在每次輸出操作后都刷新緩沖區,我們可以使用unitbuf操縱符。它告訴流在接下來的每次寫操作之后都進行一次flush操作。而nounitbuf操縱符則重置流,使其恢復使用正常的系統管理的緩沖區刷新機制:
?
如果程序崩潰,輸出緩沖區不會被刷新
當調試一個已經崩潰的程序時,需要確認那些你認為已經輸出的數據確實已經刷新了。否則,可能將大量時間浪費在追蹤代碼為什么沒有執行上,而實際上代碼已經執行了,只是程序崩潰后緩沖區沒有被刷新,輸出數據被掛起沒有打印而已
?
關聯輸入和輸出流
當一個輸入流被關聯到一個輸出流時,任何試圖從輸入流讀取數據的操作都會先刷新關聯的輸出流。
交互式系統通常應該關聯輸入流和輸出流。這意味著所有輸出,包括用戶提示信息,都會在讀操作之前被打印出來。
tie有兩個重載的版本:一個版本不帶參數,返回指向輸出流的指針。如果本對象當前關聯到一個輸出流,則返回的就是指向這個流的指針,如果對象未關聯到流,則返回空指針。tie的第二個版本接受一個指向ostream的指針,將自己關聯到此ostream。即,x.tie(&o)將流x關聯到輸出流o。
?
文件輸入輸出
頭文件fstream定義了三個類型來支持文件IO::ifstream從一個給定文件讀取數據,ofstream向一個給定文件寫入數據,以及fstream可以讀寫給定文件。
?
下表中的操作僅可以對fstream、ifstream和ofstream對象調用,不可對其他IO類型調用:
?
文件模式
每個流都有一個關聯的文件模式(file mode),用來指出如何使用文件。
指定文件模式:
無論用哪種方式打開文件,我們都可以指定文件模式,調用open打開文件時可以,用一個文件名初始化流來隱式打開文件時也可以。指定文件模式有如下限制:
只可以對ofstream或fstream對象設定out模式
只可以對ifstream或fstream對象設定in模式
只有當out也被設定時才可設定trunc模式
只要trunc沒被設定,就可以設定app模式。在app模式下,即使沒有顯式指定out模式,文件也總是以輸出方式被打開。
默認情況下,即使我們沒有指定trunc,以out模式打開的文件也會被截斷。為了保留以out模式打開的文件的內容,我們必須同時指定app模式,這樣只會將數據追加寫到文件末尾;或者同時指定in模式,即打開文件同時進行讀寫操作
ate和binary模式可用于任何類型的文件流對象,且可以與其他任何文件模式組合使用
?
每個文件流類型都定義了一個默認的文件模式,當我們未指定文件模式時,就使用此默認模式。與ifstream關聯的文件默認以in模式打開;與ofstream關聯的文件默認以out模式打開;與fstream關聯的文件默認以in和out模式打開。
?
以out模式打開文件會丟棄已有數據
默認情況下,當打開一個ofstream時,文件的內容會被丟棄。阻止一個ofstream清空給定文件內容的方法是同時指定app模式:
//在這幾條語句中,file1都被截斷
ofstream out("file1");//隱含以輸出模式打開文件并截斷文件
ofstream out2("file1",ofstream::out);//隱含的截斷文件
ofstream out3("file1",ofstream::out|ofstream::trunc);
//為了保留文件內容,我們必須顯式指定app模式
ofstream app("file2",ofstream::app);//隱含為輸出模式
ofstream app2("file2",ofstream::out|ofstream::app);
保留被ofstream打開的文件中的已有數據的唯一方法是顯式指定app或in模式。
每次調用open時都會確定文件模式
對于一個給定流,每當打開文件時,都可以改變其文件模式。
通常情況下,out模式意味著同時使用trunc模式。
?
string流
sstream頭文件定義了三個類型來支持內存IO,這些類型可以向string寫入數據,從string讀取數據,就像string是一個IO流一樣
istringstream從string讀取數據,ostringstream向string寫入數據,而頭文件stringstream既可從string讀數據也可向string寫數據。
頭文件sstream中定義的類型都繼承自iostream頭文件中定義的類型,除此之外sstream中定義的類型還增加了一些成員來管理與流相關聯的string。這些操作可以對stringstream對象調用,但不能對其他IO類型調用這些操作:
istringstream
當我們的某些工作是對整行文本進行處理,而其他一些工作是處理行內的單個單詞時,通常可以使用istringstream。
?
第九章
順序容器
順序容器提供了控制元素存儲和訪問順序的能力,這種順序不依賴于元素的值,而是與元素加入容器時的位置來對應。有序和無序關聯容器,則根據關鍵字的值類存儲元素。
順序容器類型
容器選擇的基本原則
大多數情況下vector都是適用的,但是有時也需要根據功能的不同來確定不同的容器類型。
容器操作
迭代器
forward_list迭代器不支持遞減運算符 (--)
迭代器支持的算術運算只能應用于string、vector、deque、和array的迭代器,我們不能將它們用于其他任何容器類型的迭代器。
迭代器范圍
一個迭代器范圍由一對迭代器表示,兩個迭代器分別指向同一個容器中的元素或是尾元素之后的位置。(begin和end或first和last,它們標記了容器中元素的一個范圍)
雖然第二個迭代器被稱為end或last,但是第二個迭代器從來都不會指向范圍中的最后一個元素,而是指向尾元素之后的位置。迭代器范圍中的元素包含first所表示的元素以及從first開始直至last(但不包含last)之間的所有元素。
這種元素范圍被稱為左閉合區間
使用左閉合范圍蘊含的編程假定
?
begin和end的版本:
帶r的版本返回反向迭代器;以c開頭的版本則返回const迭代器? begin、rbegin、cbegin、crbegin
容器定義和初始化
將一個容器初始化為另一個容器的拷貝
當將一個容器初始化未另一個容器的拷貝時,兩個容器的容器類型和元素類型都必須相同
列表初始化(除array外)
也可以這樣
array類型
定義一個array時,除了指定元素類型,還要指定容器大小:
為了使用array類型,需要同時定義元素類型和大小:
array大小固定的特性也影響了它所定義的構造函數的行為。與其他容器不同,一個默認構造的array是非空的:它包含了與其大小一樣多的元素,這些元素都被默認初始化。如果對array進行列表初始化,初始值的數目必須等于小于array的大小。如果元素類型是一個類類型,那么該類必須有一個默認構造函數,以使值初始化能夠進行:
容器賦值運算
assign
assign僅除array之外的順序容器可以使用(將原容器中的所有舊的元素都替換掉),以下是用法:
swap
swap操作交換兩個相同類型容器的內容
假設 *p原來指向svec0[0],兩個vec交換之后,*p指向的則變成了svec1[0]。(因為指針指向的是元素的地址,所以*p從始到終指向的都是同一個地址,發生改變的只是vec的元素排列而已)
容器大小操作
size返回容器中的元素的數目
empty當size為0時返回布爾值true,否則返回false
max_size返回一個大于或等于該類型容器所能容納的最大元素數的值
forward_list支持max_size和empty,但不支持size
關系運算符
每個容器類型都支持相等運算符(==和!=)除了無序關聯容器外的所有容器都支持關系運算符(>、>=、<、<=),關系運算符左右兩邊的運算對象必須是相同類型的容器,且必須保存相同類型的元素。
比較兩個容器實際上是進行元素的逐對比較,這些運算符的工作方式與string的關系運算類似:
只有當其元素類型也定義了相應的比較運算符時,我們才可以使用關系運算符來比較兩個容器
?
順序容器操作
容器分為順序容器和關聯容器,所以與它們相關的操作也分為所有容器都可以用的通用操作和只有個別容器可以用的獨有操作
以下是向順序容器添加元素的操作
可使用push_back(尾部添加)的是 vector、list、deque。(其實string也可以 string str; str.push_back("a")相當于是str+=a)
可使用push_front(首部添加)的是list、forward_list、deque。
可使用insert(在指定位置添加)的是vector、deque、list、string,而forward_list提供了特殊版本的insert
slist.insert(iter,"hello");//將"hello"添加到iter之前的位置
list<string> lst;auto iter = lst.begin();string word;while (std::cin >> word) {iter = lst.insert(iter,word);//等價于調用push_front//在這里insert的返回值是新添加進的元素, 利用這種方式,來將iter永遠指向第一個元素}
使用emplace操作
emplace直接將參數傳遞給元素類型的構造函數,如下:
Sales_data的構造函數接收的參數為 (string,int,double)如果有重載的構造函數,也可使用其它形式的構造函數的參數類型
不同容器間的拷貝
int ia[] = { 0,1,1,2,3,5,8,13,21,55,89 };
? vector<int> vec(ia,ia+9);
?list<int> lis(ia, ia + 9);
訪問元素
一下的vec代指容器
vec.begin()和vec.end()是迭代器 相當于指針
vec.front()是首元素的引用 vec.back()是尾元素的引用(注意,back是尾元素的引用,end是尾元素后一位的指針,如果要用end來獲取尾元素的話,必須首先遞減此迭代器)
在調用front和back(或begin和end)之前,要確保vec非空 (!vec.empty())
下標操作和安全的隨機訪問(以下僅為vs2015? x64的測試結果)
書上是這么說的:
下面是親測結果:
vector<int> ivec;//ivec是一個空的vectorint a = ivec.at[0]; //直接報錯并指向錯誤的位置int b = ivec[0];//發生不確定的狀況int c = *ivec.begin();//直接崩潰int d = ivec.front();//直接崩潰
?
刪除元素
順序容器的刪除操作
forward_list
forward_list中未定義insert、emplace和erase,而是定義了insert_after、emplace_after和erase_after
還有首前迭代器 before_begin
改變容器大小
順序容器大小操作
容器操作可能使迭代器失效
在向容器添加元素后:
當刪除一個元素后:
管理迭代器
更新迭代器
在循環中調用insert或erase,會更容易的更新迭代器
如果在一個循環中插入/刪除deque、string或vector中的元素,不要保存end返回的迭代器,反正end執行的很快,干脆每次直接獲取好了。
?
轉載于:https://www.cnblogs.com/lingLuoChengMi/p/8252449.html
總結
以上是生活随笔為你收集整理的C++ 笔记 2 (C++ primer)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: css3滤镜Filter使用
- 下一篇: python3远程连接MySQL