Section 1
命名空間
- 三種方式
using std::cout;//全部文件使用的cout來自std
std::cout << "hello" << std::endl;//用的地方指定
using namespace std;//把std所有的命名空間全部引入
- 定義一個命名空間
- 07 using聲明和using編譯指令
對C語言增強的地方
- 在c++中,變量可以隨用,隨定義;C一般要求定義在前面;
- c中對全局變量重複定義,會解釋爲一個是聲明;c++會報錯
- struct student在c中定義變量要加上struct;在c++中不用(把他當成類處理)直接student s1
- 對於函數在c中不寫返回值(默認是int),不會報錯;c++強制要寫
- 在c中可以傳多餘的參數
- 增加了bool類型
- 三目運算符在c++中可以當左值:返回的值變量的引用
- const的增強:針對直接賦值(通過指針,全局變量都不能改,局部變量c可以改,c++改的是臨時變量的值,本身沒有改)
-
對const修飾的變量取地址的時候,會分配臨時內存(一般來說分配了內存就可以通過指針改,但是這個臨時的內存改了沒有意義)
-
const前加入關鍵字external後也會分配內存,(這裏通過指針改會報錯)
-
使用變量初始化const的變量可以通過指針修改
-
自定義的數據類型也可以通過指針修改
-
c中的const可以改(針對直接賦值)
-
編譯器看到a就會到文字常量區的符號表找到key值爲a的value替代,類似於宏定義,不同的是宏定義是在預處理階段,const處理是在編譯階段
-
-
枚舉的增強:c中可以用int表示(含義不清)
-
引用的基本語法
-
int &是引用的數據類型,就是別名;
-
通過引用,在函數傳參的時候可以用引用做形參接收,迴避了傳指針的麻煩;
-
引用的本質:int *const p(常指針,內容可變,p值不變)
-
引用一定要定義的時候初始化,跟const一樣
-
引用的大小和指針一樣
-
int *const p=&a
-
有關常指針:數組爲例
-
引用作爲函數的返回值:
-
引用作爲返回值,不要返回局部變量的引用
-
如果是在堆上開闢的,或者是static定義的局部變量可以返回
-
函數可以作爲左值(如果函數返回值是指針,他也是返回的是數值,不能作爲左值,但是引用可以)
-
指針引用
- 避免了二級指針操作中容易出錯
- 避免了二級指針操作中容易出錯
-
const 修飾引用(可以通過指針修改)
- 引用用在形參上(可以不用開闢新的空間),但是爲了防止誤操作改變外面的量,可以加上const:
- 引用用在形參上(可以不用開闢新的空間),但是爲了防止誤操作改變外面的量,可以加上const:
內聯函數
- 如果反覆多次調用一個函數,就會有多次進出棧的操作,爲了避免這個開銷。引入內聯函數
- 內聯函數的作用類似於宏函數(但是宏函數不會檢查語法錯誤。,是在預處理階段做的,沒有語法檢查的能力)
- 內聯函數是編譯階段做的,有語法的檢查能力,本質就是在調用處,原處展開。(但是不會無限展開,如果代碼量太大,爲了避免過於佔用內存,還是會進出棧操作,比如在inline里加個1000次的循環),一般用在一兩行代碼,要反覆運行的位置(犧牲空間換時間)
- 成員函數一般都很短,他們都隱藏了關鍵字inline
進入18
函數的默認參數以及佔位參數
- 帶默認參數
- 函數的聲明和實現只能有一個有默認參數
- 函數的佔位參數:符號重載用
- 函數重載(不是複寫)
- 原理:編譯器針對不同的函數,修改了函數名
externC
- 因爲c++獨有的函數重載機制,編譯器會修改函數名(方便找到函數),所以在c++編譯器上運行c++代碼要加上一下帶條件宏定義:
c語言的結構體不做類型的檢測,完全只是內存大小相同即可,只是檢測指針的步長。導致不同的結構體,只要內存分配相同,可以同樣傳遞:
- 例如一個函數要求傳入的結構他爲person但是另一個結構體dog內存分配與人相同也可以傳入該函數。
- c語言中結構體裏只能放屬性,不能放行爲(函數,要函數指針纔行)
c++中struct和class區別就是默認權限不同,struct默認public,class默認private(class相比struct增加了權限控制)
Section 2
類相關
構造函數和析構函數
-
編譯器自動調用
-
編譯器自動生成空的無參構造和析構
-
在公共作用域下
-
構造:沒有返回值,也不用寫void,跟類名寫法一樣,可以重載
-
析構:函數名前加~
-
拷貝構造函數(就是拷貝一個對象):傳的是同類對象的引用,防止修改要加const
- 要用&接收,不用的話就是值傳遞。要新創建對象,就進入了無限的循環
- 要用&接收,不用的話就是值傳遞。要新創建對象,就進入了無限的循環
-
調用:
- 括號法:
- 顯示法:
- 注意點:
- 隱式法:
- 括號法:
-
拷貝構造函數的調用時機
- 當傳值的時候,形參就是調用 了拷貝構造函數:Person p=p1(隱式法)
- 以值的方式返回局部對象
- 當傳值的時候,形參就是調用 了拷貝構造函數:Person p=p1(隱式法)
-
構造函數的調用規則
深拷貝與淺拷貝
- 淺拷貝的時候只拷貝了指針,這樣在釋放的時候,其他有拷貝構造函數創建出來的對象p2先釋放了指針,而本對象p1再釋放就會崩潰;解決方法就是深拷貝:
- 重寫系統默認的拷貝構造函數(系統只會簡單的值拷貝):
初始化列表
- 用來初始化屬性
- explicit關鍵字使用
- new和delete運算符:完成了開闢空間,調用構造函數(賦值),判斷的複合工作
- new
區別
- 注意事項:
- 利用new創建數組
- new
靜態成員
-
爲什麼要在類外初始化:
- 因爲如果沒有創建對象,類裏面的代碼不會運行;
- 編譯期(分配了空間)
- 這個時候如果用類名訪問就會報錯(沒有值)
- 但是私有的靜態成員,可以不用賦值,類外通過類名也訪問不到,但是可以在類外賦值
-
訪問方式
-
單例模式:
- 私有有參構造和拷貝構造
- 加了作用域:可以視爲是在類內部執行的,所以能訪問私有構造;
C++對象模型初探-成員變量和成員函數分開存儲
this指向被調用成員函數所屬的對象
- 編譯器編譯的時候,把this指針已經加入了:
- 空指針訪問成員函數- 也可以訪問沒用用到this 的成員函數;否則會崩潰
常函數和常對象
- 加上關鍵字mutable
- 常對象
- 常對象不能調用普通的成員函數,只能調用常函數。
友元
-
全局函數作爲友元函數
- 在類中用friend關鍵字+全局函數的聲明,就能訪問該類內部的私有屬性
- 在類中用friend關鍵字+全局函數的聲明,就能訪問該類內部的私有屬性
-
友元類
- 友元類不一定有傳遞性,可逆性
- 類內的函數,可以放在類外部實現,加上命名空間就行
同樣:
-
同理也可以是指成員函數,作爲另一個類的友元;
數組類封裝
運算符重載
-
加運算符:分別利用成員函數,和全局函數
-
-
左移運算符重載
- 要用全局函數實現
- 返回引用是爲了鏈式編程
- 要用全局函數實現
-
遞增運算符重載++
-
前置++:一般放在成員函數,考慮到一直要操作,所以要返回引用,例如:++(++a)
- - 後置++:返回的是臨時變量,本體內部已經記錄了;臨時變量方法調用完會回收,所以不能返回引用;
-
由於後置++產生了臨時副本,所以爲了效率考慮,推薦日常使用前置++;
-
指針運算符重載-智能指針
-
賦值運算符重載
-
[]運算符重載
-
關係運算符重載
-
函數調用運算符重載
-
不要重載邏輯與和邏輯或符號:因爲沒法實現短路的效果
Section 3
繼承和派生
- 語法:class 子類:繼承方式:父類
- 繼承方式
- protected 子類內可以訪問,其他地方不可訪問
- private 只有自己類內可以訪問
繼承中的對象模型
繼承中的構造和析構
-
子類默認走父類的無參構造,但是可以利用初始化列表指定要走的父類構造函數(類似於java中的super):
-
子類不繼承父類的構造函數和析構函數,只有父類才知道怎麼樣構造和析構自己的屬性
-
繼承中的同名成員處理
-
繼承中的同名靜態成員處理
-
多繼承的語法
-
菱形繼承問題以及解決
-
使用虛繼承處理兩份數據的問題(二義性,浪費內存)
-
虛繼承的內部工作原理剖析
- 一旦使用了虛繼承,那麼內部之前綁定的變量就會變成指針,指向一個表,表中的內容就會根據子類是否重寫而覆蓋
多態相關
-動態聯編,通過虛函數實現-
- 多態原理
- 多態的好處(計算器案例)
- 把要幹什麼和怎麼幹分開
- 有純虛函數的類,也叫抽象類
- 純虛函數子類一定要重寫
- 純虛函數跟java的抽象函數類似;
- 虛析構和純虛析構
- 虛析構:當子類有的屬性是創建在堆區上的,那麼就要重寫析構函數,爲了子類delete的時候能夠調用到,父類的析構函數應該寫成虛析構;
- 純虛析構和純虛函數不同:由於父類的析構函數一定會被調用(父類自己也有屬性可能在堆區,自己也要釋放),所以要有函數體;但是純虛函數已經=0,所以函數體寫在外面;
- 純虛析構 有了之後,也變成了抽象類;
- 向上向下類型轉換
- 重寫、重載、重定義;
- 重定義:
泛型編程
模板技術
- 引用傳遞的時候原名的類型和別名的類型一定要一致,編譯器不會自己幫你轉(例如char 轉 int),但是普通的傳遞會轉
- 模板使用:編譯器推導出來了也行;推不出來就要指定(比如:函數都沒有參數的時候)
- 函數模板和普通函數的區別以及調用規則
- 區別:
-規則:
- 區別:
- 模板機制
- 函數模板通過傳入的T的類型生成了模板函數(編譯器內部生成的)
- 模板的侷限性以及解決:不能真的針對每一個類型
- 函數模板通過傳入的T的類型生成了模板函數(編譯器內部生成的)
類模板
-
與函數模板的區別:
- 類模板不能使用自動類型推導,必須顯示指定
- 類模板中的類型可以有默認參數(默認什麼類),函數模板不行;
-
泛型編程:體現在模板技術;特點是將類型參數化;
-
類模板中的成員函數創建時機
- 類模板的成員函數,並不是一開始就創建出來的,而是在運行階段,傳入了T的具體類型才創建出來的;
-
類模板作爲函數參數
-
類模板碰到繼承的問題以及解決
- 子類創建的時候必須要知道父類模板T是什麼類型,否則無法給父類分配內存(因爲要走子類父類的構造)
- 如果子類是具體類(不是類模板),那就指定父類T的類型
- 如果子類是類模板,那麼把自己的一個泛型交給父類就行:
-
類模板的類外實現
-
類模板的分文件編寫問題以及解決
-
類模板碰到友元函數
-
類模板案例—數組類封裝
- 需要注意的是,自定義對象的數組初始化前,會先初始化自定義對象(走無參的默認構造0),自定義類裏的有參構造會默認刪掉無參構造(所以自己手動補上)
- 需要注意的是,自定義對象的數組初始化前,會先初始化自定義對象(走無參的默認構造0),自定義類裏的有參構造會默認刪掉無參構造(所以自己手動補上)
類型轉換-靜態類型和動態類型轉換
- 自定義類型 支持繼承類對象的,指針,引用間的轉換;
- 沒有繼承關係的不支持
-
動態轉換(嚴謹)
- 不安全或者丟失精度都不讓轉
- 不安全或者丟失精度都不讓轉
-
類型轉換-常量類型
- 不允許把非指針或者非引用做const_cast轉換(不能把常量轉成變量)
- 不允許把非指針或者非引用做const_cast轉換(不能把常量轉成變量)
-
重新解釋類型轉換(不安全,不建議用)
Section 4
-
異常的基本語法
- 爲什麼要有異常:c中的返回值判斷有缺陷(可能是異常,也可能是正常值)
- 爲什麼要有異常:c中的返回值判斷有缺陷(可能是異常,也可能是正常值)
-
拋出自定義異常:跟java不一樣,不用繼承exception的父類
-
棧解旋
-
異常的接口聲明
-
異常變量的生命週期
- 值的方式接受對象,會調用拷貝構造
- 用引用接受
- 值的方式接受對象,會調用拷貝構造
-
用指針接收,匿名對象走完本行沒有變量接收就會回收,指針就變成野指針(接收匿名對象的地址沒有用)
-
異常變量如果拋出有接收的話,在拋出的函數體內創建的對象不會釋放(即使是創建在棧上,創建在堆上的更不會),他的生命週期跑到了catch的函數體裏;不像函數返回值(會在本函數結束的時候釋放棧裏的內容),理解:一個拋字,拋出去了就不規我管了。
-
異常的多態使用
- 繼承基類異常,由基類異常的引用接受
- 繼承基類異常,由基類異常的引用接受
-
使用C++系統標準異常類
-
編寫自己的異常類(繼承系統的)
- char* 不可以直接賦值給string,要通過構造函數string(char*)
- char* 不可以直接賦值給string,要通過構造函數string(char*)
-
標準輸入流
-
理解:cin就是鍵盤的緩衝區(輸入的數據現在緩衝區,cin.get就是在這個緩衝區拿);同理cout也有緩衝區,endl會刷新緩衝區,並加上\n換行符;
-
標準輸出流
-
格式化輸出(成員函數法)
- 控制符法:
- 控制符法:
-
文件的讀寫
- 寫:
- 讀
- 寫: