C++基礎學習(C++ Primer——未看完版本)

2020-08-09 22:55:24

C++基礎學習

C++基礎

變數和基本型別

  • 變數

    • 無符號和有符號加減會把有符號轉化爲無符號
    • 無符號只是解釋方式不同,運演算法則遵循二補數法則
  • 字面值常數

    • 0開頭-8進位制

    • 0x | 0X開頭-16進位制

    • 字面值型別

      • 是:算術型別,參照和指針
      • 否:自定義類,IO庫,string
  • 宣告和定義

    • extern-宣告(可以重複):extern int i;

    • int-定義:int i;

      • 數據型別
      • 宣告符* &
  • 參照和指針

    • 參照

      • 參照並非物件,定義參照就無法系結到另外的物件,使用就是存取最初系結的物件
    • 指針

      • 指針可以改變
  • const

    • 常數特徵僅僅在執行改變常數操作時纔會發揮作用,所以利用const物件去初始化變數無關緊要

    • 參照的例外

      • 初始化常數參照允許任意表達式作爲初始值,只要該表達式結果能轉換成參照型別即可

      • 原理

        • double val = 3.14;
          const int& ri = val;
        • const int temp = 3.14;
          const int& ri = temp;
    • 指針和const

      • const int *p-指向常數的指針
      • int* const p-常數指針
    • 頂層和底層

      • 頂層:作用物件本身
      • 底層:作用指向的物件
  • 處理型別

    • 型別別名

      • typedef
      • using
  • decltype

    • int i=42,&r=i,*p=&i;
      decltype®=int&
      decltype(r+0)=int
      decltype(*p)=int&----解除參照指針可以得到指針所指的物件的參照
      decltype((i))=int&

    • 與auto的區別

      • 如果使用參照型別,auto會識別爲其所指物件的型別,decltype則會識別爲參照的型別。
    • 當我們使用decltype作用於某個函數時,它返回函數型別而非指針型別,因此我們需要顯示的加上*已表明我們需要返回指針

  • 前處理器

字串,向量和陣列

string

  • getline

    • 忽略回車符
  • size_type與帶符號數混用會造成歧義

  • +兩側運算物件至少一個爲string

    • 字串字面值並不是string物件

vector

  • 構造和初始化

    • 構造

      • ()
    • 初始化

      • {}
    • 例外:使用花括號但是提供的值不能用來列表初始化,就要考慮用這樣的值構造vector物件

迭代器

  • 不允許相加

陣列與指針

  • int *a[10]; //a陣列含有10個指針整形
    int &a[10]; //錯誤,不存在參照的陣列
    int (*a)[10]; //a是一個指針,指向一個含有10個整數的陣列
    int (&a)[10]; //a是一個參照,參照一個含有10個整數的陣列
    int *(&a)[10]; // a是陣列的參照,陣列含有十個指針
  • 原則:從內到外,從右到左
  • 內建下標運算子所用的索引值是有符號型別

多維陣列

  • 使用auto
  • 使用型別別名
  • 要使用範圍for語句處理多維陣列,除了最內層的回圈外,其他所有回圈的控制變量都應該是參照型別

表達式

基礎

  • 左值

    • 用的是物件身份
  • 右值

    • 用的是物件的值

逗號運算子

  • 結果是右側表達式的值

語句

迭代語句

  • 範圍for

    • 預存了end()的值,所以不能通過範圍for增加容器物件的元素
  • 回圈的區別

    • do while

      • 塊中定義的變數,while不可用
      • do塊先執行,後執行while
    • while和for

      • while和for中定義的變數可以被塊使用

函數

函數基礎

  • 區域性靜態變數

    • 程式終止才被銷燬,在此期間即使物件所在函數結束執行也不會對他有影響

返回型別和return語句

  • 函數能夠處理不同數量的實參

    • initializer_list

      • 元素都爲常數
    • 省略符形參…

