C++
1 語法&特性
返回值-NRV
有一條語句,A a = f()
;其中 f()是一個函式,函式里邊申請了一個 A 的物件 b,然後把物件 b 返回。在物件返回的時候,一般情況下要呼叫複製函式,把函式 f()裡邊的區域性物件 b 複製到函式外部的物件 a。但
NRV:具名返回值最佳化(Named Return Value)簡單的說就是,如果用了 NRV 最佳化,那就不必要呼叫複製建構函式,編譯器可以這樣做,把 a 的地址傳遞進函式 f(),然後不讓 f()申請要返回的物件 b 的空間,用 a 的地址來代替 b 的地址,這樣當要返回物件 b 的時候,就不必要複製了,因為 b 就是 a,省去了區域性變數 b,省去了複製的過程。
指標傳參
指標作為引數傳遞,也是傳值,即複製一份指標。這同樣意味著你在函式中對指標的操作,並不會真正改變原指標。這裡要注意理解:不改變的,是指標指向哪裡,但是你卻可以透過這個指標,真實的改變原來指標指向的內容。
如果你想修改一個值,你可以傳遞指向它的指標(或引用)。因此如果你想修改指標的值(即指標的指向),那麼你就要傳遞指向指標的指標(或指標的引用)。
但是要注意,如果你修改了指標的指向,那麼原來指標的指向也會變,這就意味著,原來指標指向的那一塊記憶體區域可能無法引用了,那就會造成記憶體洩漏。
類
(1) 語法
- 一些“關鍵字”:
inline
(外部函式變為內聯)、const
(不修改)、default
()、explict
(不隱式型別轉換)、virtual
(虛擬函式、虛繼承)、static
(類成員/靜態成員)、using
(可以在基類中隱藏父類的 public 繼承來的東西) - 初始化列表:建構函式後以
:
開始,進行初始化。- 靜態成員只能在類內定義,類外初始化,不能出現在初始化列表中(因為一個類只含有一份?)
- 基類的建構函式,也在初始化列表中呼叫
D(int a, int b, int c) : B(a, b), _c(c)
(如果基類沒有預設建構函式,那子類必須顯示呼叫) - 初始化順序只看宣告順序(以及建構函式呼叫順序),與初始化列表出現順序無關。(注意區別,跟出現次序有關的是多繼承時,建構函式的呼叫順序)
- 子類只關心父類的初始化,不管再往上幾輩的事,爺爺類自有父類負責(虛基類除外:子類還要呼叫虛基類的建構函式來初始化虛基類子物件,每個低層次的類都要寫,但是實際上只有最派生類執行)【最派生類】:建立物件時所指定的類
- 構造與解構函式
- 建構函式呼叫順序:虛基類 \(\Rarr\) 父類(按宣告左到右) \(\Rarr\)客人(類型別資料,按定義前到後)\(\Rarr\) 自己。析構順序與之相反
- 不管顯示還是隱式,基類總是要呼叫父類的建構函式,以及解構函式的。因此可以用子類的指標來析構父類,但父類的指標不會析構子類
- 型別適應:當且僅當
- 繼承不會繼承基類的構造和解構函式;
- 虛繼承:虛繼承即讓某個類做出宣告,承諾願意共享它的基類。其中,這個被共享的基類就稱為虛基類。目的是解決菱形繼承問題\(\Rarr\)同名虛基類,在物件中只會產生一份虛基類子物件(全部父類加上子類,共享一個虛基類子物件)
(2) 記憶體佈局
- 成員函式在程式碼區,不佔有物件的記憶體。成員函式中定義的區域性變數是“共享的”
- 有虛擬函式的,物件首塊記憶體會存放一個指向虛表的指標。可以
(void **)(*(void**)&obj)
- 有繼承的,子類中先出現的是父類中的資料成員(基類子物件),然後緊跟著是自己的資料成員
- 如果父類中有虛擬函式,子類也會有一個虛表,如果沒有重寫父類的虛擬函式,那虛表中專案就指向父類的虛擬函式,如果重寫了,那就指向子類的虛擬函式
(3) 常見問題
- 複製建構函式引數:
- 深淺複製:
- 虛建構函式與虛解構函式:
關鍵字和上下文關鍵字
// delete
void func(int)=delete;
// override, final
void f() override final;
類别範本與友元函式
參考這裡
// 模板類中宣告友元函式共有四種:
template <typename T>
class A{
public:
friend void f1(); // 1.不需要模板引數的,非模板函式
friend void f2(A<T>& a); // 2.需要模板引數的,非模板函式
friend void f3<T>(A<T>& a); // 3.需要模板引數的,模板函式
template <typename U> // 4.需要模板引數且自帶引數的模板函式
friend void f4(A<U>& a);
private:
T v;
};
不要在類內宣告的是第二種,在類外定義的卻是第三種了。
智慧指標
Lambda 表示式
auto foo = [capture](parameters)->return_type {func_body}
-
[capture] 用來捕獲表示式外部的變數:
-
[ = ] :以值(複製)的方式捕獲所有外部變數,函式體內可以訪問,但是不能修改。
-
[ & ] :以引用的方式捕獲所有外部變數,函式體內可以訪問並修改(需要當心無效的引用);
-
[ var ] :以值(複製)的方式捕獲某個外部變數,函式體可以訪問但不能修改。
-
[ &var ] :以引用的方式獲取某個外部變數,函式體可以訪問並修改
-
[ this ] :捕獲 this 指標,可以訪問類的成員變數和函式
-
[ =,&var ] :引用捕獲變數 var,其他外部變數使用值捕獲。
-
[ &,var ] :只捕獲變數 var,其他外部變數使用引用捕獲。
-
(parameters):即自定義函式里面的引數,只能在表示式的封閉範圍內用
-
返回值可以自動推斷
2 庫與函式速查
⭐getline()
-
有兩個 getline()函式,一個在<istream>裡面,透過(輸入流)成員呼叫的方式指出輸入流,一個在<string>裡面,為頂層函式,在引數列表中指出輸入流
-
getline()實際讀取的為(n - 1)個字元,且會把終止符從流中刪除
-
讀到檔案尾返回eofbit
// # include<istream> // 注意是char*,同時要指出讀入的size
istream& getline (char* s, streamsize n, char delim );
// # include<string>
istream& getline (istream& is, string& str, char delim);
⭐get()
-
與 getline()區別在於,讀到 delim 字元後,會中止並將其保留在流中,這就會被下一個使用者讀到
-
只有<istream>版本
// 依次為:single character (1),c-string (2),stream buffer (3)
int get(); istream& get (char& c);
istream& get (char* s, streamsize n, char delim);
istream& get (streambuf& sb, char delim);
正規表示式
<regex>庫是 C11 中新增的特性,下面給出一些使用的例子
regex
:定義一個正規表示式類,如regex rx("^[0-9]");
match_result
:
3 標準庫 STL
⭐黑馬 ⭐STL 底層資料結構總結
陣列
(1) vector
🌟vector 小指南(附帶一些新手錯誤)
- vector 的大小。裡面似乎存放的只是幾個指標而已,start、fin 等。在使用
umap
時候發現的
string 類
⭐string 的常用操作
棧與佇列
(1) priority_queue
⭐cpp-reference ⭐cplusplus
template<
typename T, // 型別
typename Container = std::vector<T>,
typename Compare = std::less<typename Container::value_type>
> class priority_queue;
優先佇列的底層實現是二叉堆,插入時,讓使 Compare 返回 true 的元素下沉(sink)。
使用時容易模糊的就在於,Compare
怎麼定義(討論的是自定義型別,而非 POD)。一般的策略有:在型別中過載 <
運算子,則即可預設 compare,或定義仿函式
//(1) Comp預設,自定義型別中,過載或友元過載小於 < 運算子
struct ListNode {
bool operator< (const ListNode&){}
friend operator< (const ListNode&, const ListNode&){}
}
//(2) Comp為仿函式(即定義一個類Comp,然後過載它的括號運算子)
Class Comp{
bool operator()(const T&, const T&){}}
常用的操作有push(), pop(), top(), empty()
集合 與 對映
(1) set
⭐cpp-reference ⭐[set 部落格總結](c++ stl 庫中的 set - 然終酒肆 - 部落格園 (cnblogs.com))
template<
typename Key,
typename Compare = std::less<Key>,
typename Allocator = std::allocator<Key>
> class multiset;
注意四個版本:set
, multiset
, unordered_set
, unordered_multiset
預設有序是升序,(只需使用 rbegin()
和rend()
就能的到逆序的)
常用操作:insert、find、count、clear、empty
(2) map
⭐map 用法詳解
預設按關鍵字升序,STL 中的有序均是升序,因此需要過載的只是小於號 \(<\)
連結串列
(1) list
template<
typename T,
typename Allocator = std::allocator<T>
> class list;
雙向連結串列
front、back、push_back、pop_front、insert、erase、remove_if、sort、unique、reverse
(2) forward_list
單項鍊表