一、類的六個(gè)默認(rèn)成員函數(shù)
??默認(rèn)成員函數(shù)
用戶沒(méi)有顯式實(shí)現(xiàn),編譯器會(huì)自動(dòng)生成的成員函數(shù),稱為默認(rèn)成員函數(shù)。
- 構(gòu)造函數(shù):完成對(duì)象的初始化工作。
- 析構(gòu)函數(shù):完成對(duì)象空間的清理工作。
- 拷貝構(gòu)造:使用同類對(duì)象初始化創(chuàng)建對(duì)象。
- 賦值重載:把一個(gè)對(duì)象賦值給另外一個(gè)對(duì)象(該對(duì)象已存在)。
- 取地址重載:獲取對(duì)象的地址,這兩個(gè)很少自己實(shí)現(xiàn)。
注意:構(gòu)造和析構(gòu)函數(shù),不是創(chuàng)建對(duì)象和銷毀對(duì)象。對(duì)象的創(chuàng)建和銷毀都是編譯器做的工作。
二、構(gòu)造函數(shù)
??為什么要有構(gòu)造函數(shù)?
為了避免每次創(chuàng)建對(duì)象后,都要去調(diào)用專門的成員函數(shù)設(shè)置對(duì)象的信息,這樣很麻煩,并且容易遺忘,那就想著能否在創(chuàng)建對(duì)象的同時(shí),就將信息設(shè)置進(jìn)去。因此,就有了構(gòu)造函數(shù)。以日期類為例:
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;//第一步:創(chuàng)建對(duì)象
d1.Init(2022, 7, 5);//調(diào)用初始化函數(shù)
d1.Print();
Date d2;
d2.Init(2022, 7, 6);
d2.Print();
return 0;
}
如上面的代碼,每次創(chuàng)建一個(gè)日期類對(duì)象后,都要手動(dòng)的去調(diào)用Init
函數(shù),完成對(duì)象的初始化,整個(gè)過(guò)程繁瑣,而且容易遺忘,為此,提出了構(gòu)造函數(shù)的概念。
??定義
構(gòu)造函數(shù)是一個(gè)特殊的成員函數(shù),名字與類名相同,創(chuàng)建對(duì)象的時(shí)候由編譯器自動(dòng)調(diào)用,以保證每個(gè)數(shù)據(jù)成員都有一個(gè)合適的初始值,并且在對(duì)象的整個(gè)生命周期中只調(diào)用一次。
??構(gòu)造函數(shù)的特性
- 函數(shù)名與類名相同。
- 無(wú)返回值。(無(wú)需void)
- 對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)。
- 構(gòu)造函數(shù)可以重載。
??示例:
class Date
{
public:
// 1.無(wú)參構(gòu)造函數(shù)
Date()
{}
// 2.帶參構(gòu)造函數(shù)
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
void TestDate()
{
Date d1; // 調(diào)用無(wú)參構(gòu)造函數(shù)
Date d2(2015, 1, 1); // 調(diào)用帶參的構(gòu)造函數(shù)
Date d3();
}
注意:如果通過(guò)無(wú)參構(gòu)造函數(shù)創(chuàng)建對(duì)象時(shí),對(duì)象后面不用跟括號(hào),否則就成了函數(shù)聲明。即Date d3();
是聲明了一個(gè)d3
函數(shù),該函數(shù)無(wú)參,返回一個(gè)日期類對(duì)象,并不是創(chuàng)建了一個(gè)日期類對(duì)象d3
。
構(gòu)造函數(shù)在語(yǔ)法上可以是私有的,但是在創(chuàng)建對(duì)象的時(shí)候就調(diào)不動(dòng)了。在單例模式中,會(huì)把構(gòu)造函數(shù)搞成私有,具體的我們以后再說(shuō)。
??編譯器生成的構(gòu)造函數(shù)
如果類中沒(méi)有顯式定義構(gòu)造函數(shù),則C++編譯器會(huì)自動(dòng)生成一個(gè)無(wú)參的默認(rèn)構(gòu)造函數(shù),一旦用戶顯式定義,編譯器將不在生成。
??示例:
class Date
{
public:
/*
// 如果用戶顯式定義了構(gòu)造函數(shù),編譯器將不再生成
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
*/
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
return 0;
}
將Date類中的構(gòu)造函數(shù)注釋掉后,代碼可以編譯通過(guò),因?yàn)榫幾g器生成了一個(gè)無(wú)參的默認(rèn)構(gòu)造函數(shù)。將Date類中的構(gòu)造函數(shù)放開(kāi)后,代碼編譯失敗,因?yàn)?strong>一旦顯式定義了任何構(gòu)造函數(shù),編譯器將不再生成默認(rèn)構(gòu)造函數(shù)。而此時(shí)Date中的構(gòu)造函數(shù)需要三個(gè)參數(shù),Date d1;
會(huì)去調(diào)用無(wú)參的構(gòu)造函數(shù),但是當(dāng)前類中沒(méi)有無(wú)參的構(gòu)造函數(shù),所以編譯會(huì)報(bào)錯(cuò)。
??編譯器生成的構(gòu)造函數(shù)干了什么?
C++中把類型分為內(nèi)置類型和自定義類型。內(nèi)置類型就是語(yǔ)言提供的數(shù)據(jù)類型,如:int
、char
……自定義類型就是我們使用class
、struct
、union
等自己定義的類型。(所有類型的指針都屬于內(nèi)置類型)。
編譯器生成的默認(rèn)構(gòu)造函數(shù),對(duì)內(nèi)置類型不做處理,對(duì)自定義類型,會(huì)去調(diào)用它的默認(rèn)構(gòu)造函數(shù)。
??示例:
//先定義一個(gè)時(shí)間類
class Time
{
public:
Time()
{
cout << "Time()" << endl;
_hour = 0;
_minute = 0;
_second = 0;
}
private:
int _hour;
int _minute;
int _second;
};
//再定義一個(gè)日期類
class Date
{
private:
// 基本類型(內(nèi)置類型)
int _year;
int _month;
int _day;
// 自定義類型
Time _t;
};
int main()
{
Date d;
return 0;
}
上面代碼,先定義了一個(gè)時(shí)間類Time
,它的成員變量都是內(nèi)置類型,給這個(gè)類寫(xiě)了一個(gè)無(wú)參的構(gòu)造函數(shù),接下來(lái),定義了一個(gè)日期類Date
,他有四個(gè)成員變量,其中_year
、_month
、 _day
都是內(nèi)置類型,_t
是自定義類型,并且,我們沒(méi)有寫(xiě)日期類的構(gòu)造函數(shù),這意味著,在創(chuàng)建對(duì)象的時(shí)候,會(huì)去使用編譯器生成的無(wú)參默認(rèn)構(gòu)造函數(shù)。
??內(nèi)置類型給默認(rèn)值
C++11中針對(duì)內(nèi)置類型成員不初始化的缺陷,打了補(bǔ)丁,即:內(nèi)置類型成員變量在類中聲明時(shí)可以給默認(rèn)值。
??示例:
class Time
{
public:
Time()
{
cout << "Time()" << endl;
_hour = 0;
_minute = 0;
_second = 0;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本類型(內(nèi)置類型)
int _year = 1970;//給默認(rèn)值
int _month = 1;
int _day = 1;
// 自定義類型
Time _t;
};
int main()
{
Date d;
return 0;
}
??默認(rèn)構(gòu)造函數(shù)
我們沒(méi)寫(xiě)編譯器自動(dòng)生成的構(gòu)造函數(shù)、無(wú)參構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù),這三種都叫做默認(rèn)構(gòu)造函數(shù),它們都有一個(gè)共同的特點(diǎn):可以不用傳參。默認(rèn)構(gòu)造函數(shù)只能有一個(gè),后面?zhèn)z,在語(yǔ)法上可以構(gòu)成函數(shù)重載,但是在無(wú)參調(diào)用的時(shí)候,會(huì)發(fā)生歧義,出現(xiàn)調(diào)用不明確。
注意:要把默認(rèn)構(gòu)造函數(shù)和默認(rèn)成員函數(shù)區(qū)分清楚,默認(rèn)成員函數(shù)是我們不寫(xiě)編譯器會(huì)自動(dòng)生成的,默認(rèn)構(gòu)造函數(shù)是不需要傳參的構(gòu)造函數(shù)。編譯器生成的構(gòu)造函數(shù),既是默認(rèn)構(gòu)造函數(shù),同時(shí)也是默認(rèn)成員函數(shù)。
??總結(jié)
一般情況下,都需要我們自己寫(xiě)構(gòu)造函數(shù)。如果滿足以下情況,即:內(nèi)置類型的成員變量都有默認(rèn)值,且初始化符合我們的要求,自定義類型都定義了默認(rèn)構(gòu)造,此時(shí)可以考慮不寫(xiě)構(gòu)造函數(shù),使用編譯器自動(dòng)生成的默認(rèn)構(gòu)造函數(shù)。自定義類型如果沒(méi)有對(duì)應(yīng)的構(gòu)造函數(shù),那就意味著初始化自定義類型需要傳參,此時(shí)必須自己寫(xiě)構(gòu)造函數(shù),并且還會(huì)用到初始化列表。
三、析構(gòu)函數(shù)
??定義
與構(gòu)造函數(shù)的功能相反,析構(gòu)函數(shù)不是完成對(duì)對(duì)象本身的銷毀,局部對(duì)象的銷毀工作是由編譯器完成的。而對(duì)象在銷毀的時(shí)候,會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù),完成對(duì)象中資源的清理工作。
??特性
-
析構(gòu)函數(shù)名是在類名前加上
~
。 - 無(wú)參數(shù)無(wú)返回值類型。
- 一個(gè)類只能有一個(gè)析構(gòu)函數(shù),若未顯式定義,系統(tǒng)會(huì)自動(dòng)生成默認(rèn)的析構(gòu)函數(shù)。
- 對(duì)象生命周期結(jié)束時(shí),C++編譯器自動(dòng)調(diào)用析構(gòu)函數(shù)。
小Tips:析構(gòu)函數(shù)不能重載。
??示例:
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 3)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (NULL == _array)
{
perror("malloc申請(qǐng)空間失敗!!!");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(DataType data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
// 其他方法...
~Stack()//析構(gòu)函數(shù)
{
if (_array)
{
free(_array);
_array = NULL;
_capacity = 0;
_size = 0;
}
}
private:
DataType* _array;
int _capacity;
int _size;
};
void TestStack()
{
Stack s;
s.Push(1);
s.Push(2);
}
Stack
中的成員變量_array
、_capacity
、_size
都是內(nèi)置類型,所以在對(duì)象s
生命周期結(jié)束要銷毀的時(shí)候,不需要資源清理,最后系統(tǒng)直接將其內(nèi)存回收即可,而_array
指向的空間是在堆區(qū)上申請(qǐng)的,這塊空間不會(huì)隨著對(duì)象生命周期的結(jié)束而自動(dòng)釋放(歸還給操作系統(tǒng)),所以_array
被回收后,就找不到動(dòng)態(tài)申請(qǐng)的那塊空間,會(huì)造成內(nèi)存泄漏,因此在對(duì)象銷毀前,要通過(guò)析構(gòu)函數(shù)去釋放成員變量_array
指向的空間,這就是析構(gòu)函數(shù)的作用。
??編譯器生成的析構(gòu)函數(shù)干了什么?
我們不寫(xiě),編譯器會(huì)自動(dòng)生成一個(gè)析構(gòu)函數(shù)。該析構(gòu)函數(shù)對(duì)內(nèi)置類型不做處理,對(duì)自定義類型會(huì)去調(diào)用它的析構(gòu)函數(shù)。
??示例:
class Time
{
public:
~Time()
{
cout << "~Time()" << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本類型(內(nèi)置類型)
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定義類型
Time _t;
};
int main()
{
Date d;
return 0;
}
main
方法中創(chuàng)建了Date
對(duì)象d
,而d
中包含4個(gè)成員變量,其中_year
、_month
、_day
、三個(gè)是內(nèi)置類型成員,對(duì)象銷毀時(shí)不需要資源清理,最后系統(tǒng)直接將其內(nèi)存回收即可,而_t
時(shí)Time
類對(duì)象,在d
銷毀時(shí),要將器內(nèi)部包含的Time
類的_t
對(duì)象銷毀,所以要去調(diào)用Time
類的析構(gòu)函數(shù)。但是main
函數(shù)中不能直接調(diào)用Time
類的析構(gòu)函數(shù),實(shí)際銷毀的是Date
類對(duì)象d
,所以編譯器會(huì)調(diào)用Date
類的析構(gòu)函數(shù),而Date
類沒(méi)有顯示提供,則編譯器會(huì)給Date
類生成一個(gè)默認(rèn)的析構(gòu)函數(shù),目的是在其內(nèi)部調(diào)用Time
類的析構(gòu)函數(shù)。
??總結(jié)
一般情況下,有動(dòng)態(tài)申請(qǐng)資源,就需要顯式的寫(xiě)析構(gòu)函數(shù)來(lái)釋放資源,沒(méi)有動(dòng)態(tài)申請(qǐng)的資源,可以不寫(xiě)析構(gòu)函數(shù),需要釋放資源的成員都是自定義類型,也不需要寫(xiě)析構(gòu)函數(shù)。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-596120.html
??結(jié)語(yǔ):
?今天的分享到這里就結(jié)束啦!如果覺(jué)得文章還不錯(cuò)的話,可以三連支持一下,您的支持就是春人前進(jìn)的動(dòng)力!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-596120.html
到了這里,關(guān)于【C++雜貨鋪】構(gòu)造函數(shù)和析構(gòu)函數(shù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!