首頁 > 軟體

詳解C++中類的六大預設成員函數

2022-10-16 14:01:19

一、類的預設成員函數

二、建構函式Date(形參列表)

建構函式主要完成初始化物件,相當於C語言階段寫的Init函數。

預設建構函式:無參的建構函式或全預設的建構函式,二者只能存在一個,同時存在類中,呼叫時會出現二義性。

1、建構函式的函數名和返回值

建構函式的函數名和類名相同且無返回值

2、建構函式的呼叫

物件範例化時,編譯器自動呼叫對應的建構函式且只呼叫一次

3、建構函式的過載

建構函式可以過載(多種初始化方式)注意:雖然全預設和無參的建構函式構成過載,但是呼叫時存在二義性。

class Date
{
public:
	//建構函式的過載
	Date()
	{
 
	}
	Date(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;//建立並通過無參建構函式初始化物件時,無需加括號,不然變成了函數宣告
	Date d2(2022,9,23);
	return 0;
}

4、系統生成的預設建構函式

如果我們在類中不寫建構函式,C++編譯器會在類中幫我們生成一個無參的建構函式。我們寫了建構函式,那麼系統將不會生成。

我們構造物件並呼叫建構函式時,初始化的資料是隨機值:

5、系統生成的預設建構函式的作用

系統生成預設建構函式對內建型別不處理,對自定義型別呼叫他的建構函式。

注意:下方程式碼中,Date類中的內建型別將會呼叫Date類中的建構函式;Date類中的Time _t將會呼叫Time類中的建構函式。

6、可以在內建型別的成員變數的宣告中給預設值

C++11中針對內建型別不處理初始化為隨機值的問題,打了修補程式:內建型別成員變數在類中宣告可以給預設值,甚至可以給動態開闢的預設值,缺點是不能判斷空間是否開闢成功。

注意這裡的預設值是預設值,不是初始化。初始化是要等物件呼叫時才叫初始化。

class Date
{
private:
	int _year=1;
	int _month=2;
	int _day=3;
	int* arr = (int*)malloc(sizeof(int) * 5);
};

這個特性只能用於解決預設建構函式初始化為隨機值的問題。這個特性不能解決物件的多種初始化方式(這也是建構函式支援過載的原因),建構函式該寫還是得自己寫。

7、初始化列表初始化

