C 類預設函式

2021-10-06 05:10:30 字數 4546 閱讀 3596

在c++中,乙個類有八個預設函式:

預設建構函式;

預設拷貝建構函式;

預設析構函式;

預設過載賦值運算子函式;

預設過載取址運算子函式;

預設過載取址運算子const函式;

預設移動建構函式(c++11);

預設過載移動賦值操作符函式(c++11)。

只是宣告乙個空類,不做任何事情的話,編譯器會自動為你生成乙個預設建構函式、乙個預設拷貝建構函式、乙個預設過載賦值操作符函式和乙個預設析構函式。這些函式只有在第一次被呼叫時,才會被編譯器建立,當然這幾個生成的預設函式的實現就是什麼都不做。所有這些函式都是inline和public的。

我們不希望物件被顯示構造(單列模式)或賦值,可以將對應函式宣告為private,或者寫乙個基類,開放部分預設函式,子類去繼承就可以了。c++11新增識別符號default和delete,控制這些預設函式是否使用。

default:被標識的預設函式將使用類的預設行為,如:a() = default;

delete:被標識的預設函式將禁用,如:a() = delete;

override:被標識的函式需要強制重寫基類虛函式;

final:被標識的函式禁止重寫基類虛函式;

一、建構函式(constructor)

1.建構函式作用是對物件進行初始化,在堆上new乙個物件或在棧上定義乙個臨時物件時,會自動呼叫物件的建構函式。有初始化列表和建構函式體內賦值兩種方式,初始化列表在初始化物件時更高效(每個成員在初始化列表中只能出現一次),減少了一次賦值操作,推薦此方法;以下成員變數必須在初始化列表中初始化:常量成員變數、引用型別成員變數、沒有預設建構函式的成員變數(如果建構函式的引數列表中有乙個類的物件,並且該物件的類裡沒有預設引數的建構函式時,要是不使用初始化列表,引數中會呼叫無參或者全預設的建構函式,而那個類中又沒有);

2.函式名與類名相同,可以過載,不能為虛函式,不能有返回值,連void也不行;

3.如果沒有顯式定義,編譯器會自動生成乙個預設的建構函式,預設的構造函什麼都不會做;

4.無參建構函式和帶有預設值的建構函式(全預設)都認為是預設的建構函式,並且預設的建構函式只能有乙個;

5.函式體內可以使用this指標,但不可以用於初始化列表。因為建構函式只是初始化物件,初始化之前此物件已經存在了,所以可以有this,函式體裡面是進行賦值,初始化列表是對類中的各個成員變數進行初始化,初始化的位置物件不完整,所以不能使用this用於初始化列表;

6.對於出現單引數的建構函式需要注意,c++會預設將引數對應的型別轉換為該類型別,有時候這種隱式的轉換是我們不想要的,需要使用explicit關鍵字來限制這種轉換;

7.構造順序:虛擬基類的建構函式(如果有多個虛擬基類,按照它們被繼承的順序構造,而不是它們在成員初始化列表中的順序);

非虛擬基類的構造函函(如果有多個非虛擬基類,按照它們被繼承的順序構造,而不是它們在成員初始化列表中的順序);

成員物件的建構函式(如果有多個成員類物件,按照它們宣告的順序呼叫,而不是它們在成員初始化列表中的順序);

本類建構函式。構造的過程是遞迴的。

二、拷貝建構函式(copy constructor)

1.拷貝建構函式實際上是建構函式的過載,具有一般建構函式的所有特性,用此類已有的物件建立乙個新的物件,一般在函式中會將已存在物件的資料成員的值複製乙份到新建立的物件中。用類的乙個已知的物件去初始化該類的另乙個物件時,會自動呼叫物件的拷貝建構函式;

3.如果沒有顯式定義,編譯器會自動生成乙個預設的拷貝建構函式,預設的拷貝建構函式會依次拷貝類的資料成員完成初始化;

4.淺拷貝和深拷貝:編譯器建立的預設拷貝建構函式只會執行"淺拷貝",也就是通過賦值完成,如果該類的資料成員中有指標成員,也只是位址的拷貝,會使得新的物件與拷貝物件該指標成員指向的位址相同,delete該指標時則會導致兩次重複delete而出錯,如果指標成員是new出來就是「深拷貝」。

三、析構函式(destructor)

1.析構函式作用是做一些清理工作,delete乙個物件或物件生命週期結束時,會自動呼叫物件的析構函式;

2.函式名在類名前加上字元~,沒有引數(可以有void型別的引數),也沒有返回值,可以為虛函式(通過基類的指標去析構子類物件時候),不能過載,故析構函式只有乙個;

3.如果沒有顯式定義,編譯器會自動生成乙個預設的析構函式,預設的析構函什麼都不會做;

4.析構順序:和建構函式順序相反。析構的過程也是遞迴的。

四、過載賦值運算子函式(copy assignment operator)

1.它是兩個已有物件,乙個給另乙個賦值的過程。當兩個物件之間進行賦值時,會自動呼叫過載賦值運算子函式,它不同於拷貝建構函式,拷貝建構函式是用已有物件給新生成的物件賦初值的過程;

2.賦值運算子過載函式引數中const和&沒有強制要求,返回值是類物件的引用,通過返回引用值可以實現連續賦值,即類似a=b=c這樣,返回值型別也不是強制的,可以返回void,使用時就不能連續賦值;

