即使走的再遠(yuǎn),也勿忘啟程時(shí)的初心
C/C++ 游戲開發(fā)
Hello,米娜桑們,這里是君兮_,我之前看過一套書叫做《明朝那些事兒》,把本來枯燥的歷史講的生動(dòng)有趣。而C++作為一門接近底層的語言,無疑是抽象且難度頗深的。我希望能努力把抽象繁多的知識(shí)講的生動(dòng)又通俗易懂,因此,咱們這個(gè)講解C++的系列博客就叫做《C++那些事兒》啦,有了之前的知識(shí),今天我們來真正走進(jìn)C++的核心知識(shí)部分——類與對(duì)象
-
好了廢話不多說,開始我們今天的學(xué)習(xí)吧!!
this指針
- 我們先來看下面這段代碼
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, d2;
d1.Init(2022,1,11);
d2.Init(2022, 1, 12);
d1.Print();
d2.Print();
return 0;
}
- 對(duì)于上述類,我們有這樣的一個(gè)問題:
- Date類中有 Init 與 Print 兩個(gè)成員函數(shù),函數(shù)體中沒有關(guān)于不同對(duì)象的區(qū)分,我們之前講過,實(shí)際上,在類中成員函數(shù)是在公共代碼區(qū)的,不會(huì)在每次定義對(duì)象時(shí)就重新定義,那當(dāng)d1調(diào)用 Init 函數(shù)時(shí),該函數(shù)是如何知道應(yīng)該設(shè)置d1對(duì)象,而不是設(shè)置d2對(duì)象呢?
- 為了解決這個(gè)問題,C++中引入了this指針。
即:C++編譯器給每個(gè)“非靜態(tài)的成員函數(shù)“增加了一個(gè)隱藏的指針參數(shù),讓該指針指向當(dāng)前對(duì)象(函數(shù)運(yùn)行時(shí)調(diào)用該函數(shù)的對(duì)象),在函數(shù)體中所有“成員變量”的操作,都是通過該指針去訪問。只不過所有的操作對(duì)用戶是透明的,即用戶不需要來傳遞,編譯器自動(dòng)完成
this指針的特性
- 1. this指針的類型:類類型 const,即成員函數(shù)中,不能給this指針賦值。*
- 2. 只能在“成員函數(shù)”的內(nèi)部使用
-
- this指針本質(zhì)上是“成員函數(shù)”的形參,當(dāng)對(duì)象調(diào)用成員函數(shù)時(shí),將對(duì)象地址作為實(shí)參傳遞給this形參。所以對(duì)象中不存儲(chǔ)this指針。
- 4. this指針是“成員函數(shù)”第一個(gè)隱含的指針形參,一般情況由編譯器通過ecx寄存器自動(dòng)傳遞,不需要用戶傳遞
在調(diào)用成員函數(shù)或者某些特殊情況時(shí),我們還會(huì)顯示的使用this指針,等具體遇到了我們?cè)俜治?/p>
類的默認(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ù)
- 假設(shè)有以下這個(gè)Date日期類
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.Init(2022, 7, 5);
d1.Print();
Date d2;
d2.Init(2022, 7, 6);
d2.Print();
return 0;
}
- 對(duì)于Date類,可以通過 Init 公有方法給對(duì)象設(shè)置日期,但如果每次創(chuàng)建對(duì)象時(shí)都必須調(diào)用該方法設(shè)置信息對(duì)對(duì)象初始化,未免有點(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ù)的特性
構(gòu)造函數(shù)是特殊的成員函數(shù),需要注意的是,構(gòu)造函數(shù)雖然名稱叫構(gòu)造,但是構(gòu)造函數(shù)的主要任務(wù)并不是開空間創(chuàng)建對(duì)象,而是初始化對(duì)象。
- 其特征如下:
1. 函數(shù)名與類名相同。
2. 無返回值。
3. 對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)。
4. 構(gòu)造函數(shù)可以重載
// 1.無參構(gòu)造函數(shù)
Date()
{}
// 2.帶參構(gòu)造函數(shù)
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
int main()
{
Date d1; // 調(diào)用無參構(gòu)造函數(shù)
Date d2(2023, 1, 1); // 調(diào)用帶參的構(gòu)造函數(shù)
}
5. 如果類中沒有顯式定義構(gòu)造函數(shù),則C++編譯器會(huì)自動(dòng)生成一個(gè)無參的默認(rèn)構(gòu)造函數(shù),一旦用戶顯式定義編譯器將不再生成
- 但是我們需要注意這樣一個(gè)地方:
class Date
{
public:
// 如果用戶顯式定義了構(gòu)造函數(shù),編譯器將不再生成默認(rèn)構(gòu)造函數(shù)
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
}
- 這里我們一旦顯示實(shí)現(xiàn)了構(gòu)造函數(shù),編譯器就不會(huì)實(shí)現(xiàn)默認(rèn)的無參構(gòu)造函數(shù)了,此時(shí)上面這樣定義一個(gè)對(duì)象就會(huì)找不到對(duì)應(yīng)的默認(rèn)構(gòu)造函數(shù)而報(bào)錯(cuò),因此當(dāng)顯示實(shí)現(xiàn)的時(shí)候,遇到這種情況必須把無參構(gòu)造函數(shù)也實(shí)現(xiàn)一遍。
- 關(guān)于編譯器生成的默認(rèn)成員函數(shù),很多童鞋會(huì)有疑惑:不實(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等自己定義的類型,看看下面的程序,就會(huì)發(fā)現(xiàn)編譯器生成默認(rèn)的構(gòu)造函數(shù)會(huì)對(duì)自定類型成員_t調(diào)用的它的默認(rèn)成員函數(shù)
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;
}
-
也就是說,對(duì)內(nèi)置類型的成員雀氏沒什么用,但是如果是自定義類型的成員,就會(huì)進(jìn)入該自定義類型中調(diào)用其構(gòu)造函數(shù),這點(diǎn)是非常重要的!!
-
這里我們的C++祖師爺就有點(diǎn)雙標(biāo)了。同樣都是一個(gè)類的成員,憑什么自定義類型成員在生成默認(rèn)構(gòu)造函數(shù)時(shí)會(huì)調(diào)用自定義成員的構(gòu)造函數(shù),而內(nèi)置類型的成員卻是隨機(jī)值
-
因此C++11 中針對(duì)內(nèi)置類型成員不初始化的缺陷,又打了補(bǔ)丁,即:內(nèi)置類型成員變量在類中聲明時(shí)可以給默認(rèn)值
class Date
{
private:
// 基本類型(內(nèi)置類型)
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定義類型
Time _t;
};
- 注意:這里需要知道,這里仍然是申明而不是定義,即使給了默認(rèn)值此時(shí)不定義一個(gè)該類的對(duì)象這些內(nèi)置類型的成員仍然沒有開辟空間
- 無參的構(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ù)
class Date
{
public:
Date()
{
_year = 2023;
_month = 1;
_day = 1;
}
Date(int year = 2023, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//上面兩個(gè)只能有一個(gè)作為默認(rèn)構(gòu)造函數(shù)
private:
int _year;
int _month;
int _day;
};
- 這倆是不能同時(shí)存在的??!
析構(gòu)函數(shù)
我們通過前面對(duì)構(gòu)造函數(shù)的學(xué)習(xí)知道一個(gè)對(duì)象是怎么來的,但是當(dāng)我們使用完成后,又該怎么銷毀這個(gè)對(duì)象呢?這就涉及到析構(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ù)的真正作用。
- 如果與之前我們學(xué)過的棧等數(shù)據(jù)結(jié)構(gòu)類比的話,它就類似于一個(gè)在不使用棧后銷毀棧的函數(shù)。
特性:
- 析構(gòu)函數(shù)是特殊的成員函數(shù),其特征如下:
1. 析構(gòu)函數(shù)名是在類名前加上字符 ~。
2. 無參數(shù)無返回值類型。
3. 一個(gè)類只能有一個(gè)析構(gòu)函數(shù)。若未顯式定義,系統(tǒng)會(huì)自動(dòng)生成默認(rèn)的析構(gòu)函數(shù)。
注意:析構(gòu)函數(shù)不能重載
4. 對(duì)象生命周期結(jié)束時(shí),C++編譯系統(tǒng)系統(tǒng)自動(dòng)調(diào)用析構(gòu)函數(shù)
- 我們拿一個(gè)棧的代碼來舉例
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;
}
~Stack()
{
if (_array)
{
free(_array);
_array = NULL;
_capacity = 0;
_size = 0;
}
}
private:
DataType* _array;
int _capacity;
int _size;
};
5. 關(guān)于編譯器自動(dòng)生成的析構(gòu)函數(shù),與構(gòu)造函數(shù)類似,編譯器生成的默認(rèn)析構(gòu)函數(shù),會(huì)對(duì)類中的自定類型成員調(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;
}
總結(jié)
- 好啦,我們今天的內(nèi)容就先到這里啦!從構(gòu)造函數(shù)開始,就是類和對(duì)象里真正的重點(diǎn)了,希望大家能夠好好理解,在今后所有對(duì)類的使用中都離不開今天講的這些默認(rèn)生成的成員函數(shù),當(dāng)然由于篇幅的原因,剩下的幾個(gè)我們放到第三篇再講。
- 有任何的問題和對(duì)文章內(nèi)容的疑惑歡迎在評(píng)論區(qū)中提出,當(dāng)然也可以私信我,我會(huì)在第一時(shí)間回復(fù)的!!
新人博主創(chuàng)作不易,如果感覺文章內(nèi)容對(duì)你有所幫助的話不妨三連一下再走唄。你們的支持就是我更新的動(dòng)力?。?!
**(可莉請(qǐng)求你們?nèi)B支持一下博主?。?!點(diǎn)擊下方評(píng)論點(diǎn)贊收藏幫幫可莉吧)** 文章來源:http://www.zghlxwxcb.cn/news/detail-757435.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-757435.html
到了這里,關(guān)于【C++那些事兒】類與對(duì)象(2)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!