大家都定義struct或class時,不能給成員直接賦值,那麼物件中成員變數的初始值是多少?
對於區域性物件變數而言,其成員是個隨機值,因為該變數是被分配在棧上,對於其它區域性變數也是這樣.
對於全域性物件變數而言,其成員都為0,因為該變數是被分配在靜態儲存區上,對於const修飾就是分配在唯讀靜態儲存區上.
對於使用malloc分配的物件變數而言,其成員是個隨機值,分配的位址是存在堆上
對於使用new分配的物件變數而言,其成員都為0,由於分配的位址是存在堆上,很顯然new分配出來的值是內部清0了
所以:
建構函式
一般而言,物件建立時都會需要乙個確定的初始狀態
所以在c++中,引入了乙個特殊函式-建構函式
參考下面示例:
classtest
int getj()
test()
//建構函式
};test t;
//建立全域性物件t,並自動呼叫test()來初始化 i=1 j=2
多個過載的建構函式
由於建構函式可以帶引數,所以乙個類可以存在多個過載的建構函式
例如:
classtest
test(
inti)
test(
int i,float
t)};
多個過載建構函式的呼叫
在之前小節,分析到建構函式是用來初始化物件的.
如果有多個過載的建構函式,又如何來呼叫呢?
參考下面示例:
#include classtest
test(
inti)
test(
float t,int
i)
};int
main()
為什麼使用test t4=1 能呼叫test (4)?
當建構函式的引數只有乙個時,並且引數是其它型別,該建構函式便稱為轉換建構函式
所以編譯test t4=1時,編譯器會通過1來查詢哪個建構函式的引數滿足它,若沒找到則編譯報錯.
同樣在c++中,也可以通過()來初始化變數,比如:
int i(100); //轉換為int i=100;
物件陣列之手工呼叫建構函式
還是以上個test類為例:
test tarray[3]=; //初始化物件陣列裡的m_val值分別為0,1,2;
從上面可以看出,乙個建構函式其實是有返回值的,返回的是乙個臨時物件,然後賦值給tarray陣列裡。
這個臨時物件僅僅在呼叫時有效,執行下個**時,就會被登出。
臨時物件在後面第11章會講到:11.c++-臨時物件分析
特殊的建構函式
-無引數建構函式
當類中沒有定義建構函式時,編譯器會預設提供乙個函式體為空的無參建構函式,
-拷貝建構函式 (引數為: const class_name&)
當類中沒有定義拷貝建構函式時,編譯器會預設提供乙個拷貝建構函式,簡單的進行成員變數的複製
1.接下來證明無參建構函式的存在,參考下面出錯的示例
#include classtest
test(int val
)
};int
main()
編譯時, 報錯:
test.cpp:21: error: no matching function for call to 『test::test()』
提示說, 定義test t1時,沒有沒匹配到test()無參建構函式.
這是因為我們提供了建構函式,所以編譯器就不再提供無參建構函式了,從而編譯報錯。
也可以將上面的test(int val)改為:
test(int val=0)
這樣,就相當於提供了兩個函式:test(int val), test().
當我們呼叫test(1)時,則val=1.
當我們呼叫test()時,則val=0.
這是c++新加的特性,在c裡是沒有該功能
2.接下來來證明拷貝建構函式的存在,參考下面示例
#include classtest
//test()
////
test(const test& t)
//定義乙個拷貝建構函式
//};
intmain()
執行列印:
t1.m_val=-1078151848 t2.m_val=-1078151848
可以發現列印的資料t1.m_val和t2.m_val的值是一摸一樣的,這是因為執行test t2=t1;時,由於test類裡沒有提供拷貝建構函式,所以編譯器提供了乙個拷貝建構函式。
我們取消上面示例的遮蔽,使用自定義的拷貝建構函式:
執行列印:
set m_val=-1076378568t1.m_val=-1076378568 t2.m_val=-1076378568
從列印的資料上看到,執行test t2=t1; 時,明顯呼叫了我們自定義的test::test(const test& t)拷貝函式.
所以當類中沒有定義拷貝建構函式時,編譯器會預設提供乙個拷貝建構函式,進行簡單的成員變數拷貝.
深入理解拷貝建構函式
拷貝建構函式分為兩種:
-淺拷貝(編譯器提供的)
拷貝後物件的物理狀態相同
-深拷貝(指自己定義的)
拷貝後物件的邏輯狀態相同
接下來看淺拷貝和深拷貝的區別,參考下面示例:
#include classtest
int*getp()
void
free()
test(
inti)
//test(const test& obj) //
};int
main()
執行列印:
t1.m_val=0 t1.p=0x9fd1008 *t1.p=2t2.m_val=0 t2.p=0x9fd1008 *t2.p=2
*** glibc detected *** ./a.out: double free or corruption (fasttop): 0x09fd1008 ***
從列印結果看出,進行淺拷貝時,兩個物件的成員指標都指向同乙個位址0x9fd1008,可以發現當我們釋放了t1物件的成員指標後,就不能繼續使用t2物件的成員指標了.
接下來,我們取消上面示例的遮蔽,使用深拷貝,便能解決這類問題了.
那麼什麼時候需要進行深拷貝?
-當物件成員有指標時
-當物件成員需要開啟檔案時
-需要鏈結資料庫時
總結:
既然,淺拷貝可以實現成員變數拷貝,所以,只要自定義拷貝建構函式,必然裡面會實現深拷貝.
下章繼續學習:10.c++-建構函式初始化列表、物件構造順序、析構函式
9 C 類建構函式
類建構函式 前言,定義,基類與派生類,設計指導 1.前言 大部分物件在使用之前沒有正確的初始化是c 出錯的主要領域 引入類的建構函式是正確的初始化類的物件 一般什麼時候觸發並呼叫類的建構函式呢?答案是 當我們用類來定義乙個類變數的時候,如 class demo 宣告並定義好完整的類 當我們用類去建立...
c 物件導向9 c 聚合
在c 中,聚合是乙個程序,乙個類將另乙個類定義為實體引用 乙個類作為另乙個類的成員 這是另一種重用類的方法。它是一種表示has a關係的關聯形式。下面來看看乙個聚合的例子,其中employee類有address類的引用作為資料成員。這樣,它可以重用address類的成員。include using ...
程式設計練習思考9 C 建構函式不可繼承
1 靜態成員函式,非靜態成員函式,賦值操作函式,上述三種子類都可以繼承基類。2 但是建構函式,子類不能直接繼承父類的,需要自己寫,或者呼叫父類的建構函式,完成初始化。3 析構函式可以繼承擴充套件 編譯器總是根據型別來呼叫類成員函式。但是乙個派生類的指標可以安全地轉化為乙個基類的指標。這樣刪除乙個基類...