1. << > > 輸出 輸入運算子返回其左側物件,因此他們可以連用。並且他們沒有規定求值順序(p123)
2. 註釋界定符不能巢狀。
3. 讀取數量不確定的輸入 while (cin>>value ) sum += value ;當時用istream物件作為條件時,會檢測流的狀態,當流發生錯誤或遇到EOF時狀態無效,條件為假。
在windows中 輸入檔案結束符的方法是ctrl+z,再敲擊enter。
int main(){
int value ,sum = 0 ;
while (cin>>value ) sum += value ;
cout<<sum;
return 0 ;
}
4. for (unsigned int i;i>=0 ;--i)
5.預設初始化:
對於內建型別,當其位於任何函式內部時,不被初始化,其值是未定義的。
6. 如果想宣告一個變數而不定義它,就在其型別前加extern ,並且不初始化。在函式體內部顯示初始化一個extern 變數會引發錯誤。
7.型別修飾符(* &等)和變數名寫一起較好。
8. 一般地,頂層const 可以表示任何物件是常量,而底層const 和指標引用等複合型別的基本型別部分
9. typedef char *pstring
const pstring cstr = 0
10. auto 在進行型別推斷時,會捨棄頂層const 而保留底層const (p62);
auto 用於在一條語句中定義多個變數時,變數的初始值型別應相同。
11. 與auto 不同,decltype 會保留頂層const 。
如果decltype 作用於解引用,則會得到一個引用型別:
int *p = i;
decltype ()的括號中如果又加了一層或多層括號,則得到引用型別:
int i;
decltype ((i)) d;
decltype (i) d;
decltype 作用於左值,得到引用,作用於右值,得到右值的型別。(p121)
Note: 賦值表示式的型別是左值的引用:
int a;a = b的型別是int &;
12. 類內初始值使用花括號或者等號,不能使用圓括號。
13. string 的讀操作會自動忽略開始的空白,讀取到其後的第一處空白為止。
getline讀取換行符但不儲存。
14 . 陣列名作為begin 和end 函式的引數可以得到類似迭代器的首尾指標。利用這個可以用陣列初始化向量。
15. 範圍for 訪問多維陣列(以二維為例):
for (auto &row : a)
for (auto col : row)
16. sizeof 運算子有兩種形式:
sizeof (type)
sizeof expr
sizeof 並不計算表示式的值,對於sizeof *p 即使p是一個無效的指標,也可以使用,sizeof 不解引用也能知道其型別。
sizeof 不會將陣列名轉化為指標,作用於陣列時他返回陣列的大小(位元組數)而非陣列元素的個數
17 . 在某一case 語句中初始化(顯示或隱式)的變數不能在另一case 語句中使用。原因在於初始化語句可能被跳過。
18. 使用引用形參可以實現“返回”多個“返回值”。
將形參定義為常量引用就可以使用字面值實參。
如果將匹配字元的函式find定義為:
string::size_type find(string &s, char c);
1 ) 那麼形如find("string" ,'r' )的呼叫將導致編譯錯誤。
2 )
void fun(const string &s){
char ch;
find(s,ch);
...
}
形如這樣的函式定義也將報錯,因為不能用fun中的常量引用來初始化find中的普通引用。
19. 管理陣列實參的三種方法(防止陣列越界的錯誤):
1 ) 給陣列末尾加一個結束符,以此判斷,例如C 風格字串末尾的'\0'
2 ) 同時傳遞首元素和末尾元素
3 ) 同時傳遞收元素和陣列長度。
20. 陣列引用形參:
void (int (&arr)[10 ])
21. 可變形參:
可變形參通過initializer_list實現,這是與vector類似的模板型別,eg:
void fun (initializer_list<string> lst);
fun ({"string1" ,"string2" });
fun ({"string1" ,"string2" ,"string3" });
22. 返回一個引用型別返回的是左值,返回其他型別返回的是右值。
23. C++ 11 允許返回一個列表:
vector <string > fun(){
return {"s1" ,"s2" };
}
24. 尾置返回型別:
auto fun(int i) -> int (*)[10 ];
使用decltype :
int a[] = {1 ,2 ,3 ,4 ,5 }
decltype (a) *fun(int i);
25. 過載函式中,頂層const 無法與普通型別區分開來。(p208)
26. const_cast 與過載:
假如我們在之前定義了:
const string & shorter(const string & s);
現在我們希望當傳入非常量引用時也返回非常量值,那麼:
string & shorter(string & s){
auto &r shorter(const_cast <const string &>(s));
return const_cast <string &> (r);
}
27. 只能省略靠後的預設實參;
函式的多次宣告可以修改預設實參(把普通形參改成預設實參,不能改變已有的預設實參
void fun(int a, int b, char c = 'c' );
void fun(int a, int b = 2 , char c);
void fun(int a, int b, char c = 'a' );
28 . 預處理巨集assert ,assert 定義在cassert中,使用格式為
assert (expr);
當expr表示式為假,assert 輸出資訊並終止程式執行,為真時什麼也不做。可以使用 #define NDEBUG 來關閉除錯模式,忽視assert 。
29. C++編譯器定義的幾個變數名:
__func__ 存放當前函式名;
__LINE__ 存放當前行號的整型字面值;
__FILE__ 存放檔名的字串字面值;
__TIME__ 存放編譯時間的字串字面值;
__DATA__ 存放編譯日期的字串字面值;
30.
1 ) funcname是一個函式名,pf是一個函式指標,則下列兩種賦值方法都是正確的:
pf = funcname;
pf = &funcname;
2 ) 函式指標做形參:和陣列類似,雖然函式不能直接作為引數使用,但是函式指標卻可以:
void fun(int a, int b, int fun2 (args));
void fun(int a, int b, int (*fun2)(args));
fun(1 ,2 ,funcname);
3 )使用型別別名和decltype 簡化宣告:
typedef bool fun(const string & , const string &);
typedef decltype (compare) fun;
type bool (*fun)(const string & , const string &);
typedef decltype (compare) *fun;
(p222)
31. 類的成員函式通過一個名為this 的隱式引數來訪問呼叫它的物件。
class .func();
任何自定義的名為this 的引數或變數都是違法的。 在類的內部我們可以使用this (實際上沒有這個必要),表示當前類的地址。
32. 常量成員函式:
預設情況下,this 是一個指向非常量物件的常量指標,例如在Sales_data類中, this 的型別是 Sales_data *const , 因此this 不能指向常量物件,也就是說我們不能在常量物件上呼叫普通成員函式,因此需要引入常量成員函式:在C++中,允許在成員函式引數列表後面緊接著寫上const ,將函式定義為常量成員函式,常量成員函式不能改變呼叫它的物件的值。
常量成員函式如果以引用的形式返回*this ,則返回的是一個常量引用。
33. 編譯器首先處理類中成員的宣告,再處理成員函式體,因此在成員函式體中可以隨意使用成員,而無需考慮他們出現的次序。
34. 我們可以在類的內部宣告成員函式,而在類外部定義成員函式,在類外部定義時,應該在函式名前面用::指明作用域(所在的類),在這種情況下,實現行內函數有兩種方法:
1 ) 在類內部宣告時寫上inline 。
2 ) 在類外部定義時寫上inline 。
35. 為了在需要的情況下能連續使用.運算子,某些函式需要返回類本身,所以它們的返回型別宣告和返回語句分別應為:
classname &func(args);
return *this ;
36. 建構函式初步:
1 ) 建構函式沒有返回型別,函式名和類名一樣(這可以解釋string 等類型別用()初始化的形式)
2 ) 可以顯示地定義預設建構函式:Sales_data() = default ;
3 ) 建構函式初始值列表:
Sales_data(const string &s, unsigned n, double p):
bookNo(s),unit_sold(n), revenue(p*n){}
如上所示,冒號以及左花括號之間的部分成為建構函式初始值列表。
37. 可以在類中定義型別別名,這樣的型別別名也有public 和private 之分(p243)
38. 可變資料成員:將某資料成員宣告為mutable ,則它永遠不會是const ,甚至它可以被常量成員函式所修改。
39. 當一個類完全完成之後才算是被定義,因此類的成員不能是自己;但是一個類的名字一旦出現,就被認為已經宣告過,因此類的成員可以是自身的指標或者引用。
40. 將類的成員函式宣告為友元,必須按照以下的順序編寫程式(假設類A中的func 函式要訪問類B):
1)先定義類A,並宣告func ,但不定義func
2)定義類B,包括對func 的友元宣告
3)完成func 的定義(類外定義,注意指明它所屬的類)
如果想宣告一組過載成員函式為類的友元,必須對每一個都宣告。
41. 在類的外部定義成員函式時,必須用
但是對於函式的返回型別,它出現在類名之前,所以它在需要用到該類中的名字時需要用
42. 建構函式初始化列表的順序和其實際執行初始化的順序無關,執行初始化的順序和其在類中出現的順序一樣。
43. 能通過一個實參呼叫的建構函式定義了一條從該引數型別到該類型別的轉換規則。只允許一步類型別的轉換。當建構函式宣告為explicit 時不能使用拷貝初始化。(p264)
44. 一般只能在類內部宣告靜態成員,而在類外部定義它,比如
static double a;
double classname:: a = func();
如果靜態成員是字面值常量型別的constexpr ,則可以給它提供const 整型的類內初始值。
45. 如果一個靜態成員僅應用於編譯器可以替換其值的情況,則它不需要在類外定義,否則需要在類外定義(p271)
46. 與非靜態型別相比:靜態型別是不完全型別,它的型別可以就是其所在的類型別。靜態成員可以做成員函式和建構函式的預設實參,而費靜態成員不能。
47. IO流物件不能夠拷貝,所以它們之間不能夠相互賦值,也不能夠直接作為形參和返回值,只用將其引用作為形參和返回值。讀寫一個IO物件會改變其值,因此他們不能是const 。
48. strm.rdstate()返回的標誌位順序為fallbit eofbit badbit 不包括goodbit, rdstate返回的值時以上三個標誌位組成二進位制值的十進位制表示。
49. 緩衝區重新整理:
1 )程式正常結束時。
2 )緩衝區滿時。
3 )用endl,flush,ends等操作符顯示重新整理。
4 )在每個輸出操作後,用操作符unitbuf來設定流的內部狀態,來清空緩衝區。預設情況下cerr 是設定unitbuf的,因此每次cerr 後會立即重新整理。
5 )讀寫被關聯的流時,關聯到的流會重新整理。
p(282 )
50. 程式崩潰時緩衝區不會重新整理,因此有時候在崩潰之前程式的某一部分已經執行過,只是沒有列印出來。
51. 在互動式系統中,任何輸出提示資訊都應該在使用者輸入之前列印出來,所以cin 和cout 關聯,在執行cin 之前,輸出緩衝區會被重新整理。
cin .(&cout )
注意:每個流最多同時關聯到一個流,但多個流可以同時關聯到同一個流。
52. 選擇順序容器的一些小技巧:
1 )很多容器在中間位置的插入很慢,比如vector 等,有時候我們可以選擇在容器的末尾插入,再用某些演算法進行重排,來達到與直接在中間插入相同的效果。
2 )如果必須要在中間插入,則在輸入階段可以選擇list 容器,輸入完成後將其拷貝到一個vector 裡。
53. 如下的構造方法需要注意:
vector <NoDefault> (10 ,init);
vector <NoDefault> (10 );
54. 順序容器的型別別名:
iterator
const_iterator
size_type
difference_type
value_type
reference
const_reference
...
list <int > ::iterator itr;
利用這些型別別名,我們可以在不清楚容器元素具體型別的時候進行相關操作。
55. 容器的定義和初始化
//預設建構函式
C c
//將c1初始化為c2的拷貝,如果是array,大小必須相同
C c1(c2)
C c1 = c2
//列表初始化,對於array,列表長度需小於等於array長度。
C c{a,b,c...}
C c = {a,b,c...}
//迭代器範圍初始化
C c(b,e)
//對於順序容器的接受元素數量(及其初始值)的建構函式
C seq(n)
C seq(n,q)
56. 容器賦值運算:
c1 = c2;
c = {a,b,c... }
swap(c1,c2)
c1. swap(c2)
seq. assign(b,e)
seq. assign(il)
seq. assign(n,t)
PS:
1 ) 用assign可實現型別的自動轉化,比如char* to string ,而賦值運算子則不能
2 ) 出string 外,指向容器的指標或者引用,在該容器與其它容器交換值後並不會失效,但是指向不再是以前的元素。而string 在交換之後,相關指標和引用會失效
3 ) array 的交換是值的交換而非資料結構的交換,時間花費並非常數級
57. 向順序容器新增元素的操作:
c.push_back(t)
c.emplace_back(args)
c.push_front(t)
c.emplace_front(args)
c.insert(p,t)在迭代器p所指元素之前建立一個值為t或者由args建立的元素,返回新元素的迭代器
c.emplace(p,args)
c.insert(p,n,t)
c.insert(p,b,e)
c.insert(p,il)
PS:
1 ) forward_list有專有版本的insert和emplace,它不支援push_back emplace_back
2 ) vector string 不支援push_front emplace_front
3 ) 向一個vector string deque 插入元素會使所有指向容器的迭代器,指標,引用失效
58. 關於insert和emplace:
1 )deque 提供了vector 一樣的隨機訪問能力,而且有vector 不支援的push_front,deque 在容器首尾插入或刪除都只花費常數時間。vector 雖然不支援push_front但是可insert到首元素之前(很耗時)。
2 )插入範圍內元素,迭代器範圍和插入位置不能指向同一個容器。
3 )insert的返回值是新插入元素的迭代器,因此可以利用這一點反覆在同一位置插入元素。
4 )emplace與insert的區別在於,emplace(args)構造出元素並插入。
59. 利用at 進行隨機訪問,在越界的時候會丟擲一個out_of_range異常。
60. 容器操作可能使迭代器失效:
1 ) 向容器新增元素:
a. 對於vector 和string ,如果插入元素後儲存空間重新分配,則全部迭代器、引用、指標失效;如果未重新分配,則插入元素之後的迭代器、引用、指標失效。
b. 對於deque 插入到首尾位置之外的位置,迭代器指標引用全部失效,如果是在首尾位置插入,僅僅使迭代器失效。
c.對於list 和forward_list,沒有影響
2 ) 刪除元素後
a. 對於list 和forward_list,沒有影響
b. deque 在首尾之外的位置刪除元素,全部失效;刪除首元素,沒影響;刪除尾元素,僅僅是尾後迭代器失效。
c. 對於vector 和string ,被刪除元素之後的全部失效。
3 ) 根據不同容器的儲存方式,結合迭代器之間的關聯性,指標和引用的獨立性,以上結論很好理解。 由於以上情況的存在,在改變容器之後緊接著最好更新迭代器,以免出錯;因為end返回的迭代器總是會失效,所以最後不要用變數儲存它,而是在需要使用的時候重新執行end.()。
61. 泛型演算法的一個關鍵概念:它不依賴於容器型別,僅僅執行在迭代器上。
62. 兩種迭代器引數:
1) 第一個和第二個參數列示第一個序列的迭代器範圍,第三個參數列示第二個序列的首元素迭代器,這種需要程式設計師保證第二個序列的長度不小於第一個序列。
2)四個引數按順序構成兩對迭代器範圍。
63. 插入迭代器:
vector <int > vec;
auto it = back_insert(vec);
*it = 42 ;
64. 謂詞:
謂詞是一個可呼叫表示式,其返回結果是一個能用作條件的值。標準庫使用的謂詞分為兩類:一元謂詞和二元謂詞。
65. 可呼叫物件 lambda表示式:
1 )它可以理解為一個未命名的行內函數。
2 )形式: [capture list](parameter list) -> return type {function body}
3 )普通函式不能在另一個函式內部定義,而lambda卻可以,這就是其特別之處,它的捕獲列表捕獲的是它所在函式的區域性變數。
4 )值捕獲:在lambda建立時就進行捕獲值的拷貝。區域性變數的值發生變化不會影響所捕獲的值。引用捕獲:在捕獲列表的成員前加&即構成引用捕獲,其值會隨區域性變數的變化而變化。
5 ) 隱式捕獲:在方括號裡寫上& 或 = 表示使用隱式捕獲,編譯器會自動推斷,&表示引用捕獲,=表示值捕獲。
6 ) 混合捕獲:...p352。
7 ) 可變lambda:對於值捕獲,在函式體左花括號前加上mutable,表示lambda會改變捕獲的值。
8 ) 預設情況下,如果一個lambda函式體內部還包含了除return 之外的語句,在編譯器假設其返回void 。
66. 引數繫結:
1 )明確概念:對於多次使用的lambda,可以用一個函式代替它,演算法中的迭代器數目不一定和函式的引數數目一致,這樣就無法傳遞,這樣就需要引數繫結。
2 )bind函式定義在標頭檔案functional中,他接受一個可呼叫物件及其引數,返回一個新的可呼叫物件。
3 )一般形式: auto newCallable = bind(callable,arg_list)
4 )arg_list中可能包含形如 _n的名字,n是一個整數,_n是newCallable的引數。
5 ) 兩種using 宣告:
a. using std ::placeholders::_1;
b. using std ::placeholders;
6 ) bind實際上可以看做兩個函式之間的對映,這兩個函式的引數列表常常有區別,也有關聯。
7 ) bind的arg_list中,非_n形式的引數實際上實現了lambda捕獲列表的功能,採用拷貝的方式傳遞這些引數。但是有些區域性變數不能被拷貝,比如ostream,這個時候就需要用ref()生成物件的引用,而引用是可以被拷貝的。
67.再探迭代器:
1)輸入流迭代器一方面可以方便地直接從輸入資料構造容器,一方面可以是輸入資料直接運用於泛型演算法。
2)輸出流迭代器可以方便的進行輸出而不用寫迴圈,並且解引用和自增運算子對於輸出流迭代器可有可無。
3)可以為任何定義了輸入運算子>>的類建立istream_iterator物件,也能為任何定義了輸出運算子的類建立ostream_iterator物件。
4)為了保持相同的左閉右開區間的關係,返向迭代器和對應的普通迭代器並不是指向相同元素,而是指向相鄰的元素。
5) 插入迭代器常常用做泛型演算法結構中的單個目標迭代器,輸出流迭代器也可以做此用途。
68. map multimap set multiset 都是有序關聯容器,在定義的時候需給出比較元素大小的謂詞,儲存的時候據此按順序儲存。
multiset <Sales_data,decltype (CompareIsbn)*>
bookstore(CompareIsbn)
69. 1 ) 靜態記憶體:儲存static 物件,定義在任何函式之外的變數。
2 ) 棧記憶體:儲存定義在函式內部的非static 物件。
3 ) 堆(動態分配的記憶體):也被稱為記憶體池,自由空間,它用來儲存程式中動態分配的物件。
70. 智慧指標:
1 )與emplace類似,make_shared函式利用引數來構造給定型別的物件。如:make_shared<string >(5 ,'9' );
2 )用內建指標初始化智慧指標的時候,必須用直接初始化,因為智慧指標的建構函式是explicit 的。p412
3 )預設情況下,用來初始化智慧指標的內建指標必須指向動態分配的記憶體,因為智慧指標會自動釋放它們。但是有些時候我們也可以用一個不是指向動態記憶體的內建指標來初始化智慧指標,這種時候要求我們自定義可呼叫物件來代替delete。
4 )引用計數僅僅侷限在智慧指標的自身的拷貝上,當孤獨的建立兩個指向同一塊記憶體的智慧指標,它們的計數會獨立進行,不會疊加,這常常出現問題。
5 )如果使用get ()返回的指標,那麼在最後一個智慧指標銷燬後,該指標失效。
6 )一個unique_ptr“擁有”它所指的物件,它只能用new 初始化,不支援拷貝和賦值,但是可以通過release和reset轉移控制權。不能拷貝的特性有一個例外:我們可以拷貝或賦值一個將要被銷燬的unique_ptr,比如返回一個unique_ptr。
7 )智慧指標和普通指標的一個重要區別:當指向一個陣列或向量時,智慧指標指向“整個陣列或向量”,而普通指標指向其中一個元素。智慧指標的解引用就相當於向量名字,而普通指標的解引用相當於向量元素。
8 ) new 的一個侷限表現在它將記憶體分配和構造結合在一起,這除了會導致一些浪費,還會使沒有預設建構函式的類無法使用new 。
71. 拷貝建構函式:
1) 如果一個建構函式第一個引數是自身類型別的引用(必須是引用p442),且其他引數都有預設值,則其為拷貝建構函式。
2) 如果沒有定義拷貝建構函式,編譯器會為我們合成一個。
72. 拷貝建構函式、拷貝賦值運算子、解構函式具有相同的基本形式,它們三者應該儘量同時地顯式定義,p474闡明瞭原因。具體來說:
1) 需要析構的類幾乎可以肯定需要另外兩者。
2) 需要拷貝操作的類也需要賦值操作,反之亦然。
3) 當類中有成員是無法拷貝或者是不能拷貝的時候,合成拷貝控制成員會被定義成刪除的。p477
73. 類的拷貝語義:
1 ) 這是指讓類物件看起來像一個值(拷貝過後相互獨立)或一個指標(拷貝過後還有關聯,比如指標和它的副本指向同一記憶體)
2 ) 行為像值的類的拷貝:對於指標的處理,應該用new 分配新的記憶體地址來使副本和元指標獨立。
3 ) 行為像指標的類的拷貝:與2 )不同,指標直接拷貝,並利用一個指標成員作為計數器,指向動態記憶體,記憶體中的值就是計數值,在拷貝建構函式的函式體內遞增該值。還要在解構函式體內遞減該計數值。486
74. 使用移動而非拷貝的原因:
1) 拷貝後原物件會立即被銷燬,浪費。
2) IO類、unique_ptr等不能拷貝的物件。
75. 左值引用和右值引用:
1) 常引用(左值引用)可以繫結到右值。事實上:常量左值引用可以繫結到所有型別的值,包括非常量左值、常量左值、非常量右值和常量右值。
2) 返回左值引用的函式,賦值、下標、解引用、前置遞增遞減都返回右值。返回非引用型別的函式、算術、關係、位、及後置遞增遞減都返回左值。
3) 對一個移後源物件,可以賦值和銷燬,但是不能使用。
4) 右值是可以被安全地移動走的,因為通常,右值在起到作用後會立馬被丟棄,不會被繼續利用,所以把它移動走是安全的。
76. 移動建構函式:
1 )移動操作不應該丟擲任何異常(因為不分配資源)深入原因p474
2 )在移動後,要將移後源物件置於可析構狀態。比如用nullptr
3 )當一個類沒有定義任何版本的拷貝控制成員,且每一個非static 成員都可以移動時,編譯器會合成移動建構函式和移動賦值運算子。
4 )個人理解:很多類都會有指標成員,指向為這個類分配的一些記憶體。對於拷貝構造,會將這些記憶體的內容拷貝到另一個類中,而移動構造僅僅是拷貝指標,而將原來類中的指標析構。
5 ) 個人理解:其實在類的編寫過程中,左值引用也可以手動的進行指標的拷貝(所有權的交換);但這樣在函式過載的層面上無法和拷貝建構函式區分開來,所以引入了專門的右值引用。
6 ) 由於內建型別可以直接移動,而類型別有自己的移動建構函式,所以編譯器可以合成移動建構函式。
7 ) 用拷貝建構函式代替移動建構函式幾乎可以肯定是安全的,因為前者不會是移後源物件改變,所以在沒有移動建構函式是,傳入右值引用還是會用拷貝構造。
8 ) 當賦值運算子的引數為非引用型別時,會根據傳入的是左值還是右值自動執行拷貝或者移動操作,實現了對兩種功能的統一。
9 ) 右值引用不僅可以用於建構函式,也可以用於成員函式。(左值不能原址排序?p484)
77. 運算子過載:
1) 當把運算子定義為成員函式的時候,左側運算物件必須是該類的一個物件。
78. 繼承(部分知識):
1)動態繫結針對於虛擬函式,根據傳入引數的不同,選擇函式版本。虛擬函式的解析過程發生在執行時。
2)可以將派生類物件認為是由基類部分和派生類部分組成。
3)靜態型別:物件在宣告時採用的型別。是在編譯期確定的。
4)動態型別:目前所指物件的型別。是在執行期決定的。
5)如果表示式既不是指標也不是引用,則它的靜態型別和動態型別永遠一樣。
79.虛擬函式:
1)為什麼叫虛擬函式?因為它有不確定性,在通過引用或指標呼叫的時候,只有在執行時才能確定呼叫版本。
2)一個函式一旦被定義為虛擬函式,以後的派生類中它也一直是虛擬函式。
3)
. + +