函數過載

  • 形參數量或形參型別不同

  • 兩個函數,它們的形參列表一樣但是返回型別不同,則第二個函數宣告錯誤

  • 過載和const形參

    • 擁有頂層const形參無法和另一個沒有頂層const的形參區分開來,所以兩個都出現算是重複宣告
    • 底層const不算
    • 原理:對於過載,編譯器需要根據傳入的參數判斷呼叫哪一個過載,所以需要針對實參有所區別,而非形參
  • 不同作用域中無法過載函數名

  • 匹配

    • 精準匹配
    • const型別轉換:判別方式是判斷實參是否是常數來決定選擇哪個函數。指針型別的形參判別也是類似的。
    • 型別提升:不是完全相同,皆會提升型別,char會提升爲int。
    • 算術型別轉換:該轉換的級別都相同,即3.14是double,轉換成long或者float都是同樣等級,會產生二義性。
    • 類型別轉換

constexpr函數

  • 函數的返回型別及所有形參型別都是字面值型別,而且函數體中必須有且僅有一條return語句

認識類

  • 定義在類內部的函數是隱式的inline函數

  • 參數列表後的const

    • 修改隱式this指針的型別,將其定義爲const T* const this

    • 常數成員函數

      • 常數物件,以及常數物件的參照或指針都只能呼叫常數成員函數

封裝

  • 封裝實現了類的介面和實現的分離,隱藏了類的實現細節,使用者只能接觸到類的介面

友元

  • 特點

    • 只能出現在類定義內部,但類內出現的具體位置不限
    • 友元不是類的成員,不受區域存取控制級別的約束
    • 在友元之外再專門對函數進行一次宣告

特性

  • 可變數據成員

    • mutable型別的數據可在const函數內部修改它的值
  • 名字查詢

    • 名字查詢的順序:由內而外、自上而下
    • 在類中,型別名要特殊處理,最好是定義在類的一開始,並且不能與外層作用域中同名
  • 類定義處理

    • 首先編譯成員的宣告,直到類全部可見是再編譯函數體

型別轉化

  • 抑制建構函式定義的隱式轉化

    • explicit,初始化時該關鍵詞無影響
    • explicit只對一個實參的建構函式有效,需要多個實參的建構函式不能用於執行隱式轉化
    • explicit只允許出現在類內的建構函式宣告處
    • 用於顯示宣告建構函式,同時也抑制拷貝建構函式

類的靜態成員

  • 宣告靜態成員

    • 不與任何物件系結,不包含this指針,所以不能在static函數體內使用this指針
    • 靜態成員函數不能宣告成const

IO庫

IO類

  • IO物件無拷貝或賦值,甚至不能顯式構造初始化

  • 輸出快取

    • 多個輸出操作組合成單一的系統級寫操作,帶來很大的效能提升

    • 緩衝重新整理的原因(寫入到裝置或檔案)

      • 程式結束,return,緩衝重新整理被執行
      • 緩衝區滿
      • endl,flush,ends顯示重新整理
      • unitbuf設定流的內部狀態,清空緩衝區,cerr預設設定unitbuf
      • 關聯輸入輸出流(x.tie(&o))

順序容器

迭代器

  • 公共介面
  • forward_list迭代器不支援遞減運算子(–)

新增元素

  • 特例

    • forward_list有自己的insert和emplace
    • forward_list不支援push_back和emplace_back
    • vector和string不支援push_front和emplace_front
  • insert

    • 返回的迭代器是新插入的元素的迭代器

emplace

  • 會在容器管理的記憶體空間直接建立物件,而呼叫push_back則會建立一個區域性臨時變數,並將其壓入容器中

容器操作可能使迭代器失效

容器適配器

  • 預設

    • stack和queue基於deque實現;
      priority_queue基於vector實現
  • 例子

    • stack<string,vector>str_stack
      在vector上實現的空棧
  • 擴充套件

    • array,forward_list不能用來構造適配器
      因爲所有適配器具有新增,刪除以及存取尾元素
    • stack
      push_back,pop_back,back
      除了上面兩個(下面 下麪不再闡述)
    • queue
      back,push_back,front,push_front
      list和deque都可以,但vector不行
    • priority_queue
      front,push_back,pop_back,隨機存取能力
      vector,deque,不能基於list

泛式演算法

標準庫演算法

  • 對迭代器而不是容器操作,因此演算法不能直接新增或刪除元素,例如unique不能刪除重複元素,而是將重複元素移到後面

只讀演算法

  • accumulate

    • 接受3個參數,前兩個是需要求和的元素範圍,第三個參數是和的初值,同時第三個參數型別決定了函數使用哪個加法運算子和返回值的型別

