構造函數
一、作用
主要用來完成對象的初始化工作。
二、定義及使用
構造函數的定義:
類名(參數表)
{
函數體
}
舉例說明:
class Date
{
public:
//構造函數
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(2019,10,5); //顯示的調用構造函數
Date d1(2019,10,5); //隱式的調用構造函數
Date* p=new Date(2019,10,22); // new和構造函數一起使用
}
解析:
Date d1=Date(2019,10,5);
就是分別將數據成員_year、_month、_day
分別初始化爲2019、10、5。顯示調用和隱式調用都是一樣的作用。
對於語句Date* p=new Date(2019,10,22);
它創建了一個無名對象,並將其初始化爲參數提供的值,並將該對象的地址賦給指針p
。在這種情況下,新對象沒有名稱,但是可以根據指針來進行管理。
注意:
我們無法使用對象來調用構造函數,因爲在構造函數構造出對象之前,對象是不存在的。因此構造函數是由編譯器自動調用,而不能通過對象來調用。
三、特點
1、函數名和類名相同
2、無返回值
3、在創建對象時,編譯器自動調用
4、可以重載
四、默認構造函數
(1)在沒有顯示的定義構造函數時,編譯器將自動生成一個默認構造函數。
如下所示:
Date ()
{}
如果這時候創建了一個對象d
,列如:
Date d
那麼這個對象的數據成員都是隨機值,但是程序不會出錯。
(2)如果顯示的定義了構造函數,列如只定義瞭如下構造函數:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
如果此時創建一個對象d
,列如:
Date d
那麼這時就會編譯報錯。因爲只有在沒有顯示定義構造函數時,編譯器纔會提供默認的構造函數。所以如果想要不出錯,那麼就要再寫一個無參的構造函數。
總的代碼就如下所示:
class Date
{
private:
int _year;
int _month;
int _day;
public:
Date() //無參構造函數或者也叫做默認的構造函數
{}
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
};
int main()
{
Date d1; //調用的是默認的構造函數
Date d3(2019, 10, 5); //調用的是帶三個參數構造函數
}
五、帶默認值參數的構造函數
不僅是普通的函數可以缺省參數,構造函數也可以進行缺省。
列如:
Date(int year=2022, int month=3, int day=5)
{
_year = year;
_month = month;
_day = day;
}
它的使用辦法和普通函數使用缺省是一樣的。
六、成員初始化列表
我們可以將上面的構造函數利用成員初始化列表換成另外一種形式,如下所示:
Date(int year, int month, int day):_year(year),_month(month),_day(day)
{}
注意:
對於用const修飾的數據成員、或是引用類型的數據成員我們只能使用成員初始化列表來進行初始化。
七、成員初始化的順序
首先看一下,下面這段代碼:
#include<iostream>
using namespace std;
class D
{
public:
D(int i):x(y+1),y(i)
{
cout << "x:" << x << endl;
cout << "y:" << y << endl;
}
private:
int x;
int y;
};
int main()
{
D d(10);
}
運行結果:
解析:
按照成員初始化列表的本意,應該是首先用i
的值去初始化y
,然後再用y
的值去初始化x
。但是爲什麼結果卻在意料之外呢?這是因爲數據成員的初始化順序是按照它在類中聲明的順序初始化的。所以它會首先初始化x
,然後再初始化y
。