9 C 物件的建構函式 詳解

2021-09-07 17:19:11 字數 4744 閱讀 2731

大家都定義struct或class時,不能給成員直接賦值,那麼物件中成員變數的初始值是多少?

對於區域性物件變數而言,其成員是個隨機值,因為該變數是被分配在棧上,對於其它區域性變數也是這樣.

對於全域性物件變數而言,其成員都為0,因為該變數是被分配在靜態儲存區上,對於const修飾就是分配在唯讀靜態儲存區上.

對於使用malloc分配的物件變數而言,其成員是個隨機值,分配的位址是存在堆上

對於使用new分配的物件變數而言,其成員都為0,由於分配的位址是存在堆上,很顯然new分配出來的值是內部清0了

所以:

建構函式

一般而言,物件建立時都會需要乙個確定的初始狀態

所以在c++中,引入了乙個特殊函式-建構函式

參考下面示例:

class

test

int getj()

test()

//建構函式

};test t;

//建立全域性物件t,並自動呼叫test()來初始化 i=1 j=2

多個過載的建構函式

由於建構函式可以帶引數,所以乙個類可以存在多個過載的建構函式

例如:

class

test

test(

inti)

test(

int i,float

t)};

多個過載建構函式的呼叫

在之前小節,分析到建構函式是用來初始化物件的.

如果有多個過載的建構函式,又如何來呼叫呢?

參考下面示例:

#include class

test

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 class

test

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 class

test

//test()

////

test(const test& t)

//定義乙個拷貝建構函式

//};

intmain()

執行列印:

t1.m_val=-1078151848  t2.m_val=-1078151848

可以發現列印的資料t1.m_valt2.m_val的值是一摸一樣的,這是因為執行test t2=t1;時,由於test類裡沒有提供拷貝建構函式,所以編譯器提供了乙個拷貝建構函式。

我們取消上面示例的遮蔽,使用自定義的拷貝建構函式:

執行列印:

set m_val=-1076378568

t1.m_val=-1076378568 t2.m_val=-1076378568

從列印的資料上看到,執行test t2=t1; 時,明顯呼叫了我們自定義的test::test(const test& t)拷貝函式.

所以當類中沒有定義拷貝建構函式時,編譯器會預設提供乙個拷貝建構函式,進行簡單的成員變數拷貝.

深入理解拷貝建構函式

拷貝建構函式分為兩種:

-淺拷貝(編譯器提供的)

拷貝後物件的物理狀態相同

-深拷貝(指自己定義的)

拷貝後物件的邏輯狀態相同

接下來看淺拷貝和深拷貝的區別,參考下面示例:

#include class

test

int*getp()

void

free()

test(

inti)

//test(const test& obj) //

};int

main()

執行列印:

t1.m_val=0 t1.p=0x9fd1008 *t1.p=2

t2.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 析構函式可以繼承擴充套件 編譯器總是根據型別來呼叫類成員函式。但是乙個派生類的指標可以安全地轉化為乙個基類的指標。這樣刪除乙個基類...