寫演算法

  • 向目的位置迭代器寫入數據的演算法假定目的位置足夠大,能容納要寫入的元素(位置足夠大代表序列大小,並非可用記憶體大小,即reserve無效,resize纔可以)

lambda

  • 格式:[capture list](parameter list)->return type {function body}
    尾置返回,可以忽略參數列表和返回型別,但永遠保留捕獲列表和函數體
    如果lambda函數體包含任何單一return語句之外的內容,且未指定返回型別,則返回void

  • 捕獲列表

    • 捕獲列表只用於區域性非static變數,lambda可以直接使用區域性static變數和它所在函數之外宣告的名字
  • 可變lambda

    • 值捕獲

      • lambda不會改變捕獲的拷貝變數,加上mutable即可
    • 參照捕獲

      • 取決於此參照指向的是一個const型別還是非const型別

參數系結

標頭檔案functional

  • bind,待定參數:placeholders::_1
  • 謂詞
    (一元-find_if;
    二元-sort)
  • 系結的參數爲參照:ref()函數

迭代器

  • 插入迭代器

    • inserter接受兩個參數,第一個參數是容器,第二個是指向給定容器的迭代器
    • 理解例子:
      *it=val;
      ||
      V
      it=c.insert(it,val);
      ++it;

list特定容器演算法

  • 其他容器演算法不會改變容器大小,但list和forward_list會改變容器大小,例如list的unique就會直接刪除重複的

關聯容器

map

set

型別別名

  • key_type

    • 關鍵詞型別
  • mapped_type

    • 關鍵詞關聯型別,只適用於map
  • value_type

    • 對於set,與key_value相同
    • 對於map,爲pair<const key_type,mapped_type>

動態記憶體

原因

  • 程式不知道自己需要使用多少物件(容器)
  • 程式不知道所需物件的準確型別(模板)
  • 程式需要在多個物件間共用數據

shared_ptr

  • 建構函式

    • 接受指針參數,需要直接初始化
    • explicit,抑制隱式
    • 正確例子:shared_ptrp(new int(1024));
      錯誤例子:shared_ptrp=new int(1024);
  • 陷阱

    • 不使用相同的內建指針值初始化多個智慧指針
    • 不delete get()返回的指針
    • 不使用get()初始化或reset另一個智慧指針
    • 當你使用的智慧指針管理的資源布氏new分配的記憶體,記住傳遞一個刪除器

unique_ptr

  • 特性

    • "擁有"所指物件

    • 直接初始化(不接受非new返回型別的指針,如*pi=&x等)

    • 不支援普通的拷貝或賦值操作

      • 例外:可以拷貝或賦值一個將要被銷燬的unique_ptr

weak_ptr

  • 特性

    • 不控制所指向物件生存期的智慧指針,指向由一個shared_ptr管理的物件
    • 系結到shared_ptr不會改變shared_ptr的參照計數
    • 參照計數爲0,即使有weak_ptr指向物件,物件也還是會被釋放

動態陣列

  • 初始化

    • new可以分配大小爲0的陣列,返回一個合法非空指針
      此指針不能解除參照,因爲它不指向任何元素
  • 釋放

    • delete []pa

      • 陣列元素按逆序銷燬,首先銷燬最後一個元素
  • 關於unique_ptr

    • 不能使用點和箭頭成員運算子,因爲指向的是一個數組
    • 當一個unique_ptr指向一個數組時,可以使用下標運算子來存取陣列中的元素
  • 關於shared_ptr

    • 不直接支援管理動態陣列,除非提供自定義的刪除器

    • 預設情況下使用delete刪除,但物件是一個動態陣列(非vector),對其使用delete所產生的問題與釋放一個動態陣列時忘記[]產生的問題一樣——行爲未定義

    • 存取

      • *(sp.get()+i)=i
        需要獲取一個內建指針,使用get()

allocator類

  • 與new的不同

    • new將記憶體分配和物件構造組合在一起
      allocator將記憶體分配和物件構造分離
  • 分配的記憶體是原始的,未構造的

拷貝控制

