C++ 建構函式 與 解構函式
在我們C++類中,預設會有6個預設的成員函式,即使我們不寫,也至少會有6個成員函式,分別是:建構函式、解構函式、拷貝構造、賦值操作符過載、取地址操作符過載、const修飾的取地址操作符過載。
建構函式
建構函式的功能就是給例項化出來的物件初始化。相當於在物件在被例項化的同時賦予初始值。類也是一種型別,是我們自定義的型別,那我們想要讓我們自定義的型別像內建型別一樣使用,就肯定離不來我們的預設的成員函式。
對於內建型別,可以在定義時初始化int num = 5
,定義變數num並初始化num為5。那我們的自定義型別也可以,那就要用到我們的建構函式。在我們例項化一個物件時,編譯器也會自動呼叫了我們的預設無參建構函式。
class Date
{
public:
void SetDate(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Display()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d;//呼叫了預設的無參建構函式
return 0;
}
一個物件的建構函式只能被呼叫一次,且不能顯示呼叫,也就是說不能通過物件去呼叫建構函式。
建構函式格式:類名 (引數)
建構函式時可以過載的,當我們自己寫了建構函式,編譯器就不會幫我們生成一個建構函式。建構函式分為無參構造和有參構造。無參構造和編譯器生成的預設建構函式時一樣的。由於無參的建構函式並不能很好的初始化物件,所以我們通常寫的是有參的建構函式。
//無參的建構函式
Date()
{}
//有參的建構函式
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
我們在建立物件的時候就可以給物件賦初始值:
Date d1; //呼叫無參構造
Date d2(2021, 2, 3); //呼叫有參構造
下面我們來測試:
在主函式中建立物件
class Date
{
public:
//無參的建構函式
Date()
{
cout << "Date()" << endl;
}
//有參的建構函式
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
cout << "Date(int, int,int)" << endl;
}
void Display()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Display();
Date d2(2021, 2, 3);
d2.Display();
return 0;
}
從執行結果可以證明,d1呼叫了無參構造,d2呼叫了有參構造。但是我們發現d1物件中的成員都是隨機值。為什麼會是隨機值呢?我們的建構函式對內建型別的成員變數不會處理。那不給成員變數賦初值,那無參建構函式不是就沒用了嗎?當然不是 ,如果我們類的成員變數中,有自定義型別的成員變數,建構函式會去呼叫我們自定義型別的預設建構函式,如果沒有預設建構函式,編譯器就會報錯。預設建構函式和建構函式時有區別的,下面會講到。
注意:以下程式碼並不是建立物件
//不是建立物件,而是聲明瞭一個函式
//函式名為 d,返回型別為Date型別,引數為空的函式。
Date d();
建構函式中還有一個定義是預設建構函式,預設建構函式就是不用傳引數也可以建立物件,預設建構函式有,1、我們自己不寫,編譯器自動生成的無參建構函式。2、我們自己寫了一個無參建構函式。3、在有參建構函式中給形參預設值(常用常寫)
有參帶預設值的預設建構函式:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
cout << "Date(int, int,int)" << endl;
}
總結起來就是,可以不傳引數建立物件的類是具有預設建構函式的。但是注意,預設建構函式在一個類中只能出現一個。
總結:
0、一個物件只能呼叫一次建構函式。
1、建構函式是物件建立的時候編譯器會自動呼叫的預設成員函式。完成的任務是對物件的初始化,非建立物件。
2、建構函式可以過載,分為有參構造和無參構造
3、如果我們沒有寫建構函式,編譯器會自動生成一個無參的建構函式
4、無參的建構函式和全預設的建構函式都稱為預設建構函式,且他們中只能存在一個。
5、無參建構函式只會對自定義型別做初始化,內建型別不做初始化。
6、當成員變數有自定義型別的變數時,所有的建構函式都會自定呼叫自定義型別的預設建構函式,必須是預設建構函式,如果沒有,編譯器會報錯。
解構函式
解構函式和建構函式的功能類似,解構函式是當一個物件要被銷燬時會被呼叫自身的解構函式,完成類的一些資源清理工作。
解構函式格式:~類名()
解構函式的特點:
1、無引數無返回值
2、一個類只能有一個解構函式,如果沒有顯示定義,編譯器會自動生成一個解構函式。
3、物件生命週期結束編譯器會自動呼叫解構函式
例如在我們的資料結構中,常常會用到一些結構,像棧這種需要結束時釋放掉之前malloc開闢的空間,如果不釋放就會造成記憶體洩漏。而在C++中,我們可以將棧的銷燬寫在解構函式中,這樣子就不會忘記呼叫銷燬的函數了。編譯器就可以自動呼叫解構函式完後資源的清理了。
//解構函式
~Stack()
{
if (_sData)
{
free(_sData);
_sData = NULL;
_top = 0;
_capacity = 0;
}
}
但是如果沒有涉及到的記憶體開闢,或者需要釋放的類,可以不用寫解構函式,使用編譯器自動生成的解構函式就可以。自動生成的解構函式有沒有作用呢?答案是有的,即使自身沒有資源要清理,當存在自定義型別的變數,且該變數需要釋放空間。這時候編譯器生成的解構函式會自動呼叫該自定義型別的變數的解構函式。完成釋放清理資源的功能。
析構的順序是後建立的物件先析構
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
cout << _year << "Date()" << endl;
}
~Date()
{
cout << _year << "~Date()" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2020, 2, 3);
Date d2(2021, 2, 3);
return 0;
}
總結:
0、解構函式只能有一個,如果沒有顯示定義,編譯器會自動生成
1、解構函式只能被呼叫一次,且不能顯示呼叫,只能在物件生命週期結束時會自動呼叫。
2、當類沒有資源釋放和清理,可以不寫解構函式。
3、編譯器預設生成的解構函式並非沒有作用,當成員變數存在自定義型別時,會自動呼叫自定義型別的解構函式。