c++ primer version-5 的整理
section 1:
內置類型和自定義類型;
main函數的返回值:指示狀態。0:成功;1:系統定義。
unix和win系統中,執行完程序可以使用echo命令得到返回值。
編譯器不同?
iostream有四個IO對象,cin,cout 和 cerr clog (可以往流中寫入數據)
輸出運算符 << 的左側必須是ostream對象,右側為要打印的值,如果不是內置的類型,則需要運算符的重載(友元、成員函數,寫法?);endl 除了結束該行,還可以刷新流,將buffer內容刷到設備;
命名空間,用於避免名字定義的沖突
當cin>>object作為循環判定條件時,效果是檢測流的狀態。有效:未遇到錯誤;無效:遇到無效輸入 or 遇到文件結束符(eof:win使用ctrl z,unix 用 ctrl D)
edit-confile-debug 逐個修正bug
頭文件的引用:如果是標准庫,用<>,如果不是,使用"xx.h "
文件重定向:命令行 exefile_name <infile>outfile
----------------------------------------
section 2:變量與基本類型
int long 4B 以及long long 8B 的長度;當運算同時有帶符號和不帶符號的數,會轉化為不帶符號
可尋址最小單元 字節;存儲基本單元 字(看計算機是64bit的)
0開頭8進制;0×開頭16進制
浮點數,指數部分用e表示,默認字面值是double
轉義:常用的有 換行 \n
建議初始化每個內置類型變量
分離式編譯——聲明和定義;使用 extern 表示不定義,不初始化變量
命名規范:自定義類名大寫開頭,變量名小寫
引用(這里指左值引用)必須初始化,需要且只能綁定在對象上,引用即別名
- 指針和引用都實現了間接訪問。區別:指針本身就是對象,無需定義賦初值;允許對指針賦值和拷貝,可以指向不同的對象
空指針 NULL(相當於用0初始化指針) or nullptr(新標准,特殊類型字面值,推薦使用)
void* 指針:存放任意類型對象的地址,只能拿來作比較,or 賦值給另外的void×指針。不能操作該類指針的對象,因為只表示內存空間
const 對象必須初始化,如果想在多個文件間共享const對象,需要在定義前加extern
如果引用指向的是常量,則應該申明為常量引用 ;如果不是,那么對於它的常量引用不能通過引用來修改它的值,只能通過它本身修改,就是說const int & num 和 int const & num(不能修改引用指向的值)不同
常量指針:頂層const(常量指針):int * const p,表示指針本身是常量,不能修改指針指向另外的對象;底層const(指向常量的指針),修飾的是*p,不能修改指向的值*p,可以換對象
aoto會忽略掉頂層const的特性
decltype 類型指示符,decltype(a) r,將r的類型設為a的類型,
constexpr(c++11) 常量表達式 會檢查是不是常量表達式,在編譯時會計算
typedef or using(新標准) 別名
頭文件不能被定義兩次——頭文件保護符——#ifndef #define #endif
----------------------------------------------
section 3:字符串、向量、數組
頭文件不用using namespace的聲明
string s(n,'c') 結果為n個c的串;初始化使用等號的是拷貝初始化
字符串能直接比較,getline(is,s)從is中讀取一行給s
empty函數和size函數;size函數返回size_type類型變量,是無符號整型數,注意unsigned 數和整型數比較和運算
處理字符:cctype中包含了很多比較函數,例如isalpha(c)如果是字母時為真,tolower(c)如果c是大寫字母,輸出對應小寫字母,否則原輸出
vector就是個類模板,創建時需要實例化,初始化方式;使用范圍for時,vector的遍歷序列大小不能發生改變;
迭代器的目的是通用,所有stl容器都可以使用;可以用auto變量指向 容器的begin or end返回值
字符數組 注意要留1個單位來保存空字符\0
數組也可以使用begin和end函數得到指針,end(a)表示a中最后元素的下一位置的指針
指針運算得到的類型是 ptrdiff_t 定義在cstddef頭文件與機器相關的類型,是帶符號的
strlen strcmp strcat strcpy 都是c風格的函數,c風格使用字符數組表示字符串,這在c++中也可以操作,且很多可以兼容運算,但不推薦
-----------------------------------
section 4:表達式
邏輯與和或的短路求值
少用++的后置版本,但是在*iter++形式廣泛使用
sizeof 返回size_t類型
隱式類型轉換 和 顯式類型轉換:static_cast(除了底層const外的從大類型到小類型的轉換,也可以找回void×) const_cast(底層const去掉const性質) reinterpret_cast(重新解釋,很容易出錯) dynamic_cast
---------------------------------------
section 5:語句
switch() case : default:
范圍for語句,直接使用auto,如果還需要對變量更改,必須是auto &i
如果拋出了異常,但是沒有catch,那么最終會轉到terminate的標准庫函數去處理,一般是程序非正常退出
標准異常:定義在4個頭文件中:exception stdexcept
---------------------------------
section 6:函數
傳參:傳值(指針也是拷貝),傳引用(避免拷貝,與實參綁定,c++推薦使用);引用形參還可以返回額外信息,如果不想改變,則可以加const
頂層const傳值時會忽略掉,所以盡量使用常量引用
數組不能拷貝,所以只能傳指針或者引用。由於指針不知道數組的大小,所以,通過標記或者使用前尾指針or傳遞一個數組大小來解決
數組的引用 表示為 int (&arr) [10] 必須加括號,同樣指針也是 需要加括號,不然就是10個指針構成的數組
main函數可以傳參,argc,argv【】;其中argv[0] 表示程序名
如果實參的個數不知道,且屬於同一類型,可以使用 initializer_list<T> il 傳參,就相當於vector一樣,是一個靈活的模板類,支持迭代器,不過內部的元素是const類型
省略符形參 僅僅可用於通用的類型
不要返回局部對象的引用或指針
返回值可以是vector,直接return {元素1,元素2....}
返回數組指針同樣需要加括號:int (*func(int i) ) [10] ,或者使用c++11 尾置返回類型: aoto func(int i) -> int (*) [10]
重載對於頂層const會忽略
auto 和 decltype對於指針和引用的表示還是需要加 × 和 &的
重載與作用域:c++名字查找發生在類型檢查前,當前作用域內有匹配的名字時,會忽略外層的同名實體
默認實參:申明時直接用=號,省略的只能省略尾部
內聯inline 減少調用開銷,優化規模小的,流程直接的,頻繁調用的函數,尤其在循環體內;不用於內聯遞歸函數
除了內聯,constexpr可以用於常量表達式,兩者通常放在頭文件內定義
調試幫助:assert預處理宏 NDEBUG預處理變量
函數指針:int (*pt) (int a,int b) ,則pt就是函數的指針,括號不能少;將函數名作為一個值,它將轉化為函數的指針
--------------------------
section 7:類
this是一個常量指針,不能改變指向
構造函數初始化列表用:表示,初始化與賦值不同,如果構造的時候,有const或者引用對象,他們必須使用初始化列表,而不能通過賦值(先初始化后賦值)構造;初始化順序與定義的先后順序有關
構造函數可以委托,委托使用的函數放在初始化列表中
如果有動態分配的內存,則慎用默認合成的拷貝賦值和析構函數,使用string vector這種問題會少很多
類 使用class 則默認所有成員是私有的,使用struct,默認是公有的
explicit 禁止單實參的構造函數實施隱式轉換,放在聲明處,可以顯式轉換使用
聚合類:所有成員是public,沒有定義構造函數,沒有初始值,沒有基類虛函數
字面值常量類,constexpr構造函數:函數體一般為空,或者只有return語句
static成員函數不能包含和使用this指針,在外部定義靜態函數時,不用寫static,只出現在聲明中
非靜態數據成員不能作為默認的實參,而靜態數據成員可以
------------------------------
section 8:IO庫
IO對象無拷貝或賦值
條件狀態:s.eof(); s.fail(); s.good(); s.clear(flags);常用的
緩沖區刷新
打開文件 fstrm.open(s);創建打開: fstream fstrm(s);
文件模式:in out app(每次寫操作前定位到文件末尾) trunc截斷文件
strm.str() 函數返回strm保存的string的拷貝
------------------------
section 9:順序容器
list雙向鏈表,forward_list單向鏈表,array(固定大小數組);后兩個是c++11新加的
容器支持的函數:swap以及<=這些運算符,反向容器reverse_iterator rbegin
迭代器 解引用 end其實是最后一個元素后面的位置;容器初始化,可以使用兩個迭代器來拷貝
array定義:array<int,10> arrayname=array1;該式子合法,只要array1也是<int ,10>
順序容器(除了array)外定義了assign,有兩種形式,一種是用迭代器,另外一種使用大小-元素
swap函數很快,因為並沒有元素本身的交換;只有array的swap才會真正交換;推薦使用非成員函數版本的swap
容器的插入:push_back, emplace_back,push_front(forward_list也支持), insert 多種形式,可用迭代器,或者大小指定范圍和元素;emplace的操作是構造元素,而不是像push和insert一樣拷貝元素
刪除:pop_back pop_front erase
forward_list有它特殊的插入 insert_after 和刪除 erase_after 以及返回head前不存在的元素的迭代器 before_begin() , cbefore_begin(),操作的數都是迭代器后的數
list與forward_list支持特定算法:lst.remove(val), lst.reverse(), 還有merge sort unique 算法
resize(n,t)函數:可能會使之前的迭代器失效;當刪除元素時,尾后迭代器肯定失效
容器會有capacity,表示不分配新內存在的容量
額外的string操作:構造函數:s(s1,pos) s(s1,pos,len) 從s1的pos位置開始拷貝len個字符到s 或者使用substr(pos,n)函數,n也可以省略;除了insert和erase外還支持 append(args) 和replace(range,args)
string搜索:find,rfind,find_first_of,find_last_of,find_first_not_of 函數,如果沒找到會返回npos靜態成員,是個很大的數
string與其他數值的轉化:to_string(val), stoi(s,p,d) p是位置,d是s表示的進制 stod(s,p)
容器適配器:stack和queue(依靠deque實現,默認),priority_queue(默認vector實現):都可以基於某個容器來直接構造
stack:pop(),push(item),emplace(args),top();
queue:pop, push, emplace, top, back, front
--------------------------------
section 10:泛型算法
迭代器令算法不依賴於容器,算法不改變容器大小,都對一個range進行操作
fill(pos1,pos2,num) 將range內的元素變為num
back_inserter(容器) 插入迭代器 還有front_inserter
sort(pos1,pos2) unique(pos1,pos2)返回不重復數的end迭代器位置;stable_sort()
lambda表達式(適用於在很少地方出現的簡單操作,適合有捕獲變量場景),可調用的對象,[capture list] (參數) -> 返回值類型 { } ;可以用auto f=[ ]...來表示一個lambda,捕獲的是局部變量,可以值=或者引用&捕獲。lambda可以方便寫在函數的實參里面,就像函數指針;
如果是通過值捕獲的,但是加了mutable,那么局部變量的值也是會最終改變的,和不是const &的引用類似
如果不使用lambda,使用函數,但是要捕獲局部變量。可使用STL中的auto f2=bind(f,_1,_2,...)
迭代器:插入,反向,流,移動迭代器
find_if(beg,end,pred)查找第一個令pred為真的元素
--------------------------------
section 11:關聯容器
map set的multi-和unordered形式
pair p.first p.second
set中的關鍵字是只讀的,迭代器是const,所以用cbegin
插入:insert與emplace函數 m.insert({key,val}) 返回的是一個pair(first,second),first指代的是迭代器,second指代是否插入成功,如果重復沒插入則為false
刪除:c.erase(元素),元素可以是單/雙迭代器,或者是key
map可以進行下標訪問,c.[key]
查找:find(key)返回迭代器,如果沒有則返回end()位置 ; count(key)返回出現的次數;lower_bound(k),upper_bound(k), equal_range(k)返回的是等於k元素的范圍,是pair類型
示例程序:文件單詞轉換
#include<iostream> #include<fstream> #include<sstream> #include <map> #include<algorithm> using namespace std; void transform(ifstream &map_file, ifstream &input, ofstream &out){ map<string,string> trans; string key,val,text; while(map_file>>key&&getline(map_file,val)){ if(val.size()>1) trans.insert({key,val.substr(1)}); //skip the ' ' before the val else trans.insert({key," "}); } while(getline(input,text)){ stringstream ss(text); string word; bool flag=true; while(ss>>word){ if(flag) flag=false; else out << " "; map<string,string>::iterator map_it=trans.find(word); if(map_it!=trans.end()) out<<map_it->second; else out<<word; } out<<endl; } } int main(){ ifstream input("input.txt"); ofstream out("out.txt"); ifstream map_file("map.txt"); transform(map_file,input, out); return 0; }
無序容器的輸出可以使用范圍for語句 const auto &it: unordered_map;該容器在存儲組織上類似於桶,依靠哈希函數
-----------------------------------------
section 12:動態內存
靜態內存:static變量和全局變量;棧內存:局部變量; 兩者都由編譯器自動創建和銷毀
堆:每個程序有一塊自由空間,動態分配的對象
智能指針(定義在頭文件memory中):負責自動釋放所指向的對象:shared_ptr, unique_ptr; weak_ptr弱引用,指向sharedptr
shared_ptr獨有操作:auto p=make_shared<T>(), p.unique(), p.use_count()
原理:shared_ptr的析構函數會遞減引用計數,如果為0則會銷毀指向對象,釋放內存
引用計數增加情況:初始化另外的sharedptr or 作為參數傳遞給函數 or 作為函數返回值
減小情況:被賦予了新值 or 被銷毀
為什么使用動態資源:(1)程序不知道要使用多少對象;(2)不知道所需對象的准確類型;(3)需要在多個對象間共享數據(常見需求)
內存耗盡會拋出bad_alloc異常,動態內存不能不釋放。也不能多次釋放
p.reset(new xxx)操作,使p指向另外的對象
使用智能指針管理資源,可以傳遞給它一個刪除器,用於指定析構,可以是函數指針,也可以是可調用的lambda
unique_ptr是不支持拷貝的和賦值的,但是也可以reset或者置空;
weak_ptr是弱共享,不會改變計數值,使用lock()函數來返回一個指向相同對象的shareedptr
allocator類,在memory下,分配未構造的內存,將內存分配和對象構造分離,這樣就可以按需構造了
-------------------------------
section 13:拷貝控制
拷貝構造函數的第一個參數必須是引用類型
拷貝賦值運算符: Foo & operator=(const Foo&) 最后返回一個*this
析構的順序與構造的順序相反,先函數體,再成員,成員也是逆序的
什么時候調用析構:變量離開作用域;對象被銷毀;容器被銷毀;delete 某動態生成對象的指針;臨時對象,創建它的完整表達式結束時
析構函數體自身不直接銷毀成員,成員是在析構函數體之后隱含的析構階段被銷毀
法則:(1)需要自定義析構,則也需要拷貝和賦值操作;(2)需要拷貝,則也需要賦值,反之亦然,而析構不一定需要
可以使用默認的合成函數,=default;可以阻止拷貝或者賦值,=delete
賦值函數一定要注意將自己賦值給自己 ,能夠正常操作
引用計數是作為一個指針變量出現在類的成員中的,指向真正的引用計數值
右值引用&&:變量是左值,只有const的左值引用 or 右值引用可以引用右值
移動move(頭文件utility):比拷貝高效,相當於竊取資源:int &&rr2=std::move(rr1); rr1將會不使用,被銷毀
為什么要移動:拷貝資源有額外的開銷,在這種拷貝不是必需的情況下,可以定義移動構造函數和移動復制運算符的類來避免
noexcept:告知標准庫不拋出異常
移動迭代器:解引用返回的是右值引用
引用限定符& or &&
-----------------
section 14:重載和類型轉換
非成員函數的運算符重載:通常在形參上包含不止一個對象;成員函數的重載:一般IO運算符不使用成員函數重載,使用非成員或者友元
ostream &operator<<(ostream &os, const Sals &item)
一些算數或者關系運算符的重載,通常使用非成員,形參使用常量的引用
下標運算符[] 返回的是普通引用或者是常量引用
前后置的++使用成員函數來重載
除了函數指針,lambda外,調用還可以使用函數對象類,重載()
類型轉換運算符
賦值,下標,函數調用和箭頭運算符必須作為類的成員
-----------------------------
section 15:面向對象程序設計
多態-動態綁定-看傳入的對象是到底是什么類型的,是基類還是派生類對象
虛函數-override-當通過指針或引用調用時,會動態綁定,運行時確定
派生類最好通過調用基類的構造函數來初始化基類中的成員
final 防止繼承
派生類的成員或者友元只能通過派生類對象來訪問基類受保護的成員,不能直接通過基類對象來訪問
友元可以訪問該類的私有部分
名字查找先於類型的檢查
基類和派生類的虛函數的參數類型必須相同
在容器中可以存放基類的指針,可以是智能指針
-----------------------------------
section 16:模板與泛型編程
類型名可以用class 或者 typename表示 都可以
在實例化模板的時候,才生成代碼,才會發現很多類型上的錯誤
模板的頭文件通常包括聲明和定義
類模板 template <typename T> class
using可以來設置模板的別名: using twin pair<T,T> twin<string> str;
靜態變量在模板類中,不同模板實例會有各自獨有的static對象,只在相同類型的模板類中共享
默認模板的實參 可以在typename T 后加 =
普通類中也可以有成員模板
多個文件實例化相同的模板會有開銷,可以使用extern關鍵字標識模板聲明,extern template class Blob<string> 則它會去外邊找已經定義了的該模板定義
引用折疊:右值引用的右值引用會折疊成右值引用
move其實是函數模板,利用引用折疊
可變參數模板:參數包(模板參數包和函數參數包) template <typename T, typename ... Args> ; void foo(const T &t, const Args& ... rest) ; rest是函數包的名字
STL中算法都是函數模板,容器都是類模板
類型轉換:有標准庫定義的類模板,可將給定的模板類型參數轉換為一個相關的類型
---------------------------------------
section 17:標准庫特殊設施
tuple類似於pair(頭文件tuple),可以包含多種不同類型的元素;使用aotu item0=get<0> (item)來訪問其中的成員,比較操作要求兩邊成員個數都要相同
可以使用tuple來返回多個值,tuple可以包含在vector中
<bitset>:處理超過最長整形類型大小的位集合 bitset<n> b(u) ;其中 u是unsigned long long 類型,n是位的大小
也可以從string初始化,但是string 下標最大的數存在bitset的低位(下標為0)
bitset函數:count:置位的數目;test(pos)是否被置位
正則表達式(頭文件regex)
regex r(pattern) ; smatch results保存結果; regex_search(test_str, results, r) 找到1個就結束
隨機數 random頭文件:隨機數引擎類和分布類
defaut_random_engine e, e()
uniform_int_distribution<unsighed> u(0,9) u(e)
u和e可以聲明為靜態,以保證起始點不同。種子的概念,可以以time為種子
cin.get(char ch) 讀入一個字符
流隨機訪問:seek和tell 用於文件流
-------------------------------
section 18:用於大型程序的工具
異常 拋出 catch 棧展開 最終到terminate中處理 終止當前程序
析構函數不要拋出自己不能處理的異常
拋出指針要求在對應的代碼存在的地方,指針所指的對象必須在
重新拋出 就是 空throw ,捕獲所有異常就是catch(...)
異常類:派生有runtime_error, logic_error, 異常類體系,使用我們自己的異常類
命名空間:防止命名污染,通過namespace::來訪問特定的名字(如果有重復)
using聲明: 每次只引入空間內的一個成員,在局部作用域有效;using指示:所有名字可見
using指示多了,則引入的名字也多,名字污染可能嚴重,二義性可能增加;所有有時用聲明比較好
多重繼承中,拷貝構造函數和賦值函數的構造順序都一樣,析構相反
虛繼承:解決基類的多重拷貝,共享的基類子對象為虛基類;構造順序:先由最底層的派生類調用虛基類的構造函數,再進行間接基類的構造,再直接基類的構造,最后自己的構造;析構相反順序
--------------------------
section 19:特殊工具與技術
RTTI運行時類型識別
基類到派生類的轉換 dynamic_cast:指針類型的轉換,如果不成功返回0;引用類型轉換,不成功拋出bad_cast異常
typeid(e) 返回e的類型,頂層const會忽略; 比較兩個typeid可以在運行時識別
enum枚舉:限定作用域與不限定的枚舉,枚舉成員是const的;enum intvalue : unsigned long long 在:后可以加類型
類成員指針:數據成員指針/成員函數指針;
嵌套類:可以使用訪問限定符來控制外界對其成員的訪問權限,與外層類是相互獨立的
union:節約空間的類;可以有多個數據成員,但是在任何時候只有一個數據成員可以有值;默認公有
匿名union不能包含受保護的成員或私有變量,也不能定義成員函數
局部類:局部類的成員收到嚴格限制,只能訪問外層作用域定義的類型名、靜態變量和枚舉成員,不能那個訪問局部變量;外層函數不能訪問局部類中私有成員
不可移植的特性支持:
位域:通常用無符號類型保存一個位域 unsigned int mode:2;表示2位; 指針無法指向位域
volatile限定符:當對象的值可能在程序的控制或檢測之外被改變時,告訴編譯器不應對這樣的對象優化
鏈接指示:使用其它語言的代碼:extern "C " {}