拷貝建構函式

  • 定義

    • 一個建構函式的第一個參數是自身類型別的參照,且任何額外參數都有預設值,此建構函式是拷貝建構函式
    • 合成拷貝建構函式是預設的拷貝建構函式(自己未定義,編譯器自動生成的)
      可以逐元素地拷貝一個數組型別的成員
  • 觸發條件

    • 物件作爲實參傳遞給非參照型別的形參
    • 從返回型別爲非參照型別的函數返回一個物件
    • {}初始化列表——陣列,聚合類
    • 容器呼叫insert和push,進行拷貝初始化
  • 注意

    • 編譯器爲了優化,也會繞過拷貝建構函式,但拷貝構造必須存在且可存取

解構函式

  • 特點

    • 沒有返回值,不接受參數,因此不能被過載,只有唯一一個解構函式
    • 成員按初始化順序的逆序銷燬
    • 內建型別沒有解構函式
  • 觸發條件

    • 變數離開作用域
    • 物件被銷燬,成員被銷燬
    • 容器被銷燬,其元素也被銷燬
    • delete銷燬
    • 對於臨時變數,當建立它的完整表達式結束時被銷燬
  • 注意

    • 解構函式自身並不直接銷燬成員,成員是在解構函式體之後隱含的解構階段中被銷燬的

三/五法則

  • 需要解構函式的類也需要拷貝和賦值操作
  • 需要拷貝操作的類也需要賦值操作,反之亦然

阻止拷貝

  • =delete
  • 解構函式不能是刪除的成員

物件移動

  • 物件

    • IO類和unique_ptr類可以移動但不能拷貝
  • 右值參照&&

    • 只能系結到臨時物件,不能系結到一個變數上

    • 所參照的物件將要被銷燬

    • 該物件沒有其他使用者

    • move函數

      • 返回值

        • 返回給定物件的右值參照
      • 呼叫move意味着除了對該移動源物件賦值或銷燬外,我們將不再使用它

    • 移動建構函式和移動賦值運算子接受一個(通常是非const的)右值參照(X &&)

  • 左值參照&

運算子過載

基本概念

  • ,
    &
    ||
    &&
    不應該被過載

::
.*
.
?:
不能被過載

  • 對於運算子函數來說,它或者是類的成員,或者至少含有一個類型別的參數

  • 成員函數還是非成員函數

    • = [] () ->必須是成員
    • 複合運算子(+=)一般也是成員,並非必須
    • 改變狀態的運算子或者與給定型別密切相關的運算子,如++ – &,通常是成員
    • 具有對稱性的運算子通常是普通的非成員函數

遞增和遞減運算子

  • 區分前置後置運算子

    • 後置版本接受一個額外的int型別形參(後來者排隊)
  • 規則

    • 後置運算子應該返回物件原值返回的形式是一個值而非參照,與內建版本保持一致

函數呼叫運算子

  • lambda是函數物件

    • 編譯器將lambda表達式翻譯成一個未命名類的未命名物件,在lambda表達式產生的類中含有一個過載的函數呼叫運算子(),lambda產生的類當中的函數呼叫運算子是一個const成員函數
  • 可呼叫物件與function

    • 不同類型可能具有相同的呼叫形式

    • 定義函數表,使用function儲存

    • 過載函數與function

      • 儲存函數指針
      • lambda消除二義性

型別轉換運算子

  • 沒有顯式返回型別,也沒有形參,必須被定義成類的成員函數,且爲const成員

  • 顯式的型別轉換將被隱式執行

    • if,while,do及語句的條件部分
    • for語句頭的條件表達式
    • ! || && 的運算物件
    • ? : 的條件表達式

模板與泛式程式設計

定義模板

  • 函數模板

    • 非型別模板參數

      • 模板實參必須是常數表達式
    • 函數模板和類別範本成員函數的定義通常在標頭檔案中

  • 類別範本

    • 顯示模板實參
    • 一個類別範本的每個範例都形成一個獨立的類
      沒有特殊存取許可權
    • 在一個類別範本的作用域內,我們可以直接使用模板名而不必指定模板實參

標準庫特殊設施

tuple

  • 型別

    • 任意數量成員,每個成員型別可以不同
    • tuple<T1,T2,T3,…,Tn> t (v1,v2,…,vn)

在这里插入图片描述