03》類(lèi)和對(duì)象(中)——從零拆解六大默認(rèn)成員函數(shù)底層邏輯)
目錄一.什么是默認(rèn)成員函數(shù)1.1六大默認(rèn)成員函數(shù)1.2為什么需要默認(rèn)成員函數(shù)二.1構(gòu)造函數(shù)2.1概念與定義2.2默認(rèn)構(gòu)造函數(shù)的局限性2.3內(nèi)置與自定義類(lèi)型默認(rèn)行為二.2初始化列表與構(gòu)造函數(shù)三.析構(gòu)函數(shù)3.1析構(gòu)函數(shù)概念及定義3.2內(nèi)置類(lèi)型與動(dòng)態(tài)資源的析構(gòu)四.拷貝構(gòu)造函數(shù)4.1拷貝構(gòu)造函數(shù)的定義4.2深拷貝與淺拷貝五.賦值運(yùn)算符重載5.1運(yùn)算符重載operator5.2賦值運(yùn)算符重載六.取地址運(yùn)算符重載6.1const修飾對(duì)象6.2取地址重載相關(guān)專(zhuān)欄與源碼導(dǎo)航配套源碼 本專(zhuān)欄所有配套代碼已同步至 GitHub點(diǎn)擊直達(dá)系統(tǒng)專(zhuān)欄 《從零開(kāi)始學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)》專(zhuān)欄直達(dá)? 《C 深度解構(gòu)》專(zhuān)欄直達(dá)一.什么是默認(rèn)成員函數(shù)概念定義在我們沒(méi)有顯示實(shí)現(xiàn)編譯器默認(rèn)生成的成員函數(shù)1.1六大默認(rèn)成員函數(shù)構(gòu)造函數(shù)析構(gòu)函數(shù)拷貝構(gòu)造函數(shù)賦值重載函數(shù)取地址重載函數(shù)1.2為什么需要默認(rèn)成員函數(shù)在C語(yǔ)言中功能的實(shí)現(xiàn)必定離不開(kāi)初始化 \ 資源銷(xiāo)毀但是在操作中很容易忘記操作的實(shí)施但是在C中將對(duì)象的“初始化”和 “資源釋放”與生命周期強(qiáng)行綁定創(chuàng)造對(duì)象時(shí)自動(dòng)初始化構(gòu)造離開(kāi)作用域自動(dòng)銷(xiāo)毀析構(gòu)。內(nèi)置類(lèi)型的賦值非常明顯int a b, 對(duì)于類(lèi)類(lèi)型而言也需要支持對(duì)象之間的拷貝與賦值功能所以需要我們自定義一個(gè)函數(shù)來(lái)保證對(duì)象復(fù)制時(shí)的完整與安全。二.1構(gòu)造函數(shù)函數(shù)特性函數(shù)名與類(lèi)名一致無(wú)返回值對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用、支持重載功能定位構(gòu)造函數(shù)功能類(lèi)似于語(yǔ)言的 Init2.1概念與定義無(wú)參構(gòu)造函數(shù)全缺省構(gòu)造函數(shù)未顯式定義自動(dòng)生成的默認(rèn)構(gòu)造函數(shù)核心避坑 \colorbox{#E53935}{\color{#FFFFFF}\text{ 核心避坑 }}核心避坑?默認(rèn)構(gòu)造函數(shù)不僅僅只包含未顯示定義時(shí)編譯器默認(rèn)生成的構(gòu)造函數(shù)上面提到的三種統(tǒng)稱(chēng)為默認(rèn)構(gòu)造函數(shù)。歧義無(wú)參構(gòu)造函數(shù)和全缺省構(gòu)造函數(shù)不能同時(shí)存在因?yàn)槎嘉磦鲄⒕幾g器無(wú)法正確調(diào)用 \colorbox{#FFEAEA}{\color{#C22828}\text{歧義無(wú)參構(gòu)造函數(shù)和全缺省構(gòu)造函數(shù)不能同時(shí)存在因?yàn)槎嘉磦鲄⒕幾g器無(wú)法正確調(diào)用}}歧義無(wú)參構(gòu)造函數(shù)和全缺省構(gòu)造函數(shù)不能同時(shí)存在因?yàn)槎嘉磦鲄⒕幾g器無(wú)法正確調(diào)用?錯(cuò)誤示例對(duì)比Data(intyear2022,intmonth10,intday9);// 全缺省Data();// 無(wú)參Data d1;// ? 語(yǔ)法允許兩者同時(shí)存在但實(shí)例化時(shí)不傳參會(huì)引發(fā)調(diào)用歧義編譯報(bào)錯(cuò)2.2默認(rèn)構(gòu)造函數(shù)的局限性常見(jiàn)情形大多數(shù)情況下如果不顯式定義構(gòu)造函數(shù)編譯器自動(dòng)生成的構(gòu)造函數(shù)無(wú)法滿(mǎn)足我們資源初始化的要求特殊情形經(jīng)典案例兩個(gè)棧實(shí)現(xiàn)隊(duì)列 \colorbox{#FFEAEA}{\color{#C22828}\text{經(jīng)典案例兩個(gè)棧實(shí)現(xiàn)隊(duì)列}}經(jīng)典案例兩個(gè)棧實(shí)現(xiàn)隊(duì)列?在模擬隊(duì)列Queue類(lèi)中含有兩個(gè)成員變量Stack對(duì)象這里的Stack為自定義類(lèi)型。實(shí)例化Queue時(shí)因?yàn)槌蓡T變量為自定義類(lèi)型所以編譯器生成的默認(rèn)構(gòu)造函數(shù)會(huì)自動(dòng)調(diào)用Stack內(nèi)部的默認(rèn)構(gòu)造函數(shù)來(lái)進(jìn)行初始化所以這種情況我們無(wú)需再給出構(gòu)造函數(shù)2.3內(nèi)置與自定義類(lèi)型默認(rèn)行為內(nèi)置類(lèi)型int, double, 指針等如果未顯示實(shí)現(xiàn)構(gòu)造函數(shù)那么編譯器會(huì)不做處理變量呈現(xiàn)的值為隨機(jī)值并非編譯器主動(dòng)賦予而是直接忽略了該內(nèi)存塊的初始化內(nèi)存并非為空所以讀取到了之前的垃圾值自定義類(lèi)型若自定義類(lèi)型成員含有對(duì)象那么不顯示實(shí)現(xiàn)構(gòu)造函數(shù)會(huì)調(diào)用對(duì)象內(nèi)部默認(rèn)構(gòu)造函數(shù)。如果給了含參構(gòu)造函數(shù)但是沒(méi)有傳入?yún)?shù)或未指定缺省值那么編譯器會(huì)因?yàn)檎也坏綐?gòu)造函數(shù)而報(bào)錯(cuò) \colorbox{#FFEAEA}{\color{#C22828}\text{ 如果給了含參構(gòu)造函數(shù)但是沒(méi)有傳入?yún)?shù)或未指定缺省值那么編譯器會(huì)因?yàn)檎也坏綐?gòu)造函數(shù)而報(bào)錯(cuò)}}如果給了含參構(gòu)造函數(shù)但是沒(méi)有傳入?yún)?shù)或未指定缺省值那么編譯器會(huì)因?yàn)檎也坏綐?gòu)造函數(shù)而報(bào)錯(cuò)?//未顯式實(shí)現(xiàn)構(gòu)造函數(shù)classHy{public:voidprint(){cout_aendl;}private:int_a;};intmain(){Hy a1;a1.print();//打印結(jié)果未垃圾值return0;}二.2初始化列表與構(gòu)造函數(shù)初始化列表 \colorbox{#FFEAEA}{\color{#C22828}\text{初始化列表}}初始化列表?在類(lèi)的定義中所有成員變量?jī)H僅是聲明而真正為所有成員變量分配內(nèi)存空間是在初始化列表中構(gòu)造函數(shù)是為被定義了的成員變量進(jìn)行初始化即所有成員變量都會(huì)進(jìn)入初始化列表中被定義。public:Data(intyear,intmonth,intday):_year(year)//??????此處被正式定義,_month(month),_day(day),_a((int*)malloc(12){}private:int_year;//??????這里僅僅是聲明還未被定義int_month;int_day;int*_a;初始化列表中初始化值可以是表達(dá)式也可以是單一變量初始化列表和構(gòu)造函數(shù)的區(qū)別 \colorbox{#FFEAEA}{\color{#C22828}\text{初始化列表和構(gòu)造函數(shù)的區(qū)別}}初始化列表和構(gòu)造函數(shù)的區(qū)別?都具有初始化的功能因?yàn)槌蓡T變量都要進(jìn)入初始化列表被定義所以本質(zhì)上構(gòu)造函數(shù)花括號(hào)內(nèi)的初始化是二次賦值覆蓋初始化列表中同一成員變量只能出現(xiàn)一次變量不可重定義構(gòu)造函數(shù)中初始化可以多次出現(xiàn)覆蓋賦值核心避坑: \colorbox{#E53935}{\color{#FFFFFF}\text{ 核心避坑: }}核心避坑:?普通成員變量的初始化兩者都可以做到無(wú)差別但是三種特例無(wú)法只能使用初始化列表來(lái)完成初始化。const修飾被const修飾的成員變量具有常屬性只有在定義時(shí)能被初始化所以必須使用初始化列表引用變量引用變量必須在定義時(shí)就與被引用變量綁定必須初始化后續(xù)不可以改變綁定關(guān)系自定義類(lèi)型無(wú)默認(rèn)構(gòu)造函數(shù)成員變量中如果存在自定義類(lèi)型且給只顯式定義了帶參構(gòu)造函數(shù)編譯器不在生成無(wú)默認(rèn)構(gòu)造函數(shù)此時(shí)需要初始化列表來(lái)給自定義類(lèi)型成員傳入構(gòu)造函數(shù)形參classTime{public:Time(inttime){_timetime;}private:int_time;};//利用初始化列表為自定義類(lèi)型成員傳入?yún)?shù)Data(intyear,intmonth,intday):_year(year),_month(month),_day(day),_t(1)//?{}?? 注意:能使用初始化列表盡量都使用構(gòu)造函數(shù)會(huì)存在兩次操作定義 二次賦值初始化列表只有一次定義在實(shí)際使用中初始化列表中與構(gòu)造函數(shù)相輔相成并不存在對(duì)立關(guān)系初始化列表只能完成簡(jiǎn)單賦值綁定但是復(fù)雜對(duì)象的初始化還需要判斷及校驗(yàn)C11改變 \colorbox{#FFEAEA}{\color{#C22828}\text{C11改變}}C11改變?C98中規(guī)定聲明的成員變量不可以給值但是C11中可以提供缺省值不在初始化列表中初始化的成員使用缺省值初始化??偨Y(jié) \colorbox{#FFEAEA}{\color{#C22828}\text{總結(jié)}}總結(jié)?1.所有成員變量都會(huì)進(jìn)入初始化列表2.在初始化列表中初始化的成員直接使用傳入形參3.不在初始化列表中初始化的成員a.在聲明位置有缺省值使用缺省值b.沒(méi)有缺省值x.內(nèi)置類(lèi)型成員編譯器不做處理隨機(jī)值y.自定義類(lèi)型成員自動(dòng)調(diào)用其默認(rèn)構(gòu)造函數(shù)無(wú)默認(rèn)構(gòu)造函數(shù)則報(bào)錯(cuò)三.析構(gòu)函數(shù)功能定位析構(gòu)函數(shù)類(lèi)似于C語(yǔ)言中的Destroy函數(shù)特性沒(méi)有返回值函數(shù)名也為類(lèi)名對(duì)象生命周期結(jié)束時(shí)自動(dòng)調(diào)用不支持重載3.1析構(gòu)函數(shù)概念及定義//析構(gòu)函數(shù)classStack{public://構(gòu)造函數(shù)Stack(intn4){_a(int*)malloc(sizeof(int)*n);_top0;_capacityn;}//析構(gòu)函數(shù)~Stack(){free(_a);//?將申請(qǐng)的空間資源進(jìn)行釋放_(tái)anullptr;}private:int*_a;int_top;int_capacity;};在對(duì)象生命周期結(jié)束前return 前編譯器自動(dòng)調(diào)用析構(gòu)函數(shù)完成已申請(qǐng)的資源釋放但并非銷(xiāo)毀對(duì)象本身析構(gòu)函數(shù)有且僅有一個(gè)所以不能重載 \colorbox{#FFEAEA}{\color{#C22828}\text{析構(gòu)函數(shù)有且僅有一個(gè)所以不能重載}}析構(gòu)函數(shù)有且僅有一個(gè)所以不能重載?3.2內(nèi)置類(lèi)型與動(dòng)態(tài)資源的析構(gòu)無(wú)資源申請(qǐng)時(shí)析構(gòu)函數(shù)僅對(duì)有資源申請(qǐng)的類(lèi)進(jìn)行操作對(duì)于單純的內(nèi)置類(lèi)型不做處理函數(shù)調(diào)用結(jié)束自動(dòng)釋放有資源申請(qǐng)時(shí)通過(guò)malloc或new動(dòng)態(tài)申請(qǐng)內(nèi)存的前提下如果不顯式實(shí)現(xiàn)析構(gòu)函數(shù)編譯器雖然會(huì)自動(dòng)生成析構(gòu)函數(shù)但是無(wú)法對(duì)mlloc或new申請(qǐng)的堆區(qū)空間進(jìn)行釋放會(huì)造成內(nèi)存泄漏。注意一個(gè)類(lèi)實(shí)例化出多個(gè)對(duì)象時(shí)析構(gòu)滿(mǎn)足棧的性質(zhì)先進(jìn)后出所以先構(gòu)造的后析構(gòu)、后構(gòu)造的先析構(gòu) \colorbox{#FFEAEA}{\color{#C22828}\text{注意一個(gè)類(lèi)實(shí)例化出多個(gè)對(duì)象時(shí)析構(gòu)滿(mǎn)足棧的性質(zhì)先進(jìn)后出所以先構(gòu)造的后析構(gòu)、后構(gòu)造的先析構(gòu)}}注意一個(gè)類(lèi)實(shí)例化出多個(gè)對(duì)象時(shí)析構(gòu)滿(mǎn)足棧的性質(zhì)先進(jìn)后出所以先構(gòu)造的后析構(gòu)、后構(gòu)造的先析構(gòu)?四.拷貝構(gòu)造函數(shù)函數(shù)特性無(wú)返回值函數(shù)名與類(lèi)名一致參數(shù)必須是傳引用傳值功能定位完成自定類(lèi)型的拷貝4.1拷貝構(gòu)造函數(shù)的定義定義用已經(jīng)實(shí)例化出來(lái)的對(duì)象去初始化另一個(gè)正在實(shí)例化的對(duì)象拷貝構(gòu)造函數(shù)僅針對(duì)于類(lèi)類(lèi)型成員所以第一個(gè)參數(shù)為類(lèi)類(lèi)型并且如果有其他額外參數(shù)都需要有默認(rèn)值即缺省值classData{public://構(gòu)造函數(shù)Data(intyear,intmonth,intday){_yearyear;_monthmonth;_dayday;}//拷貝構(gòu)造函數(shù)Data(constDatad)//思考為什么需要引用傳參{_yeard._year;_monthd._month;_dayd._day;}private:int_year;int_month;int_day;};?? 注意拷貝構(gòu)造函數(shù)的形參必須是傳引用傳參而不能是傳值傳參否則會(huì)造成死循環(huán) 核心避坑: \colorbox{#E53935}{\color{#FFFFFF}\text{ 核心避坑: }}核心避坑:?如果形參為傳值傳參則外部對(duì)象需先拷貝給形參d此時(shí)d相當(dāng)于實(shí)例化出來(lái)的另一個(gè)對(duì)象觸發(fā)拷貝構(gòu)造函數(shù)再次進(jìn)入形參d的拷貝又一次調(diào)用拷貝構(gòu)造函數(shù)無(wú)休止進(jìn)行。傳引用傳參不會(huì)造成拷貝所以可以直接進(jìn)入拷貝構(gòu)造函數(shù)內(nèi)進(jìn)行拷貝傳引用傳參本質(zhì)是為了減少拷貝次數(shù)但是如果作為返回值返回一定要確定變量是否還存在若出作用域被銷(xiāo)毀那么就相當(dāng)于野引用傳出值不可預(yù)測(cè)4.2深拷貝與淺拷貝編譯器默認(rèn)生成的淺拷貝值拷貝有相應(yīng)指針指向的空間也是直接拷貝會(huì)造成拷貝出的對(duì)象與被拷貝對(duì)象指向同一塊內(nèi)存深拷貝則是在面對(duì)存在有指針指向資源的情況時(shí)為拷貝出的函數(shù)重新創(chuàng)建一個(gè)指向空間而非與被拷貝空間指向同一塊地址。經(jīng)典案例棧 \colorbox{#FFEAEA}{\color{#C22828}\text{經(jīng)典案例棧}}經(jīng)典案例棧?//拷貝構(gòu)造函數(shù)Stack(constStacks){_a(int*)malloc(sizeof(int)*s._capacity);//淺拷貝只能值拷貝無(wú)法完成對(duì)象的指向關(guān)系_tops._top;_capacitys._capacity;//只需拷貝有效數(shù)據(jù)個(gè)數(shù)拷貝整個(gè)capacity會(huì)有空間還未利用memcpy(_a,s._a,sizeof(int)*s._top);}未顯式實(shí)現(xiàn)拷貝構(gòu)造函數(shù) \colorbox{#FFEAEA}{\color{#C22828}\text{未顯式實(shí)現(xiàn)拷貝構(gòu)造函數(shù)}}未顯式實(shí)現(xiàn)拷貝構(gòu)造函數(shù)?intmain(){Stack s1;Stack s2s1;//或者Stack s2 (s1);couts1._aendl;couts2._aendl;return0;}從圖中可以看出如果不顯式實(shí)現(xiàn)拷貝構(gòu)造函數(shù)那么s1中的_a和s2中的_a所指向的是同一塊空間地址相同。為什么會(huì)產(chǎn)生崩潰顯現(xiàn)呢核心避坑: \colorbox{#E53935}{\color{#FFFFFF}\text{ 核心避坑: }}核心避坑:?未顯式定義拷貝構(gòu)造函數(shù)類(lèi)內(nèi)成員變量指向同一塊空間在程序結(jié)束后自動(dòng)調(diào)用析構(gòu)函數(shù)會(huì)對(duì)同一塊空間進(jìn)行兩次資源釋放Double free五.賦值運(yùn)算符重載5.1運(yùn)算符重載operator函數(shù)特性有返回值函數(shù)名為operator加運(yùn)算符如void operator( ) 不能憑空創(chuàng)造運(yùn)算符等.* :: sizeof ?: .五大運(yùn)算符不可以重載運(yùn)算符操作對(duì)象最少有一個(gè)類(lèi)類(lèi)型內(nèi)置類(lèi)型無(wú)法進(jìn)行運(yùn)算符重載按照正常邏輯執(zhí)行重載 或 --運(yùn)算符時(shí)為了區(qū)分前置或后置定義重載函數(shù)時(shí)給后置參數(shù)多一個(gè)int變量(值無(wú)所謂只是為了區(qū)分)如operator()前置、operator(int)后置重載流運(yùn)算符 或時(shí)為了符合習(xí)慣通常定義在類(lèi)外否則寫(xiě)法為對(duì)象cout(不符合我們平時(shí)的習(xí)慣)核心避坑 \colorbox{#E53935}{\color{#FFFFFF}\text{ 核心避坑 }}核心避坑?如果運(yùn)算符重載的操作對(duì)象為兩個(gè)并且重載定義在類(lèi)內(nèi)則只需要傳入第二個(gè)參數(shù)第一個(gè)為隱式的this若定義在類(lèi)外則需要傳入兩個(gè)參數(shù)兩個(gè)操作對(duì)象的運(yùn)算符參數(shù)的傳遞是左操作對(duì)象傳第一個(gè)參數(shù)右操作對(duì)象傳第二個(gè)參數(shù)樣例示范 \colorbox{#FFEAEA}{\color{#C22828}\text{樣例示范}}樣例示范?通過(guò)重載運(yùn)算符來(lái)判斷兩個(gè)對(duì)象的日期是否相同當(dāng)然也可以實(shí)現(xiàn)大小比較日期相減減等操作流輸出、流提取運(yùn)算符重載 \colorbox{#FFEAEA}{\color{#C22828}\text{流輸出、流提取運(yùn)算符重載}}流輸出、流提取運(yùn)算符重載?在類(lèi)內(nèi)定義時(shí)第一個(gè)參數(shù)隱式傳遞this指針?biāo)灾恍枰獋魅雘stream類(lèi)型參數(shù)vs默認(rèn)做操作對(duì)象傳第一個(gè)參數(shù)第二個(gè)操作對(duì)象傳入第二個(gè)參數(shù)voidoperator(/*第一參數(shù)默認(rèn)傳入this指針*/ostreamout){out_yearendl;}Datad1(2026,8,25);d1cout;//?為滿(mǎn)足對(duì)象傳第一this參數(shù)右對(duì)象傳第二參數(shù)在類(lèi)外定義類(lèi)外可以自定義傳入?yún)?shù)順序則可以符合使用習(xí)慣將cout放在操作符前voidoperator(constDatad,ostreamout){outd._yearendl;}Datad1(2026,8,25);coutd1;5.2賦值運(yùn)算符重載上面講的運(yùn)算符重載是我們對(duì)于一個(gè)操作符進(jìn)行的自定義行為而對(duì)于賦值運(yùn)算符的重載是真正的默認(rèn)成員函數(shù)功能 \colorbox{#FFEAEA}{\color{#C22828}\text{功能}}功能?用一個(gè)已經(jīng)實(shí)例化成功的對(duì)象去賦值另一個(gè)已經(jīng)實(shí)例化的對(duì)象操作對(duì)象為兩個(gè)已經(jīng)實(shí)例化的對(duì)象。核心避坑 ?? \colorbox{#E53935}{\color{#FFFFFF}\text{ 核心避坑 ??}}核心避坑???拷貝構(gòu)造函數(shù)是用已經(jīng)實(shí)例化的對(duì)象去初始化正在實(shí)例化的對(duì)象操作對(duì)象是一個(gè)實(shí)例化完成的對(duì)象一個(gè)正在實(shí)例化的對(duì)象樣例棧 \colorbox{#FFEAEA}{\color{#C22828}\text{樣例棧}}樣例棧?voidoperator(constStackst){_a(int*)malloc(sizeof(int)*st._capacity);_topst._top;_capacityst._capacity;memcpy(_a,st._a,sizeof(int)*st._top);}如果不顯示實(shí)現(xiàn)賦值運(yùn)算符重載那么有資源申請(qǐng)的請(qǐng)款下同一塊內(nèi)存將會(huì)被兩個(gè)對(duì)象同時(shí)指向。賦值運(yùn)算符重載還支持連續(xù)賦值ru(st3 st2 st1),表達(dá)式st2 st1 賦值完成后左值st2作為返回值返回所以在賦值運(yùn)算符重載中可以將this指針解引用返回小技巧 \colorbox{#FFEAEA}{\color{#C22828}\text{小技巧}}小技巧?有析構(gòu)函數(shù)時(shí)大概率會(huì)使用拷貝構(gòu)造函數(shù)和賦值重載六.取地址運(yùn)算符重載6.1const修飾對(duì)象classData{public:Data(intyear):_year(year){}voidpinrt()const//??????const放在函數(shù)尾代表傳入?yún)?shù)為const類(lèi)型{cout_yearendl;}private:int_year;};//實(shí)例化一個(gè)const對(duì)象constd1(2026);d1.print();默認(rèn)隱式傳入的this指針為Data* this,而對(duì)象被const修飾后相對(duì)應(yīng)的指針類(lèi)型為const Data* 這里存在權(quán)限放大問(wèn)題。解決方法: \colorbox{#FFEAEA}{\color{#C22828}\text{解決方法:}}解決方法:?C規(guī)定不可以顯式提供this指針?biāo)詿o(wú)法在括號(hào)內(nèi)完成const修飾所以將const放在函數(shù)參數(shù)列表尾6.2取地址重載提醒 \colorbox{#E53935}{\color{#FFFFFF}\text{ 提醒}}提醒?編譯器默認(rèn)生成的取地址重載會(huì)直接返回對(duì)象的實(shí)際內(nèi)存地址即this指針99%情況下無(wú)需顯式實(shí)現(xiàn)。特例 \colorbox{#FFEAEA}{\color{#C22828}\text{特例}}特例?想讓其返回nullptr或者假地址時(shí)才需要顯式實(shí)現(xiàn)Data*operator(){returnnullptr;}“保持熱愛(ài)奔赴山海保持專(zhuān)注雕琢底層。”Crafted with passion byHhy當(dāng)前更新時(shí)間{{ 2026 / 8 / 25 }} | 此博文系博主學(xué)習(xí)復(fù)盤(pán)歡迎指正本文專(zhuān)欄由 Hhy 維護(hù)。如果這篇文章曾帶給你啟發(fā)請(qǐng)考慮關(guān)注我的 CSDN 博客或 GitHub 倉(cāng)庫(kù)以獲取最新技術(shù)分享。