前言
??個(gè)人主頁:@小沈YO.
??小編介紹:歡迎來到我的亂七八糟小星球??
??專欄:C++ 心愿便利店
??本章內(nèi)容:類和對(duì)象
記得 評(píng)論?? +點(diǎn)贊?? +收藏?? +關(guān)注??哦~
提示:以下是本篇文章正文內(nèi)容,下面案例可供參考
一、類的6個(gè)默認(rèn)成員函數(shù)
如果一個(gè)類中什么成員都沒有,簡(jiǎn)稱為空類。
空類中真的什么都沒有嗎?并不是,任何類在什么都不寫時(shí),編譯器會(huì)自動(dòng)生成以下6個(gè)默認(rèn)成員函數(shù)。
默認(rèn)成員函數(shù):用戶沒有顯式實(shí)現(xiàn),編譯器會(huì)生成的成員函數(shù)稱為默認(rèn)成員函數(shù)
二、構(gòu)造函數(shù)
如下Date類,沒有初始化打印出來就會(huì)是隨機(jī)值,同時(shí)對(duì)于棧沒有初始化,就會(huì)報(bào)錯(cuò)
那如果想能否在創(chuàng)建對(duì)象的同時(shí),就將信息設(shè)置進(jìn)去呢。因此,就有了構(gòu)造函數(shù)。以Date類為例:
#include<iostream>
using namespace std;
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;
d1.Print();//沒有調(diào)用Init初始化函數(shù)
Date d2;
d2.Init(2022, 7, 6);//調(diào)用Init初始化函數(shù)
d2.Print();
return 0;
}
對(duì)于Date類,可以通過 Init 公有方法給對(duì)象設(shè)置日期,但如果每次創(chuàng)建對(duì)象時(shí)都調(diào)用該方法設(shè)置信息,未免有點(diǎn)麻煩,那能否在對(duì)象創(chuàng)建時(shí),就將信息設(shè)置進(jìn)去呢?
??構(gòu)造函數(shù)是一個(gè)特殊的成員函數(shù),名字與類名相同, 創(chuàng)建類類型對(duì)象時(shí)由編譯器自動(dòng)調(diào)用,以保證每個(gè)數(shù)據(jù)成員都有 一個(gè)合適的初始值,并且在對(duì)象整個(gè)生命周期內(nèi)只調(diào)用一次。
構(gòu)造函數(shù)是特殊的成員函數(shù),需要注意的是,構(gòu)造函數(shù)雖然名稱叫構(gòu)造,但是構(gòu)造函數(shù)的主要任務(wù)并不是開空間創(chuàng)建對(duì)象,而是初始化對(duì)象。
其特征如下:
- ??函數(shù)名與類名相同。
- ??無返回值。(不需要寫void)
- ??對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)。
對(duì)于上述代碼所運(yùn)行后的結(jié)果沒有初始化d1結(jié)果是隨機(jī)值,然后對(duì)比下述代碼(同樣沒有初始化d1)及結(jié)果;運(yùn)行結(jié)果自動(dòng)初始化為1/1/1不是隨機(jī)值且打印了Date(),這就說明對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)
#include<iostream>
using namespace std;
class Date
{
public:
構(gòu)造函數(shù)
Date()
函數(shù)名與類名相同
且無返回值
{
cout << "Date()" << endl;
_year = 1;
_month = 1;
_day = 1;
}
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;
d1.Print();
return 0;
}
- ??構(gòu)造函數(shù)可以重載。(本質(zhì)可以寫多個(gè)構(gòu)造函數(shù),提供多種初始化方式)
#include<iostream>
using namespace std;
class Date
{
public:
1. 無參的構(gòu)造函數(shù)
Date()
{}
也可以寫成下面這種
Date()//函數(shù)名與類名相同且無返回值
{
cout << "Date()" << endl;
_year = 1;
_month = 1;
_day = 1;
}
2. 有參的構(gòu)造函數(shù)
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
3. 全缺省的構(gòu)造函數(shù)
Date(int year=1, int month=1, int day=1)
無參和全缺省的不能同時(shí)存在會(huì)存在調(diào)用歧義
{
_year = year;
_month = month;
_day = day;
}
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;調(diào)用無參的構(gòu)造函數(shù)
//Date func();
這也可以是一個(gè)函數(shù)聲明所以為了區(qū)分不能加()
d1.Print();
Date d2(2023,8,28);調(diào)用帶參的構(gòu)造函數(shù)
d2.Print();
對(duì)于全缺省的構(gòu)造函數(shù)使用更靈活可以傳一個(gè)參數(shù),兩個(gè)等
Date d3(2023);
d3.Print();
Date d4(2023, 8);
d4.Print();
return 0;
}
??注意:如果通過無參構(gòu)造函數(shù)創(chuàng)建對(duì)象時(shí),對(duì)象后面不用跟括號(hào)(例如Date d1() 是錯(cuò)誤的 ),否則就成了函數(shù)聲明以下代碼的函數(shù):聲明了d1函數(shù),該函數(shù)無參,返回一個(gè)日期類型的對(duì)象warning C4930: “Date d1(void)”: 未調(diào)用原型函數(shù)(是否是有意用變量定義的?)
- ??如果類中沒有顯式定義構(gòu)造函數(shù),則C++編譯器會(huì)自動(dòng)生成一個(gè)無參的默認(rèn)構(gòu)造函數(shù),一旦用戶顯式定義編譯器將不再生成。
class Date
{
public:
/*
// 如果用戶顯式定義了構(gòu)造函數(shù),編譯器將不再生成
Date(int year, int month, int day)(有參的構(gòu)造函數(shù))
{
_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ù)注釋后,代碼可以通過編譯,因?yàn)榫幾g器生成了一個(gè)無參的默認(rèn)構(gòu)造函數(shù);將Date類中構(gòu)造函數(shù)放開,代碼編譯失敗,因?yàn)橐坏╋@式定義任何構(gòu)造函數(shù),編譯器將不再生成無參構(gòu)造函數(shù),放開后報(bào)錯(cuò):error C2512: “Date”: 沒有合適的默認(rèn)構(gòu)造函數(shù)可用
- ??不實(shí)現(xiàn)構(gòu)造函數(shù)的情況下,編譯器會(huì)生成默認(rèn)的構(gòu)造函數(shù)。但是看起來默認(rèn)構(gòu)造函數(shù)又沒什么用?d對(duì)象調(diào)用了編譯器生成的默認(rèn)構(gòu)造函數(shù),但是d對(duì)象_year/_month/_day,依舊是隨機(jī)值。也就說在這里編譯器生成的默認(rèn)構(gòu)造函數(shù)并沒有什么用??
??解答:C++把類型分成內(nèi)置類型(基本類型)和自定義類型。內(nèi)置類型就是語言提供的數(shù)據(jù)類型,如:int/char…,自定義類型就是我們使用class/struct/union等自己定義的類型,所有類型的指針都是內(nèi)置類型
#include<iostream>
using namespace std;
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;
int _month;
int _day;
// 自定義類型
Time _t;
};
int main()
{
Date d;
return 0;
}
??注意:C++11 中針對(duì)內(nèi)置類型成員不初始化的缺陷,又打了補(bǔ)丁,即:內(nèi)置類型成員變量在類中聲明時(shí)可以給默認(rèn)值。
#include<iostream>
using namespace std;
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)置類型)
這個(gè)地方不是初始化而是聲明,聲明給的缺省值,默認(rèn)生成的構(gòu)造函數(shù)就會(huì)用這個(gè)缺省值初始化
int _year = 2023;
int _month = 9;
int _day = 5;
// 自定義類型
Time _t;
};
int main()
{
Date d;
return 0;
}
無參的構(gòu)造函數(shù)和全缺省的構(gòu)造函數(shù)都稱為默認(rèn)構(gòu)造函數(shù),并且默認(rèn)構(gòu)造函數(shù)只能有一個(gè)。
注意:無參構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù)、我們沒寫編譯器默認(rèn)生成的構(gòu)造函數(shù),都可以認(rèn)為
是默認(rèn)構(gòu)造函數(shù)。它們都有一個(gè)共同的特點(diǎn):可以不用傳參。默認(rèn)構(gòu)造函數(shù)只能有一個(gè),前面兩個(gè),在語法上可以構(gòu)成函數(shù)重載,但是在無參調(diào)用的時(shí)候,會(huì)發(fā)生歧義,出現(xiàn)調(diào)用不明確。
注意:要把默認(rèn)構(gòu)造函數(shù)和默認(rèn)成員函數(shù)區(qū)分清楚,默認(rèn)成員函數(shù)是我們不寫編譯器會(huì)自動(dòng)生成的,默認(rèn)構(gòu)造函數(shù)是不需要傳參的構(gòu)造函數(shù)。編譯器生成的構(gòu)造函數(shù),既是默認(rèn)構(gòu)造函數(shù),同時(shí)也是默認(rèn)成員函數(shù)。
??為什么上述說內(nèi)置類型用的缺省值
#include<iostream>
using namespace std;
class Date
{
public:
Date()
{
這里_year沒有給值而_month _day給了值打印出來是2023/2/1所以聲明那給的是缺省值
_month = 2;
_day = 1;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year = 2023;
int _month = 9;
int _day= 5;
};
int main()
{
Date d1;
d1.Print();
return 0;
}
??編譯器生成的默認(rèn)構(gòu)造的特點(diǎn):
- 不寫才會(huì)生成,寫了任意一個(gè)構(gòu)造函數(shù)就不會(huì)生成了
- 內(nèi)置類型也叫基本類型(語言用關(guān)鍵字定義的類型);內(nèi)置類型的成員不會(huì)處理(根據(jù)編譯器有的會(huì)有的不會(huì))
- 自定義類型的成員才會(huì)處理,會(huì)去調(diào)用這個(gè)成員的構(gòu)造函數(shù)
??總結(jié):
一般情況下,都需要我們自己寫構(gòu)造函數(shù),決定初始化方式;成員變量全是自定義類型,可以考慮不寫構(gòu)造函數(shù)
- ??無參的構(gòu)造函數(shù)和全缺省的構(gòu)造函數(shù)都稱為默認(rèn)構(gòu)造函數(shù),并且默認(rèn)構(gòu)造函數(shù)只能有一個(gè)多個(gè)并存在會(huì)存在調(diào)用的二義性。注意:無參構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù)、我們沒寫編譯器默認(rèn)生成的構(gòu)造函數(shù),都可以認(rèn)為是默認(rèn)構(gòu)造函數(shù)(不傳參就可以默認(rèn)調(diào)用的)。
三、析構(gòu)函數(shù)
析構(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ù)是特殊的成員函數(shù),其特征如下:
- ??析構(gòu)函數(shù)名是在類名前加上字符 ~。
- ??無參數(shù)無返回值類型。
- ??一個(gè)類只能有一個(gè)析構(gòu)函數(shù)。若未顯式定義,系統(tǒng)會(huì)自動(dòng)生成默認(rèn)的析構(gòu)函數(shù)。注意:析構(gòu)函數(shù)不能重載
- ??對(duì)象生命周期結(jié)束時(shí),C++編譯系統(tǒng)系統(tǒng)自動(dòng)調(diào)用析構(gòu)函數(shù)
溫馨提示:析構(gòu)函數(shù)不能重載。
??后定義先析構(gòu)
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
~Date()
{
cout << "Date()" << endl;
}
private:
int _year;
int _month;
int _day;
};
class Stack
{
public:
Stack(size_t n=4)
{
cout << "Stack(size_t n=4)" << endl;
if (n == 0)
{
a = nullptr;
top = capacity = 0;
}
else
{
a = (int*)malloc(sizeof(int) * n);
if (a == nullptr)
{
perror("realloc fail");
exit(-1);
}
top = 0;
capacity = n;
}
}
void Init()
{
a = nullptr;
top = capacity = 0;
}
void Push(int x)
{
if (top == capacity)
{
size_t newcapacity = capacity == 0 ? 4 : capacity * 2;
int*tmp = (int*)realloc(a,sizeof(int) * newcapacity);
if (tmp == nullptr)
{
perror("realloc fail");
exit(-1);
}
if (tmp == a)
{
cout << capacity << "原地?cái)U(kuò)容" << endl;
}
else
{
cout << capacity << "異地?cái)U(kuò)容" << endl;
}
a = tmp;
capacity = newcapacity;
}
a[top++] = x;
}
~Stack()
{
cout << "~Stack()" << endl;
free(a);
a = nullptr;
top = capacity = 0;
}
int Top()
{
return a[top - 1];
}
void Pop()
{
assert(top > 0);
--top;
}
void Destroy()
{
free(a);
a = nullptr;
top = capacity = 0;
}
bool Empty()
{
return top == 0;
}
private:
int* a;
int top;
int capacity;
};
int main()
{
Date d1;
Stack st1;
Stack st2;//后定義的先析構(gòu)
return 0;
}
Stack中的成員變量a、capacity、top都是內(nèi)置類型,對(duì)象st1生命周期結(jié)束要銷毀的時(shí)候,a和capacity和top是在棧上不需要資源清理,最后由系統(tǒng)將其內(nèi)存回收,而a指向的空間是在堆區(qū)上申請(qǐng)的,這塊空間不會(huì)隨著對(duì)象生命周期的結(jié)束而自動(dòng)釋放,所以會(huì)造成內(nèi)存泄漏,因此在對(duì)象銷毀前,要通過析構(gòu)函數(shù)去釋放成員變量a指向的空間,這就是析構(gòu)函數(shù)的作用。
- ?? 關(guān)于編譯器自動(dòng)生成的析構(gòu)函數(shù),是否會(huì)完成一些事情呢?
下面的程序我們會(huì)看到,編譯器生成的默認(rèn)析構(gòu)函數(shù),對(duì)自定類型成員調(diào)用它的析構(gòu)函數(shù)文章來源:http://www.zghlxwxcb.cn/news/detail-704443.html
#include<iostream>
using namespace std;
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;
}
程序運(yùn)行結(jié)束后輸出:~Time()
在main方法中根本沒有直接創(chuàng)建Time類的對(duì)象,為什么最后會(huì)調(diào)用Time類的析構(gòu)函數(shù)?
因?yàn)椋簃ain方法中創(chuàng)建了Date對(duì)象d,而d中包含4個(gè)成員變量,其中_year, _month, _day三個(gè)是內(nèi)置類型成員,銷毀時(shí)不需要資源清理,最后系統(tǒng)直接將其內(nèi)存回收即可;而_t是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ì)象,所以編譯器會(huì)調(diào)用Date類的析構(gòu)函數(shù),而Date沒有顯式提供,則編譯器會(huì)給Date類生成一個(gè)默認(rèn)的析構(gòu)函數(shù),目的是在其內(nèi)部調(diào)用Time類的析構(gòu)函數(shù),即當(dāng)Date對(duì)象銷毀時(shí),要保證其內(nèi)部每個(gè)自定義對(duì)象都可以正確銷毀main函數(shù)中并沒有直接調(diào)用Time類析構(gòu)函數(shù),而是顯式調(diào)用編譯器為Date類生成的默認(rèn)析構(gòu)函數(shù)
注意:創(chuàng)建哪個(gè)類的對(duì)象則調(diào)用該類的析構(gòu)函數(shù),銷毀那個(gè)類的對(duì)象則調(diào)用該類的析構(gòu)函數(shù);如果類中沒有申請(qǐng)資源時(shí)(在堆上申請(qǐng)空間),析構(gòu)函數(shù)可以不寫,直接使用編譯器生成的默認(rèn)析構(gòu)函數(shù),比如Date(日期)類;有資源申請(qǐng)時(shí),一定要寫,否則會(huì)造成內(nèi)存泄漏,比如Stack類文章來源地址http://www.zghlxwxcb.cn/news/detail-704443.html
到了這里,關(guān)于【C++心愿便利店】No.5---構(gòu)造函數(shù)和析構(gòu)函數(shù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!