3.賦值運算子過載函只能定義為類的成員函式,不能是靜態成員函式,也不能是友元函式,賦值運算子過載函式不能被繼承,要避免自賦值;

4.如果沒有顯式定義,編譯器會自動生成乙個預設的賦值運算子過載函式,預設的賦值運算子過載函式實現將資料成員逐一賦值的一種淺拷貝,會導致指標懸掛問題。

五、過載取址運算子(const)函式

1.過載取址運算子函式沒有引數;

2.如果沒有顯式定義,編譯器會自動生成預設的過載取址運算子函式,函式內部直接return this,一般使用預設即可。

六、移動建構函式和過載移動賦值操作符函式

1.c++11 新增move語義:源物件資源的控制權全部交給目標物件,可以將原物件移動到新物件, 用於a初始化b後,就將a析構的情況;

2.移動建構函式的引數和拷貝建構函式不同,拷貝建構函式的引數是乙個左值引用,但是移動建構函式的初值是乙個右值引用;

3.臨時物件即將消亡,並且它裡面的資源是需要被再利用的,這個時候就可以使用移動構造。移動構造可以減少不必要的複製,帶來效能上的提公升。

七、討論

1.建構函式為什麼不能有返回值?

(1).c++語言規定建構函式沒有返回值;

(2).建構函式不作為右值使用,返回值也沒有用;

(3).就算有返回值,從基本語義角度來講,也應該返回的是所構造的物件,所以沒必要多此一舉來指定返回型別了;

(4).假如有返回值,討論下面**

class a

a(int i):m_itest(i)

private:

int m_itest;

};按照c++的規定,a a = a();是用預設建構函式建立乙個臨時物件,並用這個臨時物件初始化a,此時,a.m_itest的值應該是0。現在如果a::a()有返回值,並且返回了1(表示構造成功),則c++會用1去初始化a,即呼叫有引數建構函式a::a(int i),得到的a.m_itest便會是1。於是,語義產生了歧義,使得c++原本已經非常複雜的語法,進一步混亂不堪。

建構函式的呼叫之所以不設返回值,是因為建構函式的特殊性決定的。當然,上面的討論,也是基於c++語言規定,如果規定建構函式可以有返回值,上面用法也許就不一樣了。是先有雞還是先有蛋,這是乙個神奇的問題。總之,現在c++語法體系是這樣的,如果設計建構函式可以有返回值,可能整個c++語言更難實現了。

2.物件建立和銷毀過程是怎樣的?

物件建立(new)過程:

(1).通過operator new申請記憶體;

(2).使用placement new呼叫建構函式(簡單型別忽略此步);

(3).返回記憶體指標。

new和malloc的比較:

(1).new失敗時會呼叫new_handler處理函式,malloc不會,失敗時返回null;

(2).new能通過placement new自動呼叫物件的建構函式,malloc不會;

(3).new出來的東西是帶型別的,malloc是void*,需要強制轉換;

(4).new是c++運算子,malloc是c標準庫函式。

new的三種形態:new operator,operator new,placement new

(1).new operator:上面所說的new就是new operator,共有三個步驟組成(申請記憶體,呼叫建構函式,返回記憶體指標),對於申請記憶體步驟是通過運算子new(operator new)完成的,對於呼叫什麼建構函式,可以由placement new決定;

(2).operator new:像普通運算子一樣可以被過載,operator new會去申請記憶體,申請失敗的時候會呼叫new_handler處理,這是乙個迴圈的過程,如果new_handler不丟擲異常,會一直迴圈申請記憶體,直到成功;

物件銷毀(delete)過程:

(1).呼叫析構函式(簡單型別忽略此步);

(2).釋放記憶體。

delete和free比較

(1).delete能自動呼叫物件的析構函式,free不會;

(2).delete是c++運算子,free是c標準庫函式。

3.拷貝構造函式引數為什麼必須使用類型別物件引用傳遞?

傳參的位置如果一直呼叫拷貝建構函式,也就是會遞迴引用,導致棧溢位。

4.賦值運算子過載函式為什麼要避免自賦值?

(1).提高效率。自賦值無意義,如果自賦值,可以立即return *this;

(2).如果不避免,當類的資料成員中如果含有指標,自賦值時會造成記憶體洩漏。

C 類的預設函式

使用者自定義乙個類,簡單的可以看成是乙個新的型別,與c 標準裡面的資料型別使用差不多。但在物件導向程式設計中,我們知道任何乙個物件必須要通過建構函式才能建立,以及可以將乙個物件拷貝給另乙個物件,將乙個物件作為引數傳遞給乙個函式等。c 預設函式 既然是預設函式,那也就是說即便類裡面乙個成員函式沒有,這...

C 類的預設函式

每個成員函式都有乙個指標形參,它的名字是固定的,稱為this指標,this指標是隱式的。但是建構函式沒有這個隱含的this指標。編譯器會對成員函式進行處理,在物件呼叫成員函式時,物件位址作實參傳遞給成員函式的第乙個形參this指標。編譯器會對成員函式進行處理,在物件呼叫成員函式時,物件位址作實參傳遞...

面試總結之C 類預設預設函式

一 編譯器定義的預設預設函式 編譯器會為每乙個類產生六個預設函式 建構函式 拷貝建構函式 析構函式 賦值運算子過載 取位址運算子過載 取位址運算子過載 const版 這六個函式的實現如下 demo demo 預設建構函式 demo demo 析構函式 demo demo operator 取址運算子...