  • 1、物件的每個成員變數是在初始化列表部分進行初始化,而函數體內的行為是對成員函數賦初值。
  • 2、如果沒有在初始化列表裡顯示初始化某個成員函數,對於內建型別,有預設值用預設值,無預設值初始化為隨機值;對於自定義型別將會呼叫它的預設建構函式,沒有找到預設構造就會報錯。
  • 3、參照、const、無預設建構函式的自定義型別必須通過初始化列表初始化。
  • 4、成員變數在類中宣告次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先後次序無關。
Date(int year=1,int month=1,int day=1)//預設值
    :_year(year)//成員變數的定義
    ,_month(month)
    ,_day(day)
{}//成員變數的賦初值

總結:儘量使用使用初始化列表進行初始化,在類中儘量提供預設建構函式(最好是全預設的預設建構函式)

8、單引數構造(C++98)、多引數構造(C++11)

class Date
{
public:
	Date(int year=1,int month=1,int day=1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	//單引數的構造,構造+拷貝,編譯器直接優化為構造C++98
	Date d1 = 2022;
	//臨時物件具有常性,構造+拷貝,編譯器不會優化
	const Date& d2 = 2023;
	//多引數的構造C++11
	Date d3 = { 2022,10,16 };
	return 0;
}
  • 1、在構造時,支援等號加引數的構造形式,實際上發生的是隱式型別轉換。2022由int型別隱式型別轉換為Date型的臨時物件,該臨時物件再將它的值拷貝構造給d1。
  • 2、2023會隱式型別轉換為Date型別的臨時物件,具有常屬性。d2是這個臨時物件的參照,所以需要加上const。這裡發生構造+拷貝構造,涉及臨時物件的參照,所以編譯器並不會發生優化。
  • 3、可以使用explicit關鍵字修飾建構函式,會禁止隱式型別轉換。

三、解構函式~Date()

解構函式:與建構函式功能相反,解構函式不是完成對物件本身的銷燬,區域性物件銷燬工作是由編譯器完成的。而物件在銷燬時會自動呼叫解構函式,完成物件中資源的清理工作。資源包括動態開闢的空間,檔案的關閉等。相當於C語言階段寫的destroy函數。

1、解構函式的函數名、引數和返回值

解構函式的函數名是類名前加~,無參無返回值型別。

2、解構函式的特點

一個類只能有一個解構函式。若未顯式定義,系統會自動生成預設的解構函式。注意:解構函式不能過載

物件生命週期結束時,C++編譯系統系統自動呼叫解構函式

3、編譯器生成的預設解構函式

對於編譯器生成的預設解構函式,對自定義型別呼叫他的解構函式。對於內建型別,沒有需要處理資源。

注意:Date類中的內建型別會呼叫Date類中的解構函式;Date類中的自定義型別Time _t會呼叫Time的解構函式。

四、拷貝構造Date(const Date& d)

1、拷貝建構函式是建構函式的過載

拷貝建構函式:只有單個形參,該形參是對本類型別物件的參照(一般常用const修飾),在用已存在的類型別物件建立新物件時由編譯器自動呼叫。

拷貝建構函式的函數名和建構函式相同無返回值,在引數上和建構函式構成過載。

2、拷貝建構函式的引數

拷貝建構函式的引數只有一個並且是類型別物件參照。如果使用傳值傳參的方式進行拷貝構造,在傳值的過程中實參需要拷貝一份資料給形參,這個過程需要呼叫拷貝構造。形成層層傳值引發物件的拷貝的遞迴(有遞無歸)呼叫。

3、若未顯式定義,編譯器會生成預設的拷貝建構函式

預設的建構函式對於內建型別按照位元組拷貝。對於自定義型別則呼叫它的拷貝建構函式。

4、拷貝建構函式的深淺拷貝

通過預設的拷貝建構函式構造的物件,按位元組完成拷貝。這種拷貝被稱為淺拷貝(值拷貝)。

int main()
{
	Date d1(2022,9,24);
	Date d2(d1);
    return 0;
}

對於內建型別,使用淺拷貝即可,系統預設生成的就可以做到,所以我們不用動手寫拷貝建構函式。注意這裡有d1,d2兩個物件,當main函數生命週期結束時,這兩個物件均會發生一次解構,d2先解構,d1後解構。(後定義的先銷燬,類似棧的後進先出原則)

但是淺拷貝對於佔用“資源”的成員變數時(例如成員變數中有動態開闢或fopen的資源),指標雖然複製了,但是所指向的內容卻沒有複製,解構時存在同一塊空間被釋放兩次的問題。需要進行深拷貝。深拷貝的拷貝建構函式必須自己手動實現。

class Stack
{
public:
	Stack(int capacity=100)//建構函式
	{
		_capacity = capacity;
		_top = 0;
		_arr = (int*)malloc(sizeof(int) * 5);
		if (_arr == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
	}
	//Stack(const Stack& st)//淺拷貝,棧這個類不能用淺拷貝
	//{
	//	_capacity = st._capacity;
	//	_top = st._top;
	//	_arr = st._arr;
	//}
	Stack(const Stack& st)//深拷貝
	{
		_capacity = st._capacity;
		_top = st._top;
		_arr = (int*)malloc(sizeof(int) * st._top);
		if (_arr == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		memcpy(_arr, st._arr,sizeof(int)*st._top);
	}
	~Stack()//解構函式
	{
		_capacity = 0;
		_top = 0;
		free(_arr);
		_arr = nullptr;
	}
private:
	int* _arr;
	int _top;
	int _capacity;
};

棧這個類因為成員變數中有動態開闢的空間,所以要用深拷貝。

5、拷貝建構函式呼叫場景

  • 1、使用已存在的物件建立新物件
  • 2、函數引數型別為類的型別物件(傳值呼叫,實參拷貝給形參)
  • 3、函數返回值型別為類的良心物件(傳值返回)

五、賦值運運算元過載Date& operator=(const Date& d )

1、賦值運運算元過載

只有賦值運運算元是預設成員函數。

2、賦值運運算元過載的注意事項

  • 1、引數是const T&,傳參照可以減少一次拷貝構造。
  • 2、返回值是*this的參照,參照返回,減少一次拷貝構造,有返回值是為了支援函數的鏈式存取。
  • 3、務必檢查下是否支援自己給自己賦值。
  • 4、賦值運運算元過載必須是類中的預設成員函數,不能寫在全域性。
  • 5、系統預設生成的賦值運運算元過載會完成值拷貝。

六、取地址操作符過載和const取地址操作符過載

Date* operator&()
{
    return this;
    //return nullptr;
}
const Date* operator&()const
{
    return this;
    //return nullptr;
}

不用自己寫,除非想讓別人通過取地址操作符獲取到特定值(自己在過載函數內部寫)或遮蔽類地址。

以上就是詳解C++中類的六大預設成員函數的詳細內容,更多關於C++類別成員函數的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com