本片博客是作者在學習c++的過程中的筆記記錄,希望和各位讀者一起學習交流
類和對象
- 基本概念
類、對象、成員變量、成員函數
面向對象的三大概念:封裝、繼承、多態 - 類的封裝
封裝就是把數據(屬性)和函數(操作)合成一個整體,也就是把客觀事物封裝成抽象的類,並且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。 - 類的訪問控制關鍵字
- public:修飾的成員變量和函數,可以在類的內部和類的外部進行訪問
- private:修飾的成員變量和函數,只能在類的內部被訪問,不能在類的外部訪問
- protected:修飾的成員變量和函數,只能在類的內部被訪問,不能在類的外部訪問,用在繼承裏面
- 在類中沒有權限修飾的成員變量和函數,默認是private
- 類的前置聲明
對象的構造和析構
構造和析構函數
- 構造函數和析構函數的概念
- 構造函數:
c++中的類可以定義與類名相同的特殊成員函數,這種與類名相同的成員函數就做構造函數
構造函數在定義時可以有參數
沒有任何返回類型的聲明 - 構造函數的調用:
自動調用、手動調用 - 析構函數:
c++中可以定義一個特殊的成員函數清理對象,這個特殊的成員函數叫做析構函數
語法:
~className()
析構函數沒有參數也沒有任何返回類型的聲明
析構函數在對象銷燬時自動被調用
c++編譯器自動調用 - 在創建多個對象的時候,先創建的對象後釋放
- 構造函數:
- 爲什麼需要構造函數和析構函數
初始狀態是對象普遍存在地一種狀態
如果沒有構造函數,那麼必須給每個類提供一個initialize函數進行屬性的初始化,如果沒有進行調用initialize,即對象就沒有初始化,則屬性的值是不確定的,同時會導致程序冗餘。
構造函數的分類
- 無參構造函數
eg:test a; - 有參構造函數
用法:
編譯器調用:test a(參數);
手動調用:test b = test(參數);這樣會產生一個匿名對象 - 賦值構造函數(拷貝構造函數)(參數是對象自己):用一個對象初始化另一個對象
用法:
1. Test t1(1,2);
Test t0;
t0 = t1;//把t1賦值給t0 不會調用拷貝構造函數 會調用=操作符重載的函數
Test t3 = t1;//用t1初始化t3 會調用拷貝構造函數
賦值和初始化這兩個操作是兩個不同的概念
2. Test t4(t1);
3. 實參對象初始化形參對象會調用拷貝構造函數
4. 函數返回值爲對象,但是a是一個局部變量,因此返回的是匿名對象,所以會調用匿名對象的拷貝構造函數
test g()//函數g會返回一個匿名對象
{
test a(1,2); //在這裏會調用有參構造函數
return a; //在這兒會待用拷貝構造函數,用對象a初始化一個匿名對象
}
Test a = g();
//如果用匿名對象初始化另外一個同類型的對象,則g()返回的匿名對象會轉換成一個有名字的對象,名字爲a
Test a(1,2);
A = g();
//如果用匿名對象賦值給另一個同類型的對象,則g()返回的匿名對象會被析構掉 - 默認構造函數
注意: 1. 如果在類中沒有顯示的定義構造函數,則會默認地提供一個構造函數
2. 如果在類中沒有提供一個拷貝構造函數,那麼就會默認的提供一個拷貝構造函數
構造函數調用規則研究
- 當類中沒有定義一個構造函數的時候,c++編譯器會提供默認的無參構造函數和默認拷貝構造函數
- 當類中定義了構造函數的時候,c++編譯器不會提供無參構造函數
- 默認拷貝構造函數是成員變量簡單的賦值
深拷貝和淺拷貝
- 淺拷貝:對象之間屬性的賦值,不涉及內存空間的操作
上面這一段程序的解讀:
1. 創建t1對象,使用帶參數的構造函數進行創建,在帶參數的構造函數裏面進行對象t1屬性的初始化
2. 使用t1對象對t2對象進行初始化,會調用默認的拷貝構造函數,使t2的屬性與t1的屬性一樣
3. 在最後main函數結束的時候,會進行內存空間的釋放,進而導致會進行兩次的內存釋放,導致程序宕機 - 深拷貝:在類中對拷貝構造函數進行顯示的定義,在實現的時候,進行內存的分配,因此要在上述的代碼片中添加拷貝構造函數
當程序添加上拷貝構造函數的時候,用t1對t2進行初始化的時候就會顯示的調用拷貝構造函數,進行內存的分配,進而不會導致內存泄漏,不會發生宕機
對象初始化列表
- 爲什麼要有對象初始化列表:
- 假設有一個類成員,它的本身是一個類或者是一個結構,同時它只有一個帶參數的構造函數,沒有默認的構造函數,這是要對這個類成員進行初始化就必須調用這個類成員的帶參數的構造函數,如果沒有初始化列表,就將報錯
下圖是正確的示例:
- 當類成員中含有一個const對象或者是一個引用的時候,它們必須也通過初始化列表進行初始化,否則就會報錯
下圖是正確的示例:
總結:因爲這兩種對象要在聲明之後要進行初始化,而在構造函數中,進行的是對他們的賦值。
- 假設有一個類成員,它的本身是一個類或者是一個結構,同時它只有一個帶參數的構造函數,沒有默認的構造函數,這是要對這個類成員進行初始化就必須調用這個類成員的帶參數的構造函數,如果沒有初始化列表,就將報錯
- 語法:
Constructor::Constructor():m1(v1),m2(v1,v2),m3(v3)
{
//some other assignment operator
} - 注意概念:
初始化:被初始化的對象正在創建
賦值:被賦值的對象已經存在 - 注意:
成員變量的初始化順序與聲明的順序相關,與在初始化列表中的順序無關,初始化列表先於構造函數的函數體先執行
匿名對象的生命週期
eg: A(1); //這是定義一個匿名對象
這個對象的生命週期僅僅侷限於這一句話,對象剛剛創建,就會被析構
對象的動態建立和釋放
- new和delete基本用法
- new運算符:new運算符動態分配內存
- 使用形式:
指針變量 = new 類型(常量); //單個類型的對象
指針變量 = new 類型[表達式]; //對象數組 - 作用:從堆分配一塊“類型”大小的存儲空間,返回首地址
- 其中:“常量”是初始值,可缺省
創建數組對象時,不能爲對象指定初始值
- 使用形式:
- delete運算符:delete運算符釋放已分配的內存
- 使用形式:
delete 指針變量; //釋放單個對象的內存
delete [] 指針變量; //釋放對象數組內存 - 其中:“指針變量”必須是一個new返回的指針
- 注意:用new分配數組空間時不能指定初值,如果由於內存不足等原因而無法正常分配空間,則new會返回一個空指針NULL,用戶可以根據該指針的值來判斷分配空間是否成功。
- 使用形式:
- new運算符:new運算符動態分配內存
- malloc、free和new、delete的區別
malloc、free不會執行類的構造函數和析構函數
new、delete會執行類的構造函數和析構函數 - malloc、free可以和new、delete混合搭配使用
malloc分配的內存可以使用delete進行釋放
new分配的內存可以使用free進行釋放