[導(dǎo)讀] 。
前言 在上一則教程中,通過(guò)與 C 語(yǔ)言相比較引出了 C 的相關(guān)特性,其中就包括函數(shù)重載,引用,this 指針,以及在脫離 IDE 編寫(xiě) C 程序時(shí),所要用到的?Makefile
的相關(guān)語(yǔ)法。本節(jié)所要敘述的是?C
的另外兩個(gè)重要的特性,也就是構(gòu)造函數(shù)和析構(gòu)函數(shù)的相關(guān)內(nèi)容,這兩部分內(nèi)容也是有別于?c
語(yǔ)言而存在的,也是?c
的一個(gè)重要特性。構(gòu)造函數(shù) 類的構(gòu)造函數(shù)是類的一種特殊的成員函數(shù),它會(huì)在每次創(chuàng)建新的對(duì)象的時(shí)候執(zhí)行,構(gòu)造函數(shù)的名稱和類的名稱是完全相同的,并不會(huì)返回任何的類型,也不會(huì)返回 void。構(gòu)造函數(shù)可以用于為某些成員變量設(shè)置初始值。比方說(shuō),我們現(xiàn)在有如下所示的一段代碼:#include ? using ?namespace ?std ;class ?Person {private : ????char ?*name; ????int ?age; ????char ?*work;public : ????Person()?{cout ?<"Person()" ?<endl ;} };int ?main (int ?argc,?char ?**argv) { ????Person?per; ????return ?0 ; }
在主函數(shù)中,定義 Person per 的同時(shí),就會(huì)自動(dòng)地調(diào)用 Person() 函數(shù),那么不難猜出,執(zhí)行 test 文件 的時(shí)候,輸出結(jié)果如下: image-20210113124209248 上述 的構(gòu)造函數(shù)并沒(méi)有參數(shù),實(shí)際上在構(gòu)造函數(shù)是可以具有參數(shù)的,具體的看如下所示的代碼: #include ? using ?namespace ?std ;class ?Person {private : ????char ?*name; ????int ?age;public : ????Person(char ?*name,?int ?age) ????{ ????????cout ?<"Person(char?*,int)" ?<endl ; ????????this ->name?=?name; ????????this ->age?=?age; ????} ????Person(){cout ?<"Person()" ?<endl ;} };int ?main (int ?argc,?char ?**argv) { ????Person?per; ????Person?per2 ("zhangsan" ,18 ) ; ????return ?0 ; }
上述代碼中,定義第一個(gè) Person 實(shí)例的時(shí)候,就會(huì)自動(dòng)地調(diào)用無(wú)形參地構(gòu)造函數(shù),當(dāng)實(shí)例化第二個(gè) Person 類 的時(shí)候,就會(huì)自動(dòng)地調(diào)用有形參地構(gòu)造函數(shù)。 這個(gè)時(shí)候,運(yùn)行函數(shù) 的輸出結(jié)果如下所示: image-20210113125016221 可以看到調(diào)用構(gòu)造函數(shù)的順序是和實(shí)例化對(duì)象的順序是一致的。構(gòu)造函數(shù)除了可以有形參,也可以有默認(rèn)的形參,比如說(shuō)下面這段代碼:#include ? using ?namespace ?std ;class ?Person {private : ????char ?*name; ????int ?age;public : ????Person(char ?*name,?int ?age,?char ?*work?=?"none" ) ????{ ????????cout ?<"Person(char?*,int)" ?<endl ; ????????this ->name?=?name; ????????this ->age?=?age; ????????this ->work?=?work; ????} ????Person(){cout ?<"Person()" ?<endl ;} ????void ?printInfo (void ) ???? { ????????cout ?<"name?=" ?<",age?=?"<",work?="<endl; ????} };int ?main (int ?argc,?char ?**argv) { ????Person?per; ????Person?per2 ("zhangsan" ,18 ) ; ????Person?per3 () ; ????per2.printInfo(); ????return ?0 ; }
上述代碼中,第一條代碼和第二條代碼創(chuàng)建了兩個(gè) Person 實(shí)例,在創(chuàng)建時(shí)依次調(diào)用構(gòu)造函數(shù),這里需要注意的是,第三條語(yǔ)句,這條語(yǔ)句看起來(lái)像是實(shí)例化了一個(gè) per3 對(duì)象,但是 per3 括號(hào)里并沒(méi)有實(shí)參,這其實(shí)是定義了一個(gè)函數(shù),函數(shù)的形參為void
,返回值為 Person ,并非是一個(gè)對(duì)象。這里還需要注意的一點(diǎn)是 per2 對(duì)象,它在調(diào)用構(gòu)造函數(shù)時(shí),形參有一個(gè)默認(rèn)值,所以最終,程序輸出的結(jié)果如下所示:image-20210113131653000 在實(shí)例化對(duì)象的時(shí)候,我們也可以通過(guò)定義指針的形式實(shí)現(xiàn),下面代碼是上述代碼的一個(gè)改進(jìn),并且以指針的形式實(shí)例化了對(duì)象,代碼如下所示:#include ? #include ? using ?namespace ?std ;class ?Person {private : ????char ?*name; ????int ?age; ????char ?*work;public : ????Person(){cout ?<"person()" ?<endl ;} ????Person(char ?*name,int ?age,?char ?*work) ????{ ????????cout ?<"Person(char?*,int,?char?*)" ?<endl ; ????????this ->name?=?new ?char [strlen (name)? ?1 ]; ????????strcpy (this ->name,name); ????????this ->age?=?age; ????????this ->work?=?new ?char [strlen (work)? ?1 ]; ????????strcpy (this ->work,work); ????} ????void ?printInfo (void ) ???? { ????????cout ?<"name?is:" ?<",age?is:"?<",work?is:"?<endl; ????} };int ?main (int ?argc,char ?*argv) { ????Person?per ("zhangsan" ,18 ,"teacher" ) ; ????Person?per2; ????Person?*per4?=?new ?Person; ????Person?*per5?=?new ?Person();?/*?這兩種方式定義的效果是一樣的?*/ ????Person?*per6?=?new ?Person[2 ]; ????Person?*per7?=?new ?Person("lisi" ,?18 ,"doctor" ); ????per.printInfo(); ????per7.printInfo(); ????delete ?per4; ????delete ?per5; ????delete ?[]per6; ????delete ?per7; }
上述代碼中,使用了new 來(lái)分配給對(duì)象空間,再分配完之后,系統(tǒng)會(huì)自動(dòng)的進(jìn)行釋放,或者說(shuō)是使用手動(dòng)的方式進(jìn)行釋放內(nèi)存,在手動(dòng)釋放內(nèi)存的時(shí)候,我們采用?delete
?的方式來(lái)進(jìn)行釋放,當(dāng)創(chuàng)建了兩個(gè)指針數(shù)組的時(shí)候,在手動(dòng)釋放的時(shí)候,要在指針變量前面加上?[]
,在實(shí)例化指針對(duì)象的時(shí)候,也可以帶上參數(shù)或者說(shuō)是不帶參數(shù)。下面是上述代碼的運(yùn)行結(jié)果:image-20210114125841211 析構(gòu)函數(shù) 析構(gòu)函數(shù)的引出 上述我們知道,在函數(shù)運(yùn)行完之后,用 new 分配到的空間才會(huì)被釋放掉,那么如果是在函數(shù)調(diào)用里用 new 獲取到的空間會(huì)隨著函數(shù)調(diào)用的結(jié)束而釋放么,我們現(xiàn)在來(lái)做這樣一個(gè)實(shí)驗(yàn),把上述中的代碼中的主函數(shù)寫(xiě)成?test()
函數(shù),然后在?main()
?函數(shù)里調(diào)用。代碼如下所示:#include ? #include ? #include ? using ?namespace ?std ;class ?Person {private : ????char ?*name; ????int ?age; ????char ?*work;public : ????Person(){cout ?<"person()" ?<endl ;} ????Person(char ?*name,int ?age,?char ?*work) ????{ ????????cout ?<"Person(char?*,int,?char?*)" ?<endl ; ????????this ->name?=?new ?char [strlen (name)? ?1 ]; ????????strcpy (this ->name,name); ????????this ->age?=?age; ????????this ->work?=?new ?char [strlen (work)? ?1 ]; ????????strcpy (this ->work,work); ????} ????void ?printInfo (void ) ???? { ????????//cout?<"name?is:"?< ????} };void ?test (void ) { ????Person?per ("zhangsan" ,18 ,"teacher" ) ; ????Person?per2; ????Person?*per4?=?new ?Person; ????Person?*per5?=?new ?Person();?/*?這兩種方式定義的效果是一樣的?*/ ????Person?*per6?=?new ?Person[2 ]; ????Person?*per7?=?new ?Person("lisi" ,?18 ,"doctor" ); ????per.printInfo(); ????per7->printInfo(); ????delete ?per4; ????delete ?per5; ????delete ?[]per6; ????delete ?per7; }int ?main (int ?argc,?char ?**argv) { ????for ?(int ?i?=?0 ;?i?1000000 ;?i ) ????????test(); ????cout ?<"run?test?end" ?<endl ; ????sleep(10 ); ????return ?0 ; }
這是運(yùn)行前的空閑內(nèi)存的大?。?/p>image-20210114133025365 緊接著是函數(shù)運(yùn)行完 100 0000 次的?test
?函數(shù)之后的空閑內(nèi)存大小:image-20210114133140216 然后,是主函數(shù)運(yùn)行完之后,推出主函數(shù)之后,空閑的內(nèi)存剩余量:image-20210114133241325 總結(jié) 一下就是,在子函數(shù)里用 new 分配給局部變量的空間,具體來(lái)說(shuō)在上述代碼中的體現(xiàn)就是用? new
給? this->name
分配的空間。 也就是在主函數(shù)沒(méi)有運(yùn)行完是不會(huì)被釋放掉的,也就是說(shuō)只有在主函數(shù)運(yùn)行完之后,子函數(shù)里用 new 分配的空間才會(huì)被釋放掉,因此,如果想要在子函數(shù)調(diào)用完之后就釋放掉用 new 分配的空間,就需要編寫(xiě)代碼來(lái)實(shí)現(xiàn)。 而這個(gè)操作, C 提供了析構(gòu)函數(shù)來(lái)完成,下面是使用析構(gòu)函數(shù)來(lái)進(jìn)行釋放內(nèi)存的代碼: #include ? #include ? #include ? using ?namespace ?std ;class ?Person {private : ????char ?*name; ????int ?age; ????char ?*work;public : ????Person(){cout ?<"person()" ?<endl ;} ????Person(char ?*name,int ?age,?char ?*work) ????{ ????????cout ?<"Person(char?*,int,?char?*)" ?<endl ; ????????this ->name?=?new ?char [strlen (name)? ?1 ]; ????????strcpy (this ->name,name); ????????this ->age?=?age; ????????this ->work?=?new ?char [strlen (work)? ?1 ]; ????????strcpy (this ->work,work); ????} ????~Person() ????{ ????????if ?(this ->name) ????????????delete ?this ->name; ????????if ?(this ->work) ????????????delete ?this ->work; ????} ????void ?printInfo (void ) ???? { ????????//cout?<"name?is:"?< ????} };void ?test (void ) { ????Person?per ("zhangsan" ,18 ,"teacher" ) ; ????Person?per2; ????Person?*per4?=?new ?Person; ????Person?*per5?=?new ?Person();?/*?這兩種方式定義的效果是一樣的?*/ ????Person?*per6?=?new ?Person[2 ]; ????Person?*per7?=?new ?Person("lisi" ,?18 ,"doctor" ); ????per.printInfo(); ????per7->printInfo(); ????delete ?per4; ????delete ?per5; ????delete ?[]per6; ????delete ?per7; }int ?main (int ?argc,?char ?**argv) { ????for ?(int ?i?=?0 ;?i?1000000 ;?i ) ????????test(); ????cout ?<"run?test?end" ?<endl ; ????sleep(10 ); ????return ?0 ; }
下述就是代碼運(yùn)行之前,和主函數(shù)在休眠的時(shí)候的剩余內(nèi)存的容量,可以看出,剩余內(nèi)存的容量是一樣的,換句話說(shuō),也就是在?test()
函數(shù)運(yùn)行完成之后,用 new 分配的空間就已經(jīng)被釋放掉了,就算執(zhí)行了 1000000 次也沒(méi)有造成內(nèi)存泄漏。這也說(shuō)明了我們的析構(gòu)函數(shù)是有作用的。image-20210115130212394 析構(gòu)函數(shù)在什么地方被調(diào)用 上述析構(gòu)函數(shù)的存在避免了內(nèi)存泄漏,那么析構(gòu)函數(shù)是在什么時(shí)候被調(diào)用的呢,用一句話描述就是:在實(shí)例化對(duì)象被銷毀的前一瞬間被調(diào)用的 ,另外還要注意的是構(gòu)造函數(shù)可以有很多個(gè),有參的,無(wú)參的構(gòu)造函數(shù),但是對(duì)于析構(gòu)函數(shù)來(lái)講,它只有一個(gè),并且它是無(wú)參的。具體的來(lái)看如下所示的代碼,在剛才那段代碼的基礎(chǔ)上,我們添加一些打印信息,從而推斷我們析構(gòu)函數(shù)調(diào)用的位置:#include ? #include ? #include ? using ?namespace ?std ;class ?Person {private : ????char ?*name; ????int ?age; ????char ?*work;public : ????Person() ????{ ????????name?=?NULL ; ????????work?=?NULL ; ????} ????Person(char ?*name,int ?age,?char ?*work) ????{ ????????this ->name?=?new ?char [strlen (name)? ?1 ]; ????????strcpy (this ->name,name); ????????this ->age?=?age; ????????this ->work?=?new ?char [strlen (work)? ?1 ]; ????????strcpy (this ->work,work); ????} ????~Person() ????{ ????????cout ?<"~Person()" ?<endl ; ????????if ?(this ->name) ????????{ ????????????delete ?this ->name; ????????????cout ?<"The?name?is:" ?<endl;??? ????????} ????????if ?(this ->work) ????????{ ????????????delete ?this ->work; ????????????cout ?<"The?work?is:" ?<endl; ????????} ????} ????void ?printInfo (void ) ???? { ????????//cout?<"name?is:"?< ????} };void ?test (void ) { ????Person?per ("zhangsan" ,18 ,"teacher" ) ; ????Person?*per7?=?new ?Person("lisi" ,?18 ,"doctor" ); ????delete ?per7; }int ?main (int ?argc,?char ?**argv) { ????test(); ????return ?0 ; }
我們來(lái)看輸出的結(jié)果:image-20210115132418481 通過(guò)上面的輸出結(jié)果可以知道,先輸出的是lisi
,后輸出的是?zhangsan
,而在實(shí)例化對(duì)象的時(shí)候,是先創(chuàng)建的 per 對(duì)象,并初始化為?zhangsan
,后創(chuàng)建的 per7 對(duì)象,并初始化為?lisi
,再調(diào)用析構(gòu)函數(shù)的時(shí)候順序卻是顛倒過(guò)來(lái)的。因此,總結(jié) 一下就是: per 這個(gè)實(shí)例化對(duì)象是在?test()
函數(shù)執(zhí)行完之后,再調(diào)用的析構(gòu)函數(shù),而對(duì)于?per7
對(duì)象來(lái)說(shuō),是在執(zhí)行?delete per7
這條語(yǔ)句之后調(diào)用的析構(gòu)函數(shù),所以也就有了上述的輸出結(jié)果。另外,引出一點(diǎn),如果我們?cè)谏鲜龅拇a中把delete per7
這條語(yǔ)句給注釋掉,那么會(huì)怎么樣呢,下圖是去掉該語(yǔ)句之后的結(jié)果:image-20210115133215468 我們看到,上述就只執(zhí)行了?zhangsan
的析構(gòu)函數(shù),并沒(méi)有執(zhí)行lisi
的析構(gòu)函數(shù),這也告訴我們,在使用 new 創(chuàng)建的實(shí)例化對(duì)象,必須使用 delete 將其釋放掉,如果沒(méi)有使用 delete 來(lái)將其釋放,那么在系統(tǒng)退出之后,會(huì)自動(dòng)地釋放掉它地內(nèi)存,但是這個(gè)時(shí)候是不會(huì)調(diào)用它地析構(gòu)函數(shù)的 。最后,關(guān)于構(gòu)造函數(shù)和析構(gòu)函數(shù),如果類里沒(méi)有實(shí)現(xiàn)任何構(gòu)造函數(shù)和析構(gòu)函數(shù),那么其系統(tǒng)本身會(huì)調(diào)用一個(gè)默認(rèn)的構(gòu)造函數(shù)和析構(gòu)函數(shù)。那么,除了默認(rèn)的構(gòu)造函數(shù)和默認(rèn)的析構(gòu)函數(shù),還存在一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù),接下來(lái),來(lái)敘述這個(gè)拷貝構(gòu)造函數(shù)。拷貝構(gòu)造函數(shù) 默認(rèn)拷貝構(gòu)造函數(shù) 我們直接來(lái)看這樣一段代碼:#include ? #include ? #include ? using ?namespace ?std ;class ?Person ?{private : ????char ?*name; ????int ?age; ????char ?*work;public : ????Person()?{//cout?<<"Pserson()"< ????????name?=?NULL ; ????????work?=?NULL ; ????} ????Person(char ?*name)? ????{ ????????//cout?<<"Pserson(char?*)"< ????????this ->name?=?new ?char [strlen (name)? ?1 ]; ????????strcpy (this ->name,?name); ????????this ->work?=?NULL ; ????} ????Person(char ?*name,?int ?age,?char ?*work?=?"none" )? ????{ ????????//cout?<<"Pserson(char*,?int)"< ????????this ->age?=?age; ????????this ->name?=?new ?char [strlen (name)? ?1 ]; ????????strcpy (this ->name,?name); ????????this ->work?=?new ?char [strlen (work)? ?1 ]; ????????strcpy (this ->work,?work); ????} ????~Person() ????{ ????????cout ?<"~Person()" <<endl ; ????????if ?(this ->name)?{ ????????????cout ?<"name?=?" <endl ; ????????????delete ?this ->name; ????????} ????????if ?(this ->work)?{ ????????????cout ?<"work?=?" <endl ; ????????????delete ?this ->work; ????????} ????} ????void ?printInfo (void ) ???? { ????????//printf("name?=?%s,?age?=?%d,?work?=?%s\n",?name,?age,?work);? ????????cout <<"name?=?" <",?age?=?" <",?work?=?"<endl; ????} };int ?main (int ?argc,?char ?**argv) { ????Person?per ("zhangsan" ,?18 ) ; ????Person?per2 (per) ; ????per2.printInfo(); ????return ?0 ; }
在主函數(shù)的第二行代碼中,我們可以看到我們創(chuàng)建了一個(gè)實(shí)例,并且傳入的參數(shù)是?per
,但是我們看類里面的代碼實(shí)現(xiàn),并沒(méi)有發(fā)現(xiàn)有一個(gè)構(gòu)造函數(shù)的形參為 Person ,那這個(gè)時(shí)候,會(huì)發(fā)生什么函數(shù)調(diào)用呢,實(shí)際上是會(huì)調(diào)用一個(gè)系統(tǒng)的默認(rèn)構(gòu)造函數(shù),這個(gè)默認(rèn)的構(gòu)造函數(shù)會(huì)進(jìn)行值拷貝,會(huì)將?per
中的內(nèi)容拷貝到?per2
中去,下圖是這個(gè)過(guò)程的一個(gè)示意圖:image-20210117015212259.png 通過(guò)上圖可以看到,在執(zhí)行默認(rèn)的拷貝構(gòu)造函數(shù)的時(shí)候,執(zhí)行的是值拷貝,那么相應(yīng)的,per 的 name 也就指向了?address1
,per2 的 name 同樣也指向了?adress
,從而完成了值拷貝的過(guò)程,下面是代碼運(yùn)行的結(jié)果:image-20210117015527675 可以看到,在輸出 per2 的內(nèi)容的時(shí)候,輸出的是?per
?的初始化內(nèi)容,在主函數(shù)運(yùn)行完之后,就要執(zhí)行析構(gòu)函數(shù)來(lái)釋放使用 new 分配的空間,首先是釋放?per
?的內(nèi)容,然后緊接著是釋放?per2
的內(nèi)容,但是在剛剛的敘述中,使用默認(rèn)構(gòu)造函數(shù)進(jìn)行拷貝的時(shí)候,使用的是值拷貝,從而造成的效果是 per2 的 name 和 work 指向的地址是?per
?中的同一塊地址,這樣,在執(zhí)行析構(gòu)函數(shù)的時(shí)候,同一塊內(nèi)存空間就會(huì)被釋放兩次,從而導(dǎo)致錯(cuò)誤。因此,使用默認(rèn)的拷貝構(gòu)造函數(shù)存在一定的問(wèn)題,也就需要我們自己來(lái)定義拷貝構(gòu)造函數(shù),下面介紹自定義的拷貝構(gòu)造函數(shù)。自定義拷貝構(gòu)造函數(shù) 我們根據(jù)在上述代碼的基礎(chǔ)上,修改得到我們自定義的拷貝構(gòu)造函數(shù)如下:#include ? #include ? #include ? using ?namespace ?std ;class ?Person ?{private : ????char ?*name; ????int ?age; ????char ?*work;public : ????Person()?{//cout?<<"Pserson()"< ????????name?=?NULL ; ????????work?=?NULL ; ????} ????Person(char ?*name)? ????{ ????????//cout?<<"Pserson(char?*)"< ????????this ->name?=?new ?char [strlen (name)? ?1 ]; ????????strcpy (this ->name,?name); ????????this ->work?=?NULL ; ????} ????Person(char ?*name,?int ?age,?char ?*work?=?"none" )? ????{ ????????cout ?<<"Pserson(char*,?int)" <<endl ; ????????this ->age?=?age; ????????this ->name?=?new ?char [strlen (name)? ?1 ]; ????????strcpy (this ->name,?name); ????????this ->work?=?new ?char [strlen (work)? ?1 ]; ????????strcpy (this ->work,?work); ????} ????Person(Person
欲知詳情,請(qǐng)下載word文檔
下載文檔
本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。