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

在C++中,當你去創建一個類的時候,即便這個類是空類,也會自動生成下面6個默認成員函數,在本篇博客中,我將逐一分析下面6個默認成員函數。

構造函數

構造函數並不是去構造函數的函數,而是去對函數進行初始化的函數。構造函數的函數名與類名相同,當我們每次創建類對象的時候,就會自動調用構造函數。構造函數在對象的生命周期中隻會調用1次。

class Date
{
public:
    //構造函數
	Date(int year = 2021, int month = 4, int day = 11)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

構造函數的幾個特點

①函數名與類名相同

②無返回值

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

④構造函數可以重載

class Date
{
public:
    //構造函數的重載:
    //無參的構造函數
	Date()
	{}
    //需要傳參的構造函數
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

⑤如果類中沒有顯式定義構造函數(就是自己沒有去定義構造函數),那麼編譯器會自動生成一個無參的默認構造函數;

如果類中顯式定義瞭構造函數,那麼編譯器將不再生成,而是去使用用戶定義的構造函數。

⑥默認構造函數隻能同時存在1個。默認構造函數分為以下3種:①無參的構造函數 ②全缺省的構造函數 ③編譯器默認生成的構造函數

默認構造函數的共同特點是:不用傳參就可以調用

class Date
{
public:
	//下面2種和 當你不寫構造函數時編譯器自動生成的默認構造函數隻能同時存在1種
    //無參的
	Date()
	{
		_year = 2021;
		_month = 4;
		_day = 11;
	}
    //全缺省的
	Date(int year = 2021, int month = 4, int day = 11)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

⑦編譯器生成的默認的構造函數,對內置類型(int, char, double…)不會做任何處理,但是會針對自定義類型的成員,調用它的構造函數去進行初始

構造函數調用的2種寫法:

int main()
{
    //無參時
    Date d;
    //單個參數
	Date(1);
	Date d1 = 2;//這種寫法會發生隱式類型轉換
    //多個參數
    Date(2021, 4, 11);
    Date d2 = {2021, 4, 11};//C++11中才支持的寫法
}

構造函數與初始化列表

初始化列表:以冒號開始,接著是一個以逗號分隔的數據成員列表,每個”成員變量”後面跟一個放在括號中的初始值或表達式。

初始化列表有什麼用?

初始化列表,顧名思義就是對對象進行初始化的,但是我們已經可以在構造函數體內進行初始化瞭(通過對成員變量進行賦值來進行初始化),為什麼還需要初始化列表?

這是因為,有些類型的數據無法通過在構造函數體內進行賦值來進行初始化。這樣的數據類型有下面3種:

  1. 引用成員變量
  2. const成員變量
  3. 自定義類型成員 (且它的類沒有默認構造函數[即,它必須要進行傳參])

上面的三種數據類型有一個共同的特點,它們都要求你在定義變量的時候進行賦值。

比如,引用成員變量,使用引用的時候必須進行初始化,否則語法就是錯誤的。

析構函數

析構函數的作用與構造函數相反,在對象的生命周期結束的時候會自動調用析構函數,完成類的一些資源清理的工作。

析構函數的特點

  • 析構函數名是在類名的前面加上~
  • 無參,無返回值
  • 一個類中有且隻有1個析構函數。如果未顯式定義,系統會自動生成默認的析構函數。(如果定義瞭,則采用顯式定義的)
  • 對象生命周期結束時,C++編譯系統會自動調用析構函數
  • 編譯器生成的默認的析構函數,對內置類型(int, char, double…)不會做任何處理,但是會針對自定義類型的成員,會去調用它的析構函數

析構函數的一般使用情況:

一般使用在那些涉及到動態內存開辟空間的類中,因為這樣的對象需要對其動態開辟的空間進行釋放。

class Stack
{
public:
    //構造函數
	Stack(int n = 3)
	{
		_a = (int*)malloc(sizeof(int)*n);
		_size = 0;
		_capacity = n;
	}
    //析構函數
	~Stack()
	{
		free(_a);
		_size = _capacity = 0;
	}
private:
	int* _a;
	int _size;
	int _capacity;
};

拷貝構造函數

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

拷貝構造函數的特點

  • 拷貝構造函數是構造函數的一個重載
  • 拷貝構造函數的參數隻有1個,且必須使用引用傳參,如果使用引用傳值的形式會引發無限遞歸

拷貝構造函數的2種調用方法(完全等價的):

int main()
{
	Date d1(1);
	//拷貝構造函數
	Date d2(d1);  //1
	Date d3 = d1; //2
 
	return 0;
}

賦值運算符重載

在瞭解賦值運算符重載之前,我們需要先知道什麼是運算符重載。

運算符重載

運算符重載是具有特殊函數名的函數。

函數名:關鍵字operator後面接需要重載的運算符符號(如:operator>)

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

註意:

  • operator後面必須跟著的是操作符(這樣是不可以的 operator@)
  • 重載操作符必須有一個類類型或者枚舉類型的操作數
  • 用於內置類型的操作符,其含義無法改變。(比如內中的整型+,3+5這其中的+的意義不會改變)
  • this指針為限定的第一個形參,也就是this作為第一個操作數
  • .*、::、sizeof、?:、. 這5個操作符無法進行重載。

賦值運算符重載

class Date
{
public:
	Date(int year = 2021, int month = 4, int day = 11)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//賦值運算符重載
	Date& operator=(const Date& d)
	{
		_year = d._day;
		_month = d._month;
		_day = d._day;
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};

註意:賦值運算符重載必須有返回值,如果沒有返回值的話,無法解決 a = b = c 這種連續賦值的操作。

拷貝構造函數與賦值運算符重載

Date d1(1);
	Date d2(0);
	
	//賦值運算符重載
	d2 = d1;     //註意,隻有2個操作數都是已經定義過的變量時,才會調用賦值運算符重載
 
	//拷貝構造函數
	Date d3(d1);

淺拷貝

淺拷貝是你在沒有寫拷貝構造函數和operator=時,編譯器自動調用的默認成員函數。它的功能是將對象以字節的為單位拷貝過去。

取地址與const取地址操作符重載

這兩個運算符一般不需要重載,使用編譯器生成的默認取地址的重載即可(編譯器默認的基本就夠用瞭),隻有特殊情況,才需要重載,比如想讓別人獲取到指定的內容。

class Date
{
public:
    //取地址操作符重載
	Date* operator&()
	{
		return this;
	}
    //const取地址操作符重載
	const Date* operator&()const
	{
		return this;
	}
private:
	int _year;
	int _month;
	int _day;
};

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

class Date
{
public:
    //初始化列表
	Date(int year, int month, int day)
		:_year(year),
		 _month(month),
		 _day(day)
	{}
 
private:
	int _year;
	int _month;
	int _day;
};

推薦閱讀:

    None Found