C++類中六個默認的成員函數詳解

淺談

先來說一下“this指針”:

C++中通過引入this指針解決該問題,暨:C++編譯器給每個“非靜態的成員函數”增加瞭一個隱藏的指針參數,讓該指針指向當前對象(函數運行時調用該函數的對象),在函數體中所有成員變量的操作,都是通過該指針去訪問,隻不過所有的操作對用戶是透明的,暨用戶不需要來傳遞,編譯器自動完成。

說瞭這麼多其實編譯器在生成程序時獲取對象首地址的信息。然後將獲取的對象的首地址存放在瞭寄存器中,成員函數的其它參數都是存放在棧中。而this指針參數則是存放在寄存器中。類的靜態成員函數(用static修飾的成員函數)因為沒有this指針這個參數,所以類的靜態成員函數也就無法調用類的非靜態成員變量。

構造函數

構造函數是一個特殊的成員函數,名字與類名相同且不能有返回值,創建類類型時由編譯器自動調用,在對象的生命周期內隻調用一次。**主要任務是初始化對象。

↓下面是一個簡單的構造函數(全缺省):

主函數初始化時如果無參則以缺省值0給成員變量賦值。

默認構造函數:

Q:為什麼會出現上面的報錯——包含多個默認構造函數?

A:無參的構造函數和全缺省的構造函數都稱為默認構造函數,並且默認構造函數隻能有一個。註意:無參構造函數,全缺省構造函數,我們沒寫編譯器默認生成的構造函數,都可以稱為默認構造函數。

特征:

1.函數名與類名相同;

2.無返回值;

3.對象實例化時編譯器自動調用對應的構造函數;

4.構造函數可以重載。

析構函數

析構函數:與構造函數功能相反,析構函數是完成對象的銷毀,局部對象銷毀工作是由編譯器完成的。而對象在銷毀時會自動調用析構函數,完成類的一些資源清理工作,和構造函數一樣,如果我們沒寫析構函數,系統會生成一個默認析構函數,但這個析構函數什麼都不會做。

如果類中的成員變量不需要動態開辟內存空間,則默認析構函數可以完成析構任務,比如下面這種,可以說不用析構。

但是像下面這種,默認析構函數已經不能夠完成


特征:

1.函數名是在類名前加上字符~;

2.無參數(有一個隱藏參數*this指針)無返回值;

3.一個類有且僅有一個析構函數,若未顯示定義,系統會自動生成默認的析構函數;

4.對象生命周期結束時,C++編譯系統自動調用析構函數。

拷貝構造函數

拷貝構造函數:隻有單個形參,該形參是對本類類型對象的引用(一般常用const修飾),在用已存在的類類型對象創建新對象時由編譯器自動調用

從上圖我們可以看出,關於給t2變量初始化時肯定不是調用構造函數。

下面這張圖應該就可以解釋上面的問題

關於對拷貝構造函數參數的說明:

當然也同構造函數一樣,若未顯示定義,系統生成默認的拷貝構造函數。默認的拷貝構造函數對象按內存存儲按字節序完成拷貝,暨淺拷貝或值拷貝

特征:

1.拷貝構造函數是構造函數的一個重載形式;

2.拷貝構造函數的參數隻有一個(當然還有個隱藏的*this)且必須使用引用傳參,使用傳值方式會引發死遞歸。

賦值重載函數

賦值重載函數:C++為瞭增強代碼的可讀性引入瞭運算符重載,運算符重載是具有特殊函數名的函數,也具有其返回值類型,函數名字以及參數列表,其返回值類型與普通的函數類似。

**函數名字為:**關鍵字operator後面接需要重載的運算符符號;

函數原型:返回值類型operator操作符(參數列表)

Test& operator= (const Test& t)

註意:

不能通過連接其他符號來創建新的操作符:比如operator@;

重載操作符必須有一個類類型或者枚舉類型的操作數;

用於內置類型的操作符的操作符,其含義不能改變,例如:內置的整型+,不能改變其含義;

作為類成員的重載函數時,其形參看起來比操作數數目少1成員函數的,操作符有一個默認的形參this,限定為第一個形參;

*、::、sizeof、?:、. 以上5個運算符不能重載。

一個類如果沒有顯示定義賦值運算符重載,編譯器也會生成一個,完成對象按字節序的值拷貝。

const成員函數

將const修飾的類成員函數稱之為const成員函數,const修飾類成員函數,實際修飾該成員函數隱含的this指針,表明該成員函數中不能對類的任何成員進行修改。

multable關鍵字:當有必須要修改的成員變量時,需在成員變量聲明時前加上該關鍵字,及時是const成員函數也依然可以修改。

取地址及const取地址重載函數

這兩個默認成員函數一般不用重新定義,編譯器默認生成;

class Date
{
public:
	Date* operator&()
	{
		return this;
	}
	//因為對象被const修飾不能更改,所以返回值也要被const修飾
	const Date* operator&() const
	{
		return this;
	}
private:
	int _year;
	int _month;
	int _day;
};

深挖

 構造函數

上面的代碼居然可以正常運行並且賦值成功,這是為什麼。實際上編譯器在執行賦值語句前,將“100作為參數來構造一個無名的臨時的類”然後進行賦值。

上面這種情況我們稱為**“隱式轉換”**。

explicit關鍵字:如果在構造函數前加上這個關鍵字,則要求顯示轉換,不能進行隱式轉換。

因此我們進行強轉之後再賦值;

我們換一種思路,將“類”給整型賦值,且先給出一個強轉函數;

最後介紹另外一種初始化的方式:

給出一個復數類,參數列表初始化的效率要高於第一種初始化方式。

拷貝構造函數

下面介紹一下深拷貝,先看下面一段代碼:

這個代碼中我們沒有寫拷貝構造函數,可見默認拷貝函數這裡出瞭問題;前面我們說過,默認拷貝方式是淺拷貝,也就是值拷貝。

上面s1使用hello初始化,s2使用s1的值來拷貝,因此s2並未開辟新空間,而是指向s1的空間,因此在最後調用析構函數的時候,對同一塊空間free瞭兩次。

如果將代碼改成下面這種,那麼就不會報錯。

賦值運算符重載函數

這裡我們繼續引用上面構造函數部分最後給出的那個復數類

我們上面說瞭賦值重載函數,那麼到底什麼是賦值運算符重載呢?

運算符重載:對運算符賦予新的意義↓↓↓

由上圖可知,上面這個對加法運算符符號“+”的重載函數是成員函數,因為它參數裡面有一個隱藏的的this指針,所以需要用對象來調動它。

上面的代碼整體的思路就是:

1.調用四次次構造函數,構造C1C2C3以及在operator+內部的tmp;

2.C1調用加法重載函數進行C1+C2;

3.加法重載函數返回時需要調動一次拷貝構造函數(tmp拷貝到臨時對象);

4.調用賦值重載將C1+C2的值賦給C。

當寫的類想做某種運算時,但編譯器不支持,因此我們需要對運算符進行重載。

總結

到此這篇關於C++類中六個默認的成員函數詳解的文章就介紹到這瞭,更多相關C++類成員函數內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀:

    None Found