C++常見面試題集合_第1頁
C++常見面試題集合_第2頁
C++常見面試題集合_第3頁
C++常見面試題集合_第4頁
C++常見面試題集合_第5頁
已閱讀5頁,還剩116頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

C++常見面試題集合引用和指針的區(qū)別?指針是一個(gè)實(shí)體,需要分配內(nèi)存空間。引用只是變量的別名,不需要分配內(nèi)存空間。引用在定義的時(shí)候必須進(jìn)行初始化,并且不能夠改變。指針在定義的時(shí)候不一定要初始化,并且指向的空間可變。(注:不能有引用的值不能為NULL)有多級指針,但是沒有多級引用,只能有一級引用。指針和引用的自增運(yùn)算結(jié)果不一樣。(指針是指向下一個(gè)空間,引用時(shí)引用的變量值加1)sizeof引用得到的是所指向的變量(對象)的大小,而sizeof指針得到的是指針本身的大小。引用訪問一個(gè)變量是直接訪問,而指針訪問一個(gè)變量是間接訪問。使用指針前最好做類型檢查,防止野指針的出現(xiàn);引用底層是通過指針實(shí)現(xiàn)的;作為參數(shù)時(shí)也不同,傳指針的實(shí)質(zhì)是傳值,傳遞的值是指針的地址;傳引用的實(shí)質(zhì)是傳地址,傳遞的是變量的地址。從匯編層去解釋一下引用9:intx=1;00401048movdwordptr[ebp-4],110:int&b=x;0040104Fleaeax,[ebp-4]00401052movdwordptr[ebp-8],eaxx的地址為ebp-4,b的地址為ebp-8,因?yàn)闂?nèi)的變量內(nèi)存是從高往低進(jìn)行分配的。所以b的地址比x的低。leaeax,[ebp-4]

這條語句將x的地址ebp-4放入eax寄存器movdwordptr[ebp-8],eax這條語句將eax的值放入b的地址ebp-8中上面兩條匯編的作用即:將x的地址存入變量b中,這不和將某個(gè)變量的地址存入指針變量是一樣的嗎?所以從匯編層次來看,的確引用是通過指針來實(shí)現(xiàn)的。C++中的指針參數(shù)傳遞和引用參數(shù)傳遞指針參數(shù)傳遞本質(zhì)上是值傳遞,它所傳遞的是一個(gè)地址值。值傳遞過程中,被調(diào)函數(shù)的形式參數(shù)作為被調(diào)函數(shù)的局部變量處理,會(huì)在棧中開辟內(nèi)存空間以存放由主調(diào)函數(shù)傳遞進(jìn)來的實(shí)參值,從而形成了實(shí)參的一個(gè)副本(替身)。值傳遞的特點(diǎn)是,被調(diào)函數(shù)對形式參數(shù)的任何操作都是作為局部變量進(jìn)行的,不會(huì)影響主調(diào)函數(shù)的實(shí)參變量的值(形參指針變了,實(shí)參指針不會(huì)變)。引用參數(shù)傳遞過程中,被調(diào)函數(shù)的形式參數(shù)也作為局部變量在棧中開辟了內(nèi)存空間,但是這時(shí)存放的是由主調(diào)函數(shù)放進(jìn)來的實(shí)參變量的地址。被調(diào)函數(shù)對形參(本體)的任何操作都被處理成間接尋址,即通過棧中存放的地址訪問主調(diào)函數(shù)中的實(shí)參變量(根據(jù)別名找到主調(diào)函數(shù)中的本體)。因此,被調(diào)函數(shù)對形參的任何操作都會(huì)影響主調(diào)函數(shù)中的實(shí)參變量。引用傳遞和指針傳遞是不同的,雖然他們都是在被調(diào)函數(shù)棧空間上的一個(gè)局部變量,但是任何對于引用參數(shù)的處理都會(huì)通過一個(gè)間接尋址的方式操作到主調(diào)函數(shù)中的相關(guān)變量。而對于指針傳遞的參數(shù),如果改變被調(diào)函數(shù)中的指針地址,它將應(yīng)用不到主調(diào)函數(shù)的相關(guān)變量。如果想通過指針參數(shù)傳遞來改變主調(diào)函數(shù)中的相關(guān)變量(地址),那就得使用指向指針的指針或者指針引用。從編譯的角度來講,程序在編譯時(shí)分別將指針和引用添加到符號表上,符號表中記錄的是變量名及變量所對應(yīng)地址。指針變量在符號表上對應(yīng)的地址值為指針變量的地址值,而引用在符號表上對應(yīng)的地址值為引用對象的地址值(與實(shí)參名字不同,地址相同)。符號表生成之后就不會(huì)再改,因此指針可以改變其指向的對象(指針變量中的值可以改),而引用對象則不能修改。形參與實(shí)參的區(qū)別?形參變量只有在被調(diào)用時(shí)才分配內(nèi)存單元,在調(diào)用結(jié)束時(shí),即刻釋放所分配的內(nèi)存單元。因此,形參只有在函數(shù)內(nèi)部有效。函數(shù)調(diào)用結(jié)束返回主調(diào)函數(shù)后則不能再使用該形參變量。實(shí)參可以是常量、變量、表達(dá)式、函數(shù)等,無論實(shí)參是何種類型的量,在進(jìn)行函數(shù)調(diào)用時(shí),它們都必須具有確定的值,以便把這些值傳送給形參。因此應(yīng)預(yù)先用賦值,輸入等辦法使實(shí)參獲得確定值,會(huì)產(chǎn)生一個(gè)臨時(shí)變量。實(shí)參和形參在數(shù)量上,類型上,順序上應(yīng)嚴(yán)格一致,否則會(huì)發(fā)生“類型不匹配”的錯(cuò)誤。函數(shù)調(diào)用中發(fā)生的數(shù)據(jù)傳送是單向的。即只能把實(shí)參的值傳送給形參,而不能把形參的值反向地傳送給實(shí)參。因此在函數(shù)調(diào)用過程中,形參的值發(fā)生改變,而實(shí)參中的值不會(huì)變化。當(dāng)形參和實(shí)參不是指針類型時(shí),在該函數(shù)運(yùn)行時(shí),形參和實(shí)參是不同的變量,他們在內(nèi)存中位于不同的位置,形參將實(shí)參的內(nèi)容復(fù)制一份,在該函數(shù)運(yùn)行結(jié)束的時(shí)候形參被釋放,而實(shí)參內(nèi)容不會(huì)改變。值傳遞:有一個(gè)形參向函數(shù)所屬的??截悢?shù)據(jù)的過程,如果值傳遞的對象是類對象或是大的結(jié)構(gòu)體對象,將耗費(fèi)一定的時(shí)間和空間。(傳值)指針傳遞:同樣有一個(gè)形參向函數(shù)所屬的??截悢?shù)據(jù)的過程,但拷貝的數(shù)據(jù)是一個(gè)固定為4字節(jié)的地址。(傳值,傳遞的是地址值)引用傳遞:同樣有上述的數(shù)據(jù)拷貝過程,但其是針對地址的,相當(dāng)于為該數(shù)據(jù)所在的地址起了一個(gè)別名。(傳地址)效率上講,指針傳遞和引用傳遞比值傳遞效率高。一般主張使用引用傳遞,代碼邏輯上更加緊湊、清晰。static的用法和作用?1.先來介紹它的第一條也是最重要的一條:隱藏。(static函數(shù),static變量均可)當(dāng)同時(shí)編譯多個(gè)文件時(shí),所有未加static前綴的全局變量和函數(shù)都具有全局可見性。2.static的第二個(gè)作用是保持變量內(nèi)容的持久。(static變量中的記憶功能和全局生存期)存儲(chǔ)在靜態(tài)數(shù)據(jù)區(qū)的變量會(huì)在程序剛開始運(yùn)行時(shí)就完成初始化,也是唯一的一次初始化。共有兩種變量存儲(chǔ)在靜態(tài)存儲(chǔ)區(qū):全局變量和static變量,只不過和全局變量比起來,static可以控制變量的可見范圍,說到底static還是用來隱藏的。3.static的第三個(gè)作用是默認(rèn)初始化為0(static變量)其實(shí)全局變量也具備這一屬性,因?yàn)槿肿兞恳泊鎯?chǔ)在靜態(tài)數(shù)據(jù)區(qū)。在靜態(tài)數(shù)據(jù)區(qū),內(nèi)存中所有的字節(jié)默認(rèn)值都是0x00,某些時(shí)候這一特點(diǎn)可以減少程序員的工作量。4.static的第四個(gè)作用:C++中的類成員聲明static函數(shù)體內(nèi)static變量的作用范圍為該函數(shù)體,不同于auto變量,該變量的內(nèi)存只被分配一次,因此其值在下次調(diào)用時(shí)仍維持上次的值;

在模塊內(nèi)的static全局變量可以被模塊內(nèi)所用函數(shù)訪問,但不能被模塊外其它函數(shù)訪問;

在模塊內(nèi)的static函數(shù)只可被這一模塊內(nèi)的其它函數(shù)調(diào)用,這個(gè)函數(shù)的使用范圍被限制在聲明它的模塊內(nèi);

在類中的static成員變量屬于整個(gè)類所擁有,對類的所有對象只有一份拷貝;

在類中的static成員函數(shù)屬于整個(gè)類所擁有,這個(gè)函數(shù)不接收this指針,因而只能訪問類的static成員變量。類內(nèi):static類對象必須要在類外進(jìn)行初始化,static修飾的變量先于對象存在,所以static修飾的變量要在類外初始化;由于static修飾的類成員屬于類,不屬于對象,因此static類成員函數(shù)是沒有this指針的,this指針是指向本對象的指針。正因?yàn)闆]有this指針,所以static類成員函數(shù)不能訪問非static的類成員,只能訪問static修飾的類成員;static成員函數(shù)不能被virtual修飾,static成員不屬于任何對象或?qū)嵗?,所以加上virtual沒有任何實(shí)際意義;靜態(tài)成員函數(shù)沒有this指針,虛函數(shù)的實(shí)現(xiàn)是為每一個(gè)對象分配一個(gè)vptr指針,而vptr是通過this指針調(diào)用的,所以不能為virtual;虛函數(shù)的調(diào)用關(guān)系,this->vptr->ctable->virtualfunction靜態(tài)變量什么時(shí)候初始化初始化只有一次,但是可以多次賦值,在主程序之前,編譯器已經(jīng)為其分配好了內(nèi)存。靜態(tài)局部變量和全局變量一樣,數(shù)據(jù)都存放在全局區(qū)域,所以在主程序之前,編譯器已經(jīng)為其分配好了內(nèi)存,但在C和C++中靜態(tài)局部變量的初始化節(jié)點(diǎn)又有點(diǎn)不太一樣。在C中,初始化發(fā)生在代碼執(zhí)行之前,編譯階段分配好內(nèi)存之后,就會(huì)進(jìn)行初始化,所以我們看到在C語言中無法使用變量對靜態(tài)局部變量進(jìn)行初始化,在程序運(yùn)行結(jié)束,變量所處的全局內(nèi)存會(huì)被全部回收。而在C++中,初始化時(shí)在執(zhí)行相關(guān)代碼時(shí)才會(huì)進(jìn)行初始化,主要是由于C++引入對象后,要進(jìn)行初始化必須執(zhí)行相應(yīng)構(gòu)造函數(shù)和析構(gòu)函數(shù),在構(gòu)造函數(shù)或析構(gòu)函數(shù)中經(jīng)常會(huì)需要進(jìn)行某些程序中需要進(jìn)行的特定操作,并非簡單地分配內(nèi)存。所以C++標(biāo)準(zhǔn)定為全局或靜態(tài)對象是有首次用到時(shí)才會(huì)進(jìn)行構(gòu)造,并通過atexit()來管理。在程序結(jié)束,按照構(gòu)造順序反方向進(jìn)行逐個(gè)析構(gòu)。所以在C++中是可以使用變量對靜態(tài)局部變量進(jìn)行初始化的。

const?阻止一個(gè)變量被改變,可以使用const關(guān)鍵字。在定義該const變量時(shí),通常需要對它進(jìn)行初始化,因?yàn)橐院缶蜎]有機(jī)會(huì)再去改變它了;

對指針來說,可以指定指針本身為const,也可以指定指針?biāo)傅臄?shù)據(jù)為const,或二者同時(shí)指定為const;

在一個(gè)函數(shù)聲明中,const可以修飾形參,表明它是一個(gè)輸入?yún)?shù),在函數(shù)內(nèi)部不能改變其值;

對于類的成員函數(shù),若指定其為const類型,則表明其是一個(gè)常函數(shù),不能修改類的成員變量,類的常對象只能訪問類的常成員函數(shù);

對于類的成員函數(shù),有時(shí)候必須指定其返回值為const類型,以使得其返回值不為“左值”。const成員函數(shù)可以訪問非const對象的非const數(shù)據(jù)成員、const數(shù)據(jù)成員,也可以訪問const對象內(nèi)的所有數(shù)據(jù)成員;非const成員函數(shù)可以訪問非const對象的非const數(shù)據(jù)成員、const數(shù)據(jù)成員,但不可以訪問const對象的任意數(shù)據(jù)成員;一個(gè)沒有明確聲明為const的成員函數(shù)被看作是將要修改對象中數(shù)據(jù)成員的函數(shù),而且編譯器不允許它為一個(gè)const對象所調(diào)用。因此const對象只能調(diào)用const成員函數(shù)。const類型變量可以通過類型轉(zhuǎn)換符const_cast將const類型轉(zhuǎn)換為非const類型;const類型變量必須定義的時(shí)候進(jìn)行初始化,因此也導(dǎo)致如果類的成員變量有const類型的變量,那么該變量必須在類的初始化列表中進(jìn)行初始化;對于函數(shù)值傳遞的情況,因?yàn)閰?shù)傳遞是通過復(fù)制實(shí)參創(chuàng)建一個(gè)臨時(shí)變量傳遞進(jìn)函數(shù)的,函數(shù)內(nèi)只能改變臨時(shí)變量,但無法改變實(shí)參。則這個(gè)時(shí)候無論加不加const對實(shí)參不會(huì)產(chǎn)生任何影響。但是在引用或指針傳遞函數(shù)調(diào)用中,因?yàn)閭鬟M(jìn)去的是一個(gè)引用或指針,這樣函數(shù)內(nèi)部可以改變引用或指針?biāo)赶虻淖兞?,這時(shí)const才是實(shí)實(shí)在在地保護(hù)了實(shí)參所指向的變量。因?yàn)樵诰幾g階段編譯器對調(diào)用函數(shù)的選擇是根據(jù)實(shí)參進(jìn)行的,所以,只有引用傳遞和指針傳遞可以用是否加const來重載。一個(gè)擁有頂層const的形參無法和另一個(gè)沒有頂層const的形參區(qū)分開來。const成員函數(shù)的理解和應(yīng)用?constStock&Stock::topval(②constStock&s)

③const①處const:確保返回的Stock對象在以后的使用中不能被修改 ②處const:確保此方法不修改傳遞的參數(shù)S③處const:保證此方法不修改調(diào)用它的對象,const對象只能調(diào)用const成員函數(shù),不能調(diào)用非const函數(shù)指針和const的用法當(dāng)const修飾指針時(shí),由于const的位置不同,它的修飾對象會(huì)有所不同。int*constp2中const修飾p2的值,所以理解為p2的值不可以改變,即p2只能指向固定的一個(gè)變量地址,但可以通過*p2讀寫這個(gè)變量的值。頂層指針表示指針本身是一個(gè)常量intconst*p1或者constint*p1兩種情況中const修飾*p1,所以理解為*p1的值不可以改變,即不可以給*p1賦值改變p1指向變量的值,但可以通過給p賦值不同的地址改變這個(gè)指針指向。底層指針表示指針?biāo)赶虻淖兞渴且粋€(gè)常量。intconst*constp;mutable如果需要在const成員方法中修改一個(gè)成員變量的值,那么需要將這個(gè)成員變量修飾為mutable。即用mutable修飾的成員變量不受const成員方法的限制;可以認(rèn)為mutable的變量是類的輔助狀態(tài),但是只是起到類的一些方面表述的功能,修改他的內(nèi)容我們可以認(rèn)為對象的狀態(tài)本身并沒有改變的。實(shí)際上由于const_cast的存在,這個(gè)概念很多時(shí)候用處不是很到了。extern用法?extern修飾變量的聲明如果文件a.c需要引用b.c中變量intv,就可以在a.c中聲明externintv,然后就可以引用變量v。extern修飾函數(shù)的聲明如果文件a.c需要引用b.c中的函數(shù),比如在b.c中原型是intfun(intmu),那么就可以在a.c中聲明externintfun(intmu),然后就能使用fun來做任何事情。就像變量的聲明一樣,externintfun(intmu)可以放在a.c中任何地方,而不一定非要放在a.c的文件作用域的范圍中。extern修飾符可用于指示C或者C++函數(shù)的調(diào)用規(guī)范。比如在C++中調(diào)用C庫函數(shù),就需要在C++程序中用extern“C”聲明要引用的函數(shù)。這是給鏈接器用的,告訴鏈接器在鏈接的時(shí)候用C函數(shù)規(guī)范來鏈接。主要原因是C++和C程序編譯完成后在目標(biāo)代碼中命名規(guī)則不同。int轉(zhuǎn)字符串字符串轉(zhuǎn)int?strcat,strcpy,strncpy,memset,memcpy的內(nèi)部實(shí)現(xiàn)?c++11標(biāo)準(zhǔn)增加了全局函數(shù)std::to_string可以使用std::stoi/stol/stoll等等函數(shù)strcpy擁有返回值,有時(shí)候函數(shù)原本不需要返回值,但為了增加靈活性如支持鏈?zhǔn)奖磉_(dá),深拷貝與淺拷貝?淺復(fù)制

—-只是拷貝了基本類型的數(shù)據(jù),而引用類型數(shù)據(jù),復(fù)制后也是會(huì)發(fā)生引用,我們把這種拷貝叫做“(淺復(fù)制)淺拷貝”,換句話說,淺復(fù)制僅僅是指向被復(fù)制的內(nèi)存地址,如果原地址中對象被改變了,那么淺復(fù)制出來的對象也會(huì)相應(yīng)改變。深復(fù)制

—-在計(jì)算機(jī)中開辟了一塊新的內(nèi)存地址用于存放復(fù)制的對象。在某些狀況下,類內(nèi)成員變量需要?jiǎng)討B(tài)開辟堆內(nèi)存,如果實(shí)行位拷貝,也就是把對象里的值完全復(fù)制給另一個(gè)對象,如A=B。這時(shí),如果B中有一個(gè)成員變量指針已經(jīng)申請了內(nèi)存,那A中的那個(gè)成員變量也指向同一塊內(nèi)存。這就出現(xiàn)了問題:當(dāng)B把內(nèi)存釋放了(如:析構(gòu)),這時(shí)A內(nèi)的指針就是野指針了,出現(xiàn)運(yùn)行錯(cuò)誤。簡單的來說,【淺拷貝】是增加了一個(gè)指針,指向原來已經(jīng)存在的內(nèi)存。而【深拷貝】是增加了一個(gè)指針,并新開辟了一塊空間讓指針指向這塊新開辟的空間?!緶\拷貝】在多個(gè)對象指向一塊空間的時(shí)候,釋放一個(gè)空間會(huì)導(dǎo)致其他對象所使用的空間也被釋放了,再次釋放便會(huì)出現(xiàn)錯(cuò)誤C++模板是什么,底層怎么實(shí)現(xiàn)的?編譯器并不是把函數(shù)模板處理成能夠處理任意類的函數(shù);編譯器從函數(shù)模板通過具體類型產(chǎn)生不同的函數(shù);編譯器會(huì)對函數(shù)模板進(jìn)行兩次編譯:在聲明的地方對模板代碼本身進(jìn)行編譯,在調(diào)用的地方對參數(shù)替換后的代碼進(jìn)行編譯。這是因?yàn)楹瘮?shù)模板要被實(shí)例化后才能成為真正的函數(shù),在使用函數(shù)模板的源文件中包含函數(shù)模板的頭文件,如果該頭文件中只有聲明,沒有定義,那編譯器無法實(shí)例化該模板,最終導(dǎo)致鏈接錯(cuò)誤。C語言struct和C++struct區(qū)別C語言中:struct是用戶自定義數(shù)據(jù)類型(UDT);C++中struct是抽象數(shù)據(jù)類型(ADT),支持成員函數(shù)的定義,(C++中的struct能繼承,能實(shí)現(xiàn)多態(tài))。C中struct是沒有權(quán)限的設(shè)置的,且struct中只能是一些變量的集合體,可以封裝數(shù)據(jù)卻不可以隱藏?cái)?shù)據(jù),而且成員不可以是函數(shù)。C++中,struct的成員默認(rèn)訪問說明符為public(為了與C兼容),class中的默認(rèn)訪問限定符為private,struct增加了訪問權(quán)限,且可以和類一樣有成員函數(shù)。struct作為類的一種特例是用來自定義數(shù)據(jù)結(jié)構(gòu)的。一個(gè)結(jié)構(gòu)標(biāo)記聲明后,在C中必須在結(jié)構(gòu)標(biāo)記前加上struct,才能做結(jié)構(gòu)類型名虛函數(shù)可以聲明為inline嗎?虛函數(shù)用于實(shí)現(xiàn)運(yùn)行時(shí)的多態(tài),或者稱為晚綁定或動(dòng)態(tài)綁定。而內(nèi)聯(lián)函數(shù)用于提高效率。內(nèi)聯(lián)函數(shù)的原理是,在編譯期間,對調(diào)用內(nèi)聯(lián)函數(shù)的地方的代碼替換成函數(shù)代碼。內(nèi)聯(lián)函數(shù)對于程序中需要頻繁使用和調(diào)用的小函數(shù)非常有用。虛函數(shù)要求在運(yùn)行時(shí)進(jìn)行類型確定,而內(nèi)斂函數(shù)要求在編譯期完成相關(guān)的函數(shù)替換;虛函數(shù)可以申明為inline,具體到編譯器解釋時(shí)候,如果編譯器在編譯的時(shí)候就可以確定該虛函數(shù)的決議,則編譯器以inline方式靜態(tài)決議該虛函數(shù)。如果編譯器在編譯的時(shí)候不能決定,則必須在運(yùn)行時(shí)決議虛函數(shù),此時(shí)虛函數(shù)不能以inline函數(shù)的方式調(diào)用。類成員初始化方式?構(gòu)造函數(shù)的執(zhí)行順序?為什么用成員初始化列表會(huì)快一些?賦值初始化,通過在函數(shù)體內(nèi)進(jìn)行賦值初始化;列表初始化,在冒號后使用初始化列表進(jìn)行初始化。這兩種方式的主要區(qū)別在于:對于在函數(shù)體中初始化,是在所有的數(shù)據(jù)成員被分配內(nèi)存空間后才進(jìn)行的。列表初始化是給數(shù)據(jù)成員分配內(nèi)存空間時(shí)就進(jìn)行初始化,就是說分配一個(gè)數(shù)據(jù)成員只要冒號后有此數(shù)據(jù)成員的賦值表達(dá)式(此表達(dá)式必須是括號賦值表達(dá)式),那么分配了內(nèi)存空間后在進(jìn)入函數(shù)體之前給數(shù)據(jù)成員賦值,就是說初始化這個(gè)數(shù)據(jù)成員此時(shí)函數(shù)體還未執(zhí)行。

一個(gè)派生類構(gòu)造函數(shù)的執(zhí)行順序如下:虛擬基類的構(gòu)造函數(shù)(多個(gè)虛擬基類則按照繼承的順序執(zhí)行構(gòu)造函數(shù))?;惖臉?gòu)造函數(shù)(多個(gè)普通基類也按照繼承的順序執(zhí)行構(gòu)造函數(shù))。類類型的成員對象的構(gòu)造函數(shù)(按照初始化順序)派生類自己的構(gòu)造函數(shù)。方法一是在構(gòu)造函數(shù)當(dāng)中做賦值的操作,而方法二是做純粹的初始化操作。我們都知道,C++的賦值操作是會(huì)產(chǎn)生臨時(shí)對象的。臨時(shí)對象的出現(xiàn)會(huì)降低程序的效率。成員列表初始化?必須使用成員初始化的四種情況當(dāng)初始化一個(gè)引用成員時(shí);當(dāng)初始化一個(gè)常量成員時(shí);當(dāng)調(diào)用一個(gè)基類的構(gòu)造函數(shù),而它擁有一組參數(shù)時(shí);當(dāng)調(diào)用一個(gè)成員類的構(gòu)造函數(shù),而它擁有一組參數(shù)時(shí);成員初始化列表做了什么編譯器會(huì)一一操作初始化列表,以適當(dāng)?shù)捻樞蛟跇?gòu)造函數(shù)之內(nèi)安插初始化操作,并且在任何顯示用戶代碼之前;list中的項(xiàng)目順序是由類中的成員聲明順序決定的,不是由初始化列表的順序決定的;構(gòu)造函數(shù)為什么不能為虛函數(shù)?析構(gòu)函數(shù)為什么要虛函數(shù)?1.從存儲(chǔ)空間角度,虛函數(shù)相應(yīng)一個(gè)指向vtable虛函數(shù)表的指針,這大家都知道,但是這個(gè)指向vtable的指針事實(shí)上是存儲(chǔ)在對象的內(nèi)存空間的。問題出來了,假設(shè)構(gòu)造函數(shù)是虛的,就須要通過vtable來調(diào)用,但是對象還沒有實(shí)例化,也就是內(nèi)存空間還沒有,怎么找vtable呢?所以構(gòu)造函數(shù)不能是虛函數(shù)。2.從使用角度,虛函數(shù)主要用于在信息不全的情況下,能使重載的函數(shù)得到相應(yīng)的調(diào)用。構(gòu)造函數(shù)本身就是要初始化實(shí)例,那使用虛函數(shù)也沒有實(shí)際意義呀。所以構(gòu)造函數(shù)沒有必要是虛函數(shù)。虛函數(shù)的作用在于通過父類的指針或者引用來調(diào)用它的時(shí)候可以變成調(diào)用子類的那個(gè)成員函數(shù)。而構(gòu)造函數(shù)是在創(chuàng)建對象時(shí)自己主動(dòng)調(diào)用的,不可能通過父類的指針或者引用去調(diào)用,因此也就規(guī)定構(gòu)造函數(shù)不能是虛函數(shù)。3.構(gòu)造函數(shù)不須要是虛函數(shù),也不同意是虛函數(shù),由于創(chuàng)建一個(gè)對象時(shí)我們總是要明白指定對象的類型,雖然我們可能通過實(shí)驗(yàn)室的基類的指針或引用去訪問它但析構(gòu)卻不一定,我們往往通過基類的指針來銷毀對象。這時(shí)候假設(shè)析構(gòu)函數(shù)不是虛函數(shù),就不能正確識別對象類型從而不能正確調(diào)用析構(gòu)函數(shù)。4.從實(shí)現(xiàn)上看,vbtl在構(gòu)造函數(shù)調(diào)用后才建立,因而構(gòu)造函數(shù)不可能成為虛函數(shù)從實(shí)際含義上看,在調(diào)用構(gòu)造函數(shù)時(shí)還不能確定對象的真實(shí)類型(由于子類會(huì)調(diào)父類的構(gòu)造函數(shù));并且構(gòu)造函數(shù)的作用是提供初始化,在對象生命期僅僅運(yùn)行一次,不是對象的動(dòng)態(tài)行為,也沒有必要成為虛函數(shù)。5.當(dāng)一個(gè)構(gòu)造函數(shù)被調(diào)用時(shí),它做的首要的事情之中的一個(gè)是初始化它的VPTR。因此,它僅僅能知道它是“當(dāng)前”類的,而全然忽視這個(gè)對象后面是否還有繼承者。當(dāng)編譯器為這個(gè)構(gòu)造函數(shù)產(chǎn)生代碼時(shí),它是為這個(gè)類的構(gòu)造函數(shù)產(chǎn)生代碼——既不是為基類,也不是為它的派生類(由于類不知道誰繼承它)。所以它使用的VPTR必須是對于這個(gè)類的VTABLE。并且,僅僅要它是最后的構(gòu)造函數(shù)調(diào)用,那么在這個(gè)對象的生命期內(nèi),VPTR將保持被初始化為指向這個(gè)VTABLE,但假設(shè)接著另一個(gè)更晚派生的構(gòu)造函數(shù)被調(diào)用,這個(gè)構(gòu)造函數(shù)又將設(shè)置VPTR指向它的VTABLE,等.直到最后的構(gòu)造函數(shù)結(jié)束。VPTR的狀態(tài)是由被最后調(diào)用的構(gòu)造函數(shù)確定的。這就是為什么構(gòu)造函數(shù)調(diào)用是從基類到更加派生類順序的還有一個(gè)理由。可是,當(dāng)這一系列構(gòu)造函數(shù)調(diào)用正發(fā)生時(shí),每一個(gè)構(gòu)造函數(shù)都已經(jīng)設(shè)置VPTR指向它自己的VTABLE。假設(shè)函數(shù)調(diào)用使用虛機(jī)制,它將僅僅產(chǎn)生通過它自己的VTABLE的調(diào)用,而不是最后的VTABLE(全部構(gòu)造函數(shù)被調(diào)用后才會(huì)有最后的VTABLE)。因?yàn)闃?gòu)造函數(shù)本來就是為了明確初始化對象成員才產(chǎn)生的,然而virtualfunction主要是為了再不完全了解細(xì)節(jié)的情況下也能正確處理對象。另外,virtual函數(shù)是在不同類型的對象產(chǎn)生不同的動(dòng)作,現(xiàn)在對象還沒有產(chǎn)生,如何使用virtual函數(shù)來完成你想完成的動(dòng)作。直接的講,C++中基類采用virtual虛析構(gòu)函數(shù)是為了防止內(nèi)存泄漏。具體地說,如果派生類中申請了內(nèi)存空間,并在其析構(gòu)函數(shù)中對這些內(nèi)存空間進(jìn)行釋放。假設(shè)基類中采用的是非虛析構(gòu)函數(shù),當(dāng)刪除基類指針指向的派生類對象時(shí)就不會(huì)觸發(fā)動(dòng)態(tài)綁定,因而只會(huì)調(diào)用基類的析構(gòu)函數(shù),而不會(huì)調(diào)用派生類的析構(gòu)函數(shù)。那么在這種情況下,派生類中申請的空間就得不到釋放從而產(chǎn)生內(nèi)存泄漏。所以,為了防止這種情況的發(fā)生,C++中基類的析構(gòu)函數(shù)應(yīng)采用virtual虛析構(gòu)函數(shù)。析構(gòu)函數(shù)的作用,如何起作用?構(gòu)造函數(shù)只是起初始化值的作用,但實(shí)例化一個(gè)對象的時(shí)候,可以通過實(shí)例去傳遞參數(shù),從主函數(shù)傳遞到其他的函數(shù)里面,這樣就使其他的函數(shù)里面有值了。規(guī)則,只要你一實(shí)例化對象,系統(tǒng)自動(dòng)回調(diào)用一個(gè)構(gòu)造函數(shù),就是你不寫,編譯器也自動(dòng)調(diào)用一次。析構(gòu)函數(shù)與構(gòu)造函數(shù)的作用相反,用于撤銷對象的一些特殊任務(wù)處理,可以是釋放對象分配的內(nèi)存空間;特點(diǎn):析構(gòu)函數(shù)與構(gòu)造函數(shù)同名,但該函數(shù)前面加~。析構(gòu)函數(shù)沒有參數(shù),也沒有返回值,而且不能重載,在一個(gè)類中只能有一個(gè)析構(gòu)函數(shù)。當(dāng)撤銷對象時(shí),編譯器也會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)。每一個(gè)類必須有一個(gè)析構(gòu)函數(shù),用戶可以自定義析構(gòu)函數(shù),也可以是編譯器自動(dòng)生成默認(rèn)的析構(gòu)函數(shù)。一般析構(gòu)函數(shù)定義為類的公有成員。構(gòu)造函數(shù)和析構(gòu)函數(shù)可以調(diào)用虛函數(shù)嗎,為什么在C++中,提倡不在構(gòu)造函數(shù)和析構(gòu)函數(shù)中調(diào)用虛函數(shù);構(gòu)造函數(shù)和析構(gòu)函數(shù)調(diào)用虛函數(shù)時(shí)都不使用動(dòng)態(tài)聯(lián)編,如果在構(gòu)造函數(shù)或析構(gòu)函數(shù)中調(diào)用虛函數(shù),則運(yùn)行的是為構(gòu)造函數(shù)或析構(gòu)函數(shù)自身類型定義的版本;因?yàn)楦割悓ο髸?huì)在子類之前進(jìn)行構(gòu)造,此時(shí)子類部分的數(shù)據(jù)成員還未初始化,因此調(diào)用子類的虛函數(shù)時(shí)不安全的,故而C++不會(huì)進(jìn)行動(dòng)態(tài)聯(lián)編;析構(gòu)函數(shù)是用來銷毀一個(gè)對象的,在銷毀一個(gè)對象時(shí),先調(diào)用子類的析構(gòu)函數(shù),然后再調(diào)用基類的析構(gòu)函數(shù)。所以在調(diào)用基類的析構(gòu)函數(shù)時(shí),派生類對象的數(shù)據(jù)成員已經(jīng)銷毀,這個(gè)時(shí)候再調(diào)用子類的虛函數(shù)沒有任何意義。構(gòu)造函數(shù)的執(zhí)行順序?析構(gòu)函數(shù)的執(zhí)行順序?構(gòu)造函數(shù)內(nèi)部干了啥?拷貝構(gòu)造干了啥?構(gòu)造函數(shù)順序基類構(gòu)造函數(shù)。如果有多個(gè)基類,則構(gòu)造函數(shù)的調(diào)用順序是某類在類派生表中出現(xiàn)的順序,而不是它們在成員初始化表中的順序。成員類對象構(gòu)造函數(shù)。如果有多個(gè)成員類對象則構(gòu)造函數(shù)的調(diào)用順序是對象在類中被聲明的順序,而不是它們出現(xiàn)在成員初始化表中的順序。派生類構(gòu)造函數(shù)。析構(gòu)函數(shù)順序調(diào)用派生類的析構(gòu)函數(shù);調(diào)用成員類對象的析構(gòu)函數(shù);調(diào)用基類的析構(gòu)函數(shù)。虛析構(gòu)函數(shù)的作用,父類的析構(gòu)函數(shù)是否要設(shè)置為虛函數(shù)?C++中基類采用virtual虛析構(gòu)函數(shù)是為了防止內(nèi)存泄漏。具體地說,如果派生類中申請了內(nèi)存空間,并在其析構(gòu)函數(shù)中對這些內(nèi)存空間進(jìn)行釋放。假設(shè)基類中采用的是非虛析構(gòu)函數(shù),當(dāng)刪除基類指針指向的派生類對象時(shí)就不會(huì)觸發(fā)動(dòng)態(tài)綁定,因而只會(huì)調(diào)用基類的析構(gòu)函數(shù),而不會(huì)調(diào)用派生類的析構(gòu)函數(shù)。那么在這種情況下,派生類中申請的空間就得不到釋放從而產(chǎn)生內(nèi)存泄漏。所以,為了防止這種情況的發(fā)生,C++中基類的析構(gòu)函數(shù)應(yīng)采用virtual虛析構(gòu)函數(shù)。純虛析構(gòu)函數(shù)一定得定義,因?yàn)槊恳粋€(gè)派生類析構(gòu)函數(shù)會(huì)被編譯器加以擴(kuò)張,以靜態(tài)調(diào)用的方式調(diào)用其每一個(gè)虛基類以及上一層基類的析構(gòu)函數(shù)。因此,缺乏任何一個(gè)基類析構(gòu)函數(shù)的定義,就會(huì)導(dǎo)致鏈接失敗。因此,最好不要把虛析構(gòu)函數(shù)定義為純虛析構(gòu)函數(shù)。構(gòu)造函數(shù)析構(gòu)函數(shù)可以調(diào)用虛函數(shù)嗎?在構(gòu)造函數(shù)和析構(gòu)函數(shù)中最好不要調(diào)用虛函數(shù);構(gòu)造函數(shù)或者析構(gòu)函數(shù)調(diào)用虛函數(shù)并不會(huì)發(fā)揮虛函數(shù)動(dòng)態(tài)綁定的特性,跟普通函數(shù)沒區(qū)別;即使構(gòu)造函數(shù)或者析構(gòu)函數(shù)如果能成功調(diào)用虛函數(shù),程序的運(yùn)行結(jié)果也是不可控的。構(gòu)造函數(shù)析構(gòu)函數(shù)可否拋出異常

C++只會(huì)析構(gòu)已經(jīng)完成的對象,對象只有在其構(gòu)造函數(shù)執(zhí)行完畢才算是完全構(gòu)造妥當(dāng)。在構(gòu)造函數(shù)中發(fā)生異常,控制權(quán)轉(zhuǎn)出構(gòu)造函數(shù)之外。因此,在對象b的構(gòu)造函數(shù)中發(fā)生異常,對象b的析構(gòu)函數(shù)不會(huì)被調(diào)用。因此會(huì)造成內(nèi)存泄漏。用auto_ptr對象來取代指針類成員,便對構(gòu)造函數(shù)做了強(qiáng)化,免除了拋出異常時(shí)發(fā)生資源泄漏的危機(jī),不再需要在析構(gòu)函數(shù)中手動(dòng)釋放資源;如果控制權(quán)基于異常的因素離開析構(gòu)函數(shù),而此時(shí)正有另一個(gè)異常處于作用狀態(tài),C++會(huì)調(diào)用terminate函數(shù)讓程序結(jié)束;如果異常從析構(gòu)函數(shù)拋出,而且沒有在當(dāng)?shù)剡M(jìn)行捕捉,那個(gè)析構(gòu)函數(shù)便是執(zhí)行不全的。如果析構(gòu)函數(shù)執(zhí)行不全,就是沒有完成他應(yīng)該執(zhí)行的每一件事情。類如何實(shí)現(xiàn)只能靜態(tài)分配和只能動(dòng)態(tài)分配前者是把new、delete運(yùn)算符重載為private屬性。后者是把構(gòu)造、析構(gòu)函數(shù)設(shè)為protected屬性,再用子類來動(dòng)態(tài)創(chuàng)建建立類的對象有兩種方式:靜態(tài)建立,靜態(tài)建立一個(gè)類對象,就是由編譯器為對象在??臻g中分配內(nèi)存;動(dòng)態(tài)建立,A*p=newA();動(dòng)態(tài)建立一個(gè)類對象,就是使用new運(yùn)算符為對象在堆空間中分配內(nèi)存。這個(gè)過程分為兩步,第一步執(zhí)行operatornew()函數(shù),在堆中搜索一塊內(nèi)存并進(jìn)行分配;第二步調(diào)用類構(gòu)造函數(shù)構(gòu)造對象;只有使用new運(yùn)算符,對象才會(huì)被建立在堆上,因此只要限制new運(yùn)算符就可以實(shí)現(xiàn)類對象只能建立在棧上??梢詫ew運(yùn)算符設(shè)為私有。如果想將某個(gè)類用作基類,為什么該類必須定義而非聲明?派生類中包含并且可以使用它從基類繼承而來的成員,為了使用這些成員,派生類必須知道他們是什么。什么情況會(huì)自動(dòng)生成默認(rèn)構(gòu)造函數(shù)?帶有默認(rèn)構(gòu)造函數(shù)的類成員對象,如果一個(gè)類沒有任何構(gòu)造函數(shù),但它含有一個(gè)成員對象,而后者有默認(rèn)構(gòu)造函數(shù),那么編譯器就為該類合成出一個(gè)默認(rèn)構(gòu)造函數(shù)。不過這個(gè)合成操作只有在構(gòu)造函數(shù)真正被需要的時(shí)候才會(huì)發(fā)生;如果一個(gè)類A含有多個(gè)成員類對象的話,那么類A的每一個(gè)構(gòu)造函數(shù)必須調(diào)用每一個(gè)成員對象的默認(rèn)構(gòu)造函數(shù)而且必須按照類對象在類A中的聲明順序進(jìn)行;帶有默認(rèn)構(gòu)造函數(shù)的基類,如果一個(gè)沒有任務(wù)構(gòu)造函數(shù)的派生類派生自一個(gè)帶有默認(rèn)構(gòu)造函數(shù)基類,那么該派生類會(huì)合成一個(gè)構(gòu)造函數(shù)調(diào)用上一層基類的默認(rèn)構(gòu)造函數(shù);帶有一個(gè)虛函數(shù)的類帶有一個(gè)虛基類的類合成的默認(rèn)構(gòu)造函數(shù)中,只有基類子對象和成員類對象會(huì)被初始化。所有其他的非靜態(tài)數(shù)據(jù)成員都不會(huì)被初始化。什么是類的繼承?類與類之間的關(guān)系has-A包含關(guān)系,用以描述一個(gè)類由多個(gè)部件類構(gòu)成,實(shí)現(xiàn)has-A關(guān)系用類的成員屬性表示,即一個(gè)類的成員屬性是另一個(gè)已經(jīng)定義好的類;use-A,一個(gè)類使用另一個(gè)類,通過類之間的成員函數(shù)相互聯(lián)系,定義友元或者通過傳遞參數(shù)的方式來實(shí)現(xiàn);is-A,繼承關(guān)系,關(guān)系具有傳遞性;繼承的相關(guān)概念所謂的繼承就是一個(gè)類繼承了另一個(gè)類的屬性和方法,這個(gè)新的類包含了上一個(gè)類的屬性和方法,被稱為子類或者派生類,被繼承的類稱為父類或者基類;繼承的特點(diǎn)子類擁有父類的所有屬性和方法,子類可以擁有父類沒有的屬性和方法,子類對象可以當(dāng)做父類對象使用;繼承中的訪問控制public、protected、private繼承中的構(gòu)造和析構(gòu)函數(shù)繼承中的兼容性原則什么是組合?一個(gè)類里面的數(shù)據(jù)成員是另一個(gè)類的對象,即內(nèi)嵌其他類的對象作為自己的成員;創(chuàng)建組合類的對象:首先創(chuàng)建各個(gè)內(nèi)嵌對象,難點(diǎn)在于構(gòu)造函數(shù)的設(shè)計(jì)。創(chuàng)建對象時(shí)既要對基本類型的成員進(jìn)行初始化,又要對內(nèi)嵌對象進(jìn)行初始化。創(chuàng)建組合類對象,構(gòu)造函數(shù)的執(zhí)行順序:先調(diào)用內(nèi)嵌對象的構(gòu)造函數(shù),然后按照內(nèi)嵌對象成員在組合類中的定義順序,與組合類構(gòu)造函數(shù)的初始化列表順序無關(guān)。然后執(zhí)行組合類構(gòu)造函數(shù)的函數(shù)體,析構(gòu)函數(shù)調(diào)用順序相反。抽象基類為什么不能創(chuàng)建對象?抽象類是一種特殊的類,它是為了抽象和設(shè)計(jì)的目的為建立的,它處于繼承層次結(jié)構(gòu)的較上層。(1)抽象類的定義:

稱帶有純虛函數(shù)的類為抽象類。(2)抽象類的作用:

抽象類的主要作用是將有關(guān)的操作作為結(jié)果接口組織在一個(gè)繼承層次結(jié)構(gòu)中,由它來為派生類提供一個(gè)公共的根,派生類將具體實(shí)現(xiàn)在其基類中作為接口的操作。所以派生類實(shí)際上刻畫了一組子類的操作接口的通用語義,這些語義也傳給子類,子類可以具體實(shí)現(xiàn)這些語義,也可以再將這些語義傳給自己的子類。(3)使用抽象類時(shí)注意:

抽象類只能作為基類來使用,其純虛函數(shù)的實(shí)現(xiàn)由派生類給出。如果派生類中沒有重新定義純虛函數(shù),而只是繼承基類的純虛函數(shù),則這個(gè)派生類仍然還是一個(gè)抽象類。如果派生類中給出了基類純虛函數(shù)的實(shí)現(xiàn),則該派生類就不再是抽象類了,它是一個(gè)可以建立對象的具體的類。抽象類是不能定義對象的。一個(gè)純虛函數(shù)不需要(但是可以)被定義。一、純虛函數(shù)定義

純虛函數(shù)是一種特殊的虛函數(shù),它的一般格式如下:

class<類名>

{

virtual<類型><函數(shù)名>(<參數(shù)表>)=0;

};

在許多情況下,在基類中不能對虛函數(shù)給出有意義的實(shí)現(xiàn),而把它聲明為純虛函數(shù),它的實(shí)現(xiàn)留給該基類的派生類去做。這就是純虛函數(shù)的作用。

純虛函數(shù)可以讓類先具有一個(gè)操作名稱,而沒有操作內(nèi)容,讓派生類在繼承時(shí)再去具體地給出定義。凡是含有純虛函數(shù)的類叫做抽象類。這種類不能聲明對象,只是作為基類為派生類服務(wù)。除非在派生類中完全實(shí)現(xiàn)基類中所有的的純虛函數(shù),否則,派生類也變成了抽象類,不能實(shí)例化對象。

二、純虛函數(shù)引入原因

1、為了方便使用多態(tài)特性,我們常常需要在基類中定義虛擬函數(shù)。

2、在很多情況下,基類本身生成對象是不合情理的。例如,動(dòng)物作為一個(gè)基類可以派生出老虎、孔雀等子類,但動(dòng)物本身生成對象明顯不合常理。

為了解決上述問題,引入了純虛函數(shù)的概念,將函數(shù)定義為純虛函數(shù)(方法:virtualReturnTypeFunction()=0;)。若要使派生類為非抽象類,則編譯器要求在派生類中,必須對純虛函數(shù)予以重載以實(shí)現(xiàn)多態(tài)性。同時(shí)含有純虛函數(shù)的類稱為抽象類,它不能生成對象。這樣就很好地解決了上述兩個(gè)問題。

例如,繪畫程序中,shape作為一個(gè)基類可以派生出圓形、矩形、正方形、梯形等,如果我要求面積總和的話,那么會(huì)可以使用一個(gè)shape*的數(shù)組,只要依次調(diào)用派生類的area()函數(shù)了。如果不用接口就沒法定義成數(shù)組,因?yàn)榧瓤梢允莄ircle,也可以是square,而且以后還可能加上rectangle,等等.三、相似概念

1、多態(tài)性指相同對象收到不同消息或不同對象收到相同消息時(shí)產(chǎn)生不同的實(shí)現(xiàn)動(dòng)作。C++支持兩種多態(tài)性:編譯時(shí)多態(tài)性,運(yùn)行時(shí)多態(tài)性。

a.編譯時(shí)多態(tài)性:通過重載函數(shù)實(shí)現(xiàn)

b.運(yùn)行時(shí)多態(tài)性:通過虛函數(shù)實(shí)現(xiàn)。

2、虛函數(shù)

虛函數(shù)是在基類中被聲明為virtual,并在派生類中重新定義的成員函數(shù),可實(shí)現(xiàn)成員函數(shù)的動(dòng)態(tài)重載。

3、抽象類

包含純虛函數(shù)的類稱為抽象類。由于抽象類包含了沒有定義的純虛函數(shù),所以不能定義抽象類的對象。類什么時(shí)候會(huì)析構(gòu)?對象生命周期結(jié)束,被銷毀時(shí);delete指向?qū)ο蟮闹羔槙r(shí),或delete指向?qū)ο蟮幕愵愋椭羔?,而其基類虛?gòu)函數(shù)是虛函數(shù)時(shí);對象i是對象o的成員,o的析構(gòu)函數(shù)被調(diào)用時(shí),對象i的析構(gòu)函數(shù)也被調(diào)用。為什么友元函數(shù)必須在類內(nèi)部聲明?因?yàn)榫幾g器必須能夠讀取這個(gè)結(jié)構(gòu)的聲明以理解這個(gè)數(shù)據(jù)類型的大、行為等方面的所有規(guī)則。有一條規(guī)則在任何關(guān)系中都很重要,那就是誰可以訪問我的私有部分。介紹一下C++里面的多態(tài)?(1)靜態(tài)多態(tài)(重載,模板)是在編譯的時(shí)候,就確定調(diào)用函數(shù)的類型。(2)動(dòng)態(tài)多態(tài)(覆蓋,虛函數(shù)實(shí)現(xiàn))在運(yùn)行的時(shí)候,才確定調(diào)用的是哪個(gè)函數(shù),動(dòng)態(tài)綁定。運(yùn)行基類指針指向派生類的對象,并調(diào)用派生類的函數(shù)。虛函數(shù)實(shí)現(xiàn)原理:虛函數(shù)表和虛函數(shù)指針。純虛函數(shù):virtualintfun()=0;函數(shù)的運(yùn)行版本由實(shí)參決定,在運(yùn)行時(shí)選擇函數(shù)的版本,所以動(dòng)態(tài)綁定又稱為運(yùn)行時(shí)綁定。當(dāng)編譯器遇到一個(gè)模板定義時(shí),它并不生成代碼。只有當(dāng)實(shí)例化出模板的一個(gè)特定版本時(shí),編譯器才會(huì)生成代碼。用C語言實(shí)現(xiàn)C++的繼承#include<iostream>usingnamespacestd;//C++中的繼承與多態(tài)structA{virtualvoidfun()//C++中的多態(tài):通過虛函數(shù)實(shí)現(xiàn){cout<<"A:fun()"<<endl;}inta;};structB:publicA//C++中的繼承:B類公有繼承A類{virtualvoidfun()//C++中的多態(tài):通過虛函數(shù)實(shí)現(xiàn)(子類的關(guān)鍵字virtual可加可不加){cout<<"B:fun()"<<endl;}intb;};//C語言模擬C++的繼承與多態(tài)typedefvoid(*FUN)();//定義一個(gè)函數(shù)指針來實(shí)現(xiàn)對成員函數(shù)的繼承struct_A//父類{FUN_fun;//由于C語言中結(jié)構(gòu)體不能包含函數(shù),故只能用函數(shù)指針在外面實(shí)現(xiàn)int_a;};struct_B//子類{_A_a_;//在子類中定義一個(gè)基類的對象即可實(shí)現(xiàn)對父類的繼承int_b;};void_fA()//父類的同名函數(shù){printf("_A:_fun()\n");}void_fB()//子類的同名函數(shù){printf("_B:_fun()\n");}voidTest(){//測試C++中的繼承與多態(tài)Aa;//定義一個(gè)父類對象aBb;//定義一個(gè)子類對象bA*p1=&a;//定義一個(gè)父類指針指向父類的對象p1->fun();//調(diào)用父類的同名函數(shù)p1=&b;//讓父類指針指向子類的對象p1->fun();//調(diào)用子類的同名函數(shù)//C語言模擬繼承與多態(tài)的測試_A_a;//定義一個(gè)父類對象_a_B_b;//定義一個(gè)子類對象_b_a._fun=_fA;//父類的對象調(diào)用父類的同名函數(shù)_b._a_._fun=_fB;//子類的對象調(diào)用子類的同名函數(shù)_A*p2=&_a;//定義一個(gè)父類指針指向父類的對象p2->_fun();//調(diào)用父類的同名函數(shù)p2=(_A*)&_b;//讓父類指針指向子類的對象,由于類型不匹配所以要進(jìn)行強(qiáng)轉(zhuǎn)p2->_fun();//調(diào)用子類的同名函數(shù)}繼承機(jī)制中對象之間如何轉(zhuǎn)換?指針和引用之間如何轉(zhuǎn)換?向上類型轉(zhuǎn)換將派生類指針或引用轉(zhuǎn)換為基類的指針或引用被稱為向上類型轉(zhuǎn)換,向上類型轉(zhuǎn)換會(huì)自動(dòng)進(jìn)行,而且向上類型轉(zhuǎn)換是安全的。向下類型轉(zhuǎn)換將基類指針或引用轉(zhuǎn)換為派生類指針或引用被稱為向下類型轉(zhuǎn)換,向下類型轉(zhuǎn)換不會(huì)自動(dòng)進(jìn)行,因?yàn)橐粋€(gè)基類對應(yīng)幾個(gè)派生類,所以向下類型轉(zhuǎn)換時(shí)不知道對應(yīng)哪個(gè)派生類,所以在向下類型轉(zhuǎn)換時(shí)必須加動(dòng)態(tài)類型識別技術(shù)。RTTI技術(shù),用dynamic_cast進(jìn)行向下類型轉(zhuǎn)換。組合與繼承優(yōu)缺點(diǎn)?一:繼承繼承是Isa的關(guān)系,比如說Student繼承Person,則說明StudentisaPerson。繼承的優(yōu)點(diǎn)是子類可以重寫父類的方法來方便地實(shí)現(xiàn)對父類的擴(kuò)展。繼承的缺點(diǎn)有以下幾點(diǎn):①:父類的內(nèi)部細(xì)節(jié)對子類是可見的。②:子類從父類繼承的方法在編譯時(shí)就確定下來了,所以無法在運(yùn)行期間改變從父類繼承的方法的行為。③:如果對父類的方法做了修改的話(比如增加了一個(gè)參數(shù)),則子類的方法必須做出相應(yīng)的修改。所以說子類與父類是一種高耦合,違背了面向?qū)ο笏枷?。二:組合組合也就是設(shè)計(jì)類的時(shí)候把要組合的類的對象加入到該類中作為自己的成員變量。組合的優(yōu)點(diǎn):①:當(dāng)前對象只能通過所包含的那個(gè)對象去調(diào)用其方法,所以所包含的對象的內(nèi)部細(xì)節(jié)對當(dāng)前對象是不可見的。②:當(dāng)前對象與包含的對象是一個(gè)低耦合關(guān)系,如果修改包含對象的類中代碼不需要修改當(dāng)前對象類的代碼。③:當(dāng)前對象可以在運(yùn)行時(shí)動(dòng)態(tài)的綁定所包含的對象??梢酝ㄟ^set方法給所包含對象賦值。組合的缺點(diǎn):①:容易產(chǎn)生過多的對象。②:為了能組合多個(gè)對象,必須仔細(xì)對接口進(jìn)行定義。左值右值在C++11中所有的值必屬于左值、右值兩者之一,右值又可以細(xì)分為純右值、將亡值。在C++11中可以取地址的、有名字的就是左值,反之,不能取地址的、沒有名字的就是右值(將亡值或純右值)。舉個(gè)例子,inta=b+c,a就是左值,其有變量名為a,通過&a可以獲取該變量的地址;表達(dá)式b+c、函數(shù)intfunc()的返回值是右值,在其被賦值給某一變量前,我們不能通過變量名找到它,&(b+c)這樣的操作則不會(huì)通過編譯。C++11對C++98中的右值進(jìn)行了擴(kuò)充。在C++11中右值又分為純右值(prvalue,PureRvalue)和將亡值(xvalue,eXpiringValue)。其中純右值的概念等同于我們在C++98標(biāo)準(zhǔn)中右值的概念,指的是臨時(shí)變量和不跟對象關(guān)聯(lián)的字面量值;將亡值則是C++11新增的跟右值引用相關(guān)的表達(dá)式,這樣表達(dá)式通常是將要被移動(dòng)的對象(移為他用),比如返回右值引用T&&的函數(shù)返回值、std::move的返回值,或者轉(zhuǎn)換為T&&的類型轉(zhuǎn)換函數(shù)的返回值。將亡值可以理解為通過“盜取”其他變量內(nèi)存空間的方式獲取到的值。在確保其他變量不再被使用、或即將被銷毀時(shí),通過“盜取”的方式可以避免內(nèi)存空間的釋放和分配,能夠延長變量值的生命期。左值引用就是對一個(gè)左值進(jìn)行引用的類型。右值引用就是對一個(gè)右值進(jìn)行引用的類型,事實(shí)上,由于右值通常不具有名字,我們也只能通過引用的方式找到它的存在。右值引用和左值引用都是屬于引用類型。無論是聲明一個(gè)左值引用還是右值引用,都必須立即進(jìn)行初始化。而其原因可以理解為是引用類型本身自己并不擁有所綁定對象的內(nèi)存,只是該對象的一個(gè)別名。左值引用是具名變量值的別名,而右值引用則是不具名(匿名)變量的別名。左值引用通常也不能綁定到右值,但常量左值引用是個(gè)“萬能”的引用類型。它可以接受非常量左值、常量左值、右值對其進(jìn)行初始化。不過常量左值所引用的右值在它的“余生”中只能是只讀的。相對地,非常量左值只能接受非常量左值對其進(jìn)行初始化。右值值引用通常不能綁定到任何的左值,要想綁定一個(gè)左值到右值引用,通常需要std::move()將左值強(qiáng)制轉(zhuǎn)換為右值。移動(dòng)構(gòu)造函數(shù)我們用對象a初始化對象b,后對象a我們就不在使用了,但是對象a的空間還在呀(在析構(gòu)之前),既然拷貝構(gòu)造函數(shù),實(shí)際上就是把a(bǔ)對象的內(nèi)容復(fù)制一份到b中,那么為什么我們不能直接使用a的空間呢?這樣就避免了新的空間的分配,大大降低了構(gòu)造的成本。這就是移動(dòng)構(gòu)造函數(shù)設(shè)計(jì)的初衷;拷貝構(gòu)造函數(shù)中,對于指針,我們一定要采用深層復(fù)制,而移動(dòng)構(gòu)造函數(shù)中,對于指針,我們采用淺層復(fù)制。淺層復(fù)制之所以危險(xiǎn),是因?yàn)閮蓚€(gè)指針共同指向一片內(nèi)存空間,若第一個(gè)指針將其釋放,另一個(gè)指針的指向就不合法了。所以我們只要避免第一個(gè)指針釋放空間就可以了。避免的方法就是將第一個(gè)指針(比如a->value)置為NULL,這樣在調(diào)用析構(gòu)函數(shù)的時(shí)候,由于有判斷是否為NULL的語句,所以析構(gòu)a的時(shí)候并不會(huì)回收a->value指向的空間;移動(dòng)構(gòu)造函數(shù)的參數(shù)和拷貝構(gòu)造函數(shù)不同,拷貝構(gòu)造函數(shù)的參數(shù)是一個(gè)左值引用,但是移動(dòng)構(gòu)造函數(shù)的初值是一個(gè)右值引用。意味著,移動(dòng)構(gòu)造函數(shù)的參數(shù)是一個(gè)右值或者將亡值的引用。也就是說,只用用一個(gè)右值,或者將亡值初始化另一個(gè)對象的時(shí)候,才會(huì)調(diào)用移動(dòng)構(gòu)造函數(shù)。而那個(gè)move語句,就是將一個(gè)左值變成一個(gè)將亡值。C語言的編譯鏈接過程?源代碼-->預(yù)處理-->編譯-->優(yōu)化-->匯編-->鏈接-->可執(zhí)行文件預(yù)處理讀取c源程序,對其中的偽指令(以#開頭的指令)和特殊符號進(jìn)行處理。包括宏定義替換、條件編譯指令、頭文件包含指令、特殊符號。

預(yù)編譯程序所完成的基本上是對源程序的“替代”工作。經(jīng)過此種替代,生成一個(gè)沒有宏定義、沒有條件編譯指令、沒有特殊符號的輸出文件。.i預(yù)處理后的c文件,.ii預(yù)處理后的C++文件。編譯階段編譯程序所要作得工作就是通過詞法分析和語法分析,在確認(rèn)所有的指令都符合語法規(guī)則之后,將其翻譯成等價(jià)的中間代碼表示或匯編代碼。.s文件匯編過程匯編過程實(shí)際上指把匯編語言代碼翻譯成目標(biāo)機(jī)器指令的過程。對于被翻譯系統(tǒng)處理的每一個(gè)C語言源程序,都將最終經(jīng)過這一處理而得到相應(yīng)的目標(biāo)文件。目標(biāo)文件中所存放的也就是與源程序等效的目標(biāo)的機(jī)器語言代碼。.o目標(biāo)文件鏈接階段鏈接程序的主要工作就是將有關(guān)的目標(biāo)文件彼此相連接,也即將在一個(gè)文件中引用的符號同該符號在另外一個(gè)文件中的定義連接起來,使得所有的這些目標(biāo)文件成為一個(gè)能夠誒操作系統(tǒng)裝入執(zhí)行的統(tǒng)一整體。vector與list的區(qū)別與應(yīng)用?怎么找某vector或者list的倒數(shù)第二個(gè)元素vector數(shù)據(jù)結(jié)構(gòu)

vector和數(shù)組類似,擁有一段連續(xù)的內(nèi)存空間,并且起始地址不變。因此能高效的進(jìn)行隨機(jī)存取,時(shí)間復(fù)雜度為o(1);但因?yàn)閮?nèi)存空間是連續(xù)的,所以在進(jìn)行插入和刪除操作時(shí),會(huì)造成內(nèi)存塊的拷貝,時(shí)間復(fù)雜度為o(n)。另外,當(dāng)數(shù)組中內(nèi)存空間不夠時(shí),會(huì)重新申請一塊內(nèi)存空間并進(jìn)行內(nèi)存拷貝。連續(xù)存儲(chǔ)結(jié)構(gòu):vector是可以實(shí)現(xiàn)動(dòng)態(tài)增長的對象數(shù)組,支持對數(shù)組高效率的訪問和在數(shù)組尾端的刪除和插入操作,在中間和頭部刪除和插入相對不易,需要挪動(dòng)大量的數(shù)據(jù)。它與數(shù)組最大的區(qū)別就是vector不需程序員自己去考慮容量問題,庫里面本身已經(jīng)實(shí)現(xiàn)了容量的動(dòng)態(tài)增長,而數(shù)組需要程序員手動(dòng)寫入擴(kuò)容函數(shù)進(jìn)形擴(kuò)容。list數(shù)據(jù)結(jié)構(gòu)

list是由雙向鏈表實(shí)現(xiàn)的,因此內(nèi)存空間是不連續(xù)的。只能通過指針訪問數(shù)據(jù),所以list的隨機(jī)存取非常沒有效率,時(shí)間復(fù)雜度為o(n);但由于鏈表的特點(diǎn),能高效地進(jìn)行插入和刪除。非連續(xù)存儲(chǔ)結(jié)構(gòu):list是一個(gè)雙鏈表結(jié)構(gòu),支持對鏈表的雙向遍歷。每個(gè)節(jié)點(diǎn)包括三個(gè)信息:元素本身,指向前一個(gè)元素的節(jié)點(diǎn)(prev)和指向下一個(gè)元素的節(jié)點(diǎn)(next)。因此list可以高效率的對數(shù)據(jù)元素任意位置進(jìn)行訪問和插入刪除等操作。由于涉及對額外指針的維護(hù),所以開銷比較大。區(qū)別:vector的隨機(jī)訪問效率高,但在插入和刪除時(shí)(不包括尾部)需要挪動(dòng)數(shù)據(jù),不易操作。list的訪問要遍歷整個(gè)鏈表,它的隨機(jī)訪問效率低。但對數(shù)據(jù)的插入和刪除操作等都比較方便,改變指針的指向即可。list是單向的,vector是雙向的。vector中的迭代器在使用后就失效了,而list的迭代器在使用之后還可以繼續(xù)使用。

int

mySize=vec.size();vec.at(mySize-2);list不提供隨機(jī)訪問,所以不能用下標(biāo)直接訪問到某個(gè)位置的元素,要訪問list里的元素只能遍歷,不過你要是只需要訪問list的最后N個(gè)元素的話,可以用反向迭代器來遍歷:STLvector的實(shí)現(xiàn),刪除其中的元素,迭代器如何變化?為什么是兩倍擴(kuò)容?釋放空間?size()函數(shù)返回的是已用空間大小,capacity()返回的是總空間大小,capacity()-size()則是剩余的可用空間大小。當(dāng)size()和capacity()相等,說明vector目前的空間已被用完,如果再添加新元素,則會(huì)引起vector空間的動(dòng)態(tài)增長。由于動(dòng)態(tài)增長會(huì)引起重新分配內(nèi)存空間、拷貝原空間、釋放原空間,這些過程會(huì)降低程序效率。因此,可以使用reserve(n)預(yù)先分配一塊較大的指定大小的內(nèi)存空間,這樣當(dāng)指定大小的內(nèi)存空間未使用完時(shí),是不會(huì)重新分配內(nèi)存空間的,這樣便提升了效率。只有當(dāng)n>capacity()時(shí),調(diào)用reserve(n)才會(huì)改變vector容量。

resize()成員函數(shù)只改變元素的數(shù)目,不改變vector的容量。1.空的vector對象,size()和capacity()都為02.當(dāng)空間大小不足時(shí),新分配的空間大小為原空間大小的2倍。3.使用reserve()預(yù)先分配一塊內(nèi)存后,在空間未滿的情況下,不會(huì)引起重新分配,從而提升了效率。4.當(dāng)reserve()分配的空間比原空間小時(shí),是不會(huì)引起重新分配的。5.resize()函數(shù)只改變?nèi)萜鞯脑財(cái)?shù)目,未改變?nèi)萜鞔笮 ?.用reserve(size_type)只是擴(kuò)大capacity值,這些內(nèi)存空間可能還是“野”的,如果此時(shí)使用“[]”來訪問,則可能會(huì)越界。而resize(size_typenew_size)會(huì)真正使容器具有new_size個(gè)對象。不同的編譯器,vector有不同的擴(kuò)容大小。在vs下是1.5倍,在GCC下是2倍;空間和時(shí)間的權(quán)衡。簡單來說,空間分配的多,平攤時(shí)間復(fù)雜度低,但浪費(fèi)空間也多。使用k=2增長因子的問題在于,每次擴(kuò)展的新尺寸必然剛好大于之前分配的總和,也就是說,之前分配的內(nèi)存空間不可能被使用。這樣對內(nèi)存不友好。最好把增長因子設(shè)為(1,2)對比可以發(fā)現(xiàn)采用采用成倍方式擴(kuò)容,可以保證常數(shù)的時(shí)間復(fù)雜度,而增加指定大小的容量只能達(dá)到O(n)的時(shí)間復(fù)雜度,因此,使用成倍的方式擴(kuò)容。如何釋放空間:由于vector的內(nèi)存占用空間只增不減,比如你首先分配了10,000個(gè)字節(jié),然后erase掉后面9,999個(gè),留下一個(gè)有效元素,但是內(nèi)存占用仍為10,000個(gè)。所有內(nèi)存空間是在vector析構(gòu)時(shí)候才能被系統(tǒng)回收。empty()用來檢測容器是否為空的,clear()可以清空所有元素。但是即使clear(),vector所占用的內(nèi)存空間依然如故,無法保證內(nèi)存的回收。如果需要空間動(dòng)態(tài)縮小,可以考慮使用deque。如果vector,可以用swap()來幫助你釋放內(nèi)存。vector(Vec).swap(Vec);

將Vec的內(nèi)存空洞清除;

vector().swap(Vec);

清空Vec的內(nèi)存;容器內(nèi)部刪除一個(gè)元素順序容器:順序容器有以下三種:可變長動(dòng)態(tài)數(shù)組vector、雙端隊(duì)列deque、雙向鏈表list。erase迭代器不僅使所指向被刪除的迭代器失效,而且使被刪元素之后的所有迭代器失效(list除外),所以不能使用erase(it++)的方式,但是erase的返回值是下一個(gè)有效迭代器;It=c.erase(it);關(guān)聯(lián)容器:關(guān)聯(lián)容器有以下四種:set、multiset、map、multimap。erase迭代器只是被刪除元素的迭代器失效,但是返回值是void,所以要采用erase(it++)的方式刪除迭代器;c.erase(it++)STL迭代器如何實(shí)現(xiàn) 迭代器是一種抽象的設(shè)計(jì)理念,通過迭代器可以在不了解容器內(nèi)部原理的情況下遍歷容器,除此之外,STL中迭代器一個(gè)最重要的作用就是作為容器與STL算法的粘合劑。迭代器的作用就是提供一個(gè)遍歷容器內(nèi)部所有元素的接口,因此迭代器內(nèi)部必須保存一個(gè)與容器相關(guān)聯(lián)的指針,然后重載各種運(yùn)算操作來遍歷,其中最重要的是*運(yùn)算符與->運(yùn)算符,以及++、--等可能需要重載的運(yùn)算符重載。這和C++中的智能指針很像,智能指針也是將一個(gè)指針封裝,然后通過引用計(jì)數(shù)或是其他方法完成自動(dòng)釋放內(nèi)存的功能。最常用的迭代器的相應(yīng)型別有五種:valuetype、differencetype、pointer、reference、iteratorcatagoly;set與hash_set的區(qū)別set底層是以RB-Tree實(shí)現(xiàn),hash_set底層是以hash_table實(shí)現(xiàn)的;RB-Tree有自動(dòng)排序功能,而hash_table不具有自動(dòng)排序功能;set和hash_set元素的鍵值就是實(shí)值;hash_table有一些無法處理的型別;hashmap與map的區(qū)別底層實(shí)現(xiàn)不同;map具有自動(dòng)排序的功能,hash_map不具有自動(dòng)排序的功能;hashtable有一些無法處理的型別;map、set是怎么實(shí)現(xiàn)的,紅黑樹是怎么能夠同時(shí)實(shí)現(xiàn)這兩種容器?為什么使用紅黑樹?他們的底層都是以紅黑樹的結(jié)構(gòu)實(shí)現(xiàn),因此插入刪除等操作都在O(logn)時(shí)間內(nèi)完成,因此可以完成高效的插入刪除;在這里我們定義了一個(gè)模版參數(shù),如果它是key那么它就是set,如果它是map,那么它就是map;底層是紅黑樹,實(shí)現(xiàn)map的紅黑樹的節(jié)點(diǎn)數(shù)據(jù)類型是key+value,而實(shí)現(xiàn)set的節(jié)點(diǎn)數(shù)據(jù)類型是value因?yàn)閙ap和set要求是自動(dòng)排序的,紅黑樹能夠?qū)崿F(xiàn)這一功能,而且時(shí)間復(fù)雜度比較低。如何在共享內(nèi)存上使用stl標(biāo)準(zhǔn)庫?想像一下把STL容器,例如map,vector,list等等,放入共享內(nèi)存中,IPC一旦有了這些強(qiáng)大的通用數(shù)據(jù)結(jié)構(gòu)做輔助,無疑進(jìn)程間通信的能力一下子強(qiáng)大了很多。我們沒必要再為共享內(nèi)存設(shè)計(jì)其他額外的數(shù)據(jù)結(jié)構(gòu),另外,STL的高度可擴(kuò)展性將為IPC所驅(qū)使。STL容器被良好的封裝,默認(rèn)情況下有它們自己的內(nèi)存管理方案。當(dāng)一個(gè)元素被插入到一個(gè)STL列表(list)中時(shí),列表容器自動(dòng)為其分配內(nèi)存,保存數(shù)據(jù)??紤]到要將STL容器放到共享內(nèi)存中,而容器卻自己在堆上分配內(nèi)存。一個(gè)最笨拙的辦法是在堆上構(gòu)造STL容器,然后把容器復(fù)制到共享內(nèi)存,并且確保所有容器的內(nèi)部分配的內(nèi)存指向共享內(nèi)存中的相應(yīng)區(qū)域,這基本是個(gè)不可能完成的任務(wù)。假設(shè)進(jìn)程A在共享內(nèi)存中放入了數(shù)個(gè)容器,進(jìn)程B如何找到這些容器呢?一個(gè)方法就是進(jìn)程A把容器放在共享內(nèi)存中的確定地址上(fixedoffsets),則進(jìn)程B可以從該已知地址上獲取容器。另外一個(gè)改進(jìn)點(diǎn)的辦法是,進(jìn)程A先在共享內(nèi)存某塊確定地址上放置一個(gè)map容器,然后進(jìn)程A再創(chuàng)建其他容器,然后給其取個(gè)名字和地址一并保存到這個(gè)map容器里。進(jìn)程B知道如何獲取該保存了地址映射的map容器,然后同樣再根據(jù)名字取得其他容器的地址。map插入方式有幾種?用insert函數(shù)插入pair數(shù)據(jù),mapStudent.insert(pair<int,

string>(1,

"student_one"));

用insert函數(shù)插入value_type數(shù)據(jù)mapStudent.insert(map<int,

string>::value_type

(1,

"student_one"));在insert函數(shù)中使用make_pair()函數(shù)mapStudent.insert(make_pair(1,

"student_one"));

用數(shù)組方式插入數(shù)據(jù)mapStudent[1]

=

"student_one";

STL中unordered_map(hash_map)和map的區(qū)別,hash_map如何解決沖突以及擴(kuò)容unordered_map和map類似,都是存儲(chǔ)的key-value的值,可以通過key快速索引到value。不同的是unordered_map不會(huì)根據(jù)key的大小進(jìn)行排序,存儲(chǔ)時(shí)是根據(jù)key的hash值判斷元素是否相同,即unordered_map內(nèi)部元素是無序的,而map中的元素是按照二叉搜索樹存儲(chǔ),進(jìn)行中序遍歷會(huì)得到有序遍歷。所以使用時(shí)map的key需要定義operator<。而unordered_map需要定義hash_value函數(shù)并且重載operator==。但是很多系統(tǒng)內(nèi)置的數(shù)據(jù)類型都自帶這些,那么如果是自定義類型,那么就需要自己重載operator<或者h(yuǎn)ash_value()了。如果需要內(nèi)部元素自動(dòng)排序,使用map,不需要排序使用unordered_mapunordered_map的底層實(shí)現(xiàn)是hash_table;hash_map底層使用的是hash_table,而hash_table使用的開鏈法進(jìn)行沖突避免,所有hash_map采用開鏈法進(jìn)行沖突解決。什么時(shí)候擴(kuò)容:當(dāng)向容器添加元素的時(shí)候,會(huì)判斷當(dāng)前容器的元素個(gè)數(shù),如果大于等于閾值---即當(dāng)前數(shù)組的長度乘以加載因子的值的時(shí)候,就要自動(dòng)擴(kuò)容啦。擴(kuò)容(resize)就是重新計(jì)算容量,向HashMap對象里不停的添加元素,而HashMap對象內(nèi)部的數(shù)組無法裝載更多的元素時(shí),對象就需要擴(kuò)大數(shù)組的長度,以便能裝入更多的元素。vector越界訪問下標(biāo),map越界訪問下標(biāo)?vector刪除元素時(shí)會(huì)不會(huì)釋放空間?通過下標(biāo)訪問vector中的元素時(shí)不會(huì)做邊界檢查,即便下標(biāo)越界。也就是說,下標(biāo)與first迭代器相加的結(jié)果超過了finish迭代器的位置,程序也不會(huì)報(bào)錯(cuò),而是返回這個(gè)地址中存儲(chǔ)的值。如果想在訪問vector中的元素時(shí)首先進(jìn)行邊界檢查,可以使用vector中的at函數(shù)。通過使用at函數(shù)不但可以通過下標(biāo)訪問vector中的元素,而且在at函數(shù)內(nèi)部會(huì)對下標(biāo)進(jìn)行邊界檢查。map的下標(biāo)運(yùn)算符[]的作用是:將key作為下標(biāo)去執(zhí)行查找,并返回相應(yīng)的值;如果不存在這個(gè)key,就將一個(gè)具有該key和value的某人值插入這個(gè)map。erase()函數(shù),只能刪除內(nèi)容,不能改變?nèi)萘看笮?erase成員函數(shù),它刪除了itVect迭代器指向的元素,并且返回要被刪除的itVect之后的迭代器,迭代器相當(dāng)于一個(gè)智能指針;clear()函數(shù),只能清空內(nèi)容,不能改變?nèi)萘看笮?如果要想在刪除內(nèi)容的同時(shí)釋放內(nèi)存,那么你可以選擇deque容器。map[]與find的區(qū)別?map的下標(biāo)運(yùn)算符[]的作用是:將關(guān)鍵碼作為下標(biāo)去執(zhí)行查找,并返回對應(yīng)的值;如果不存在這個(gè)關(guān)鍵碼,就將一個(gè)具有該關(guān)鍵碼和值類型的默認(rèn)值的項(xiàng)插入這個(gè)map。map的find函數(shù):用關(guān)鍵碼執(zhí)行查找,找到了返回該位置的迭代器;如果不存在這個(gè)關(guān)鍵碼,就返回尾迭代器。STL中l(wèi)ist與queue之間的區(qū)別list不再能夠像vector一樣以普通指針作為迭代器,因?yàn)槠涔?jié)點(diǎn)不保證在存儲(chǔ)空間中連續(xù)存在;list插入操作和結(jié)合才做都不會(huì)造成原有的list迭代器失效;list不僅是一個(gè)雙向鏈表,而且還是一個(gè)環(huán)狀雙向鏈表,所以它只需要一個(gè)指針;list不像vector那樣有可能在空間不足時(shí)做重新配置、數(shù)據(jù)移動(dòng)的操作,所以插入前的所有迭代器在插入操作之后都仍然有效;deque是一種雙向開口的連續(xù)線性空間,所謂雙向開口,意思是可以在頭尾兩端分別做元素的插入和刪除操作;可以在頭尾兩端分別做元素的插入和刪除操作;deque和vector最大的差異,一在于deque允許常數(shù)時(shí)間內(nèi)對起頭端進(jìn)行元素的插入或移除操作,二在于deque沒有所謂容量概念,因?yàn)樗莿?dòng)態(tài)地以分段連續(xù)空間組合而成,隨時(shí)可以增加一段新的空間并鏈接起來,deque沒有所謂的空間保留功能。STL中的allocator,deallocator第一級配置器直接使用malloc()、free()和relloc(),第二級配置器視情況采用不同的策略:當(dāng)配置區(qū)塊超過128bytes時(shí),視之為足夠大,便調(diào)用第一級配置器;當(dāng)配置器區(qū)塊小于128bytes時(shí),為了降低額外負(fù)擔(dān),使用復(fù)雜的內(nèi)存池整理方式,而不再用一級配置器;第二級配置器主動(dòng)將任何小額區(qū)塊的內(nèi)存需求量上調(diào)至8的倍數(shù),并維護(hù)16個(gè)free-list,各自管理大小為8~128bytes的小額區(qū)塊;空間配置函數(shù)allocate(),首先判斷區(qū)塊大小,大于128就直接調(diào)用第一級配置器,小于128時(shí)就檢查對應(yīng)的free-list。如果free-list之內(nèi)有可用區(qū)塊,就直接拿來用,如果沒有可用區(qū)塊,就將區(qū)塊大小調(diào)整至8的倍數(shù),然后調(diào)用refill(),為free-list重新分配空間;空間釋放函數(shù)deallocate(),該函數(shù)首先判斷區(qū)塊大小,大于128bytes時(shí),直接調(diào)用一級配置器,小于128bytes就找到對應(yīng)的free-list然后釋放內(nèi)存。STL中hash_map擴(kuò)容發(fā)生什么?

hashtable表格內(nèi)的元素稱為桶(bucket),而由桶所鏈接的元素稱為節(jié)點(diǎn)(node),其中存入桶元素的容器為stl本身很重要的一種序列式容器——vector容器。之所以選擇vector為存放桶元素的基礎(chǔ)容器,主要是因?yàn)関ector容器本身具有動(dòng)態(tài)擴(kuò)容能力,無需人工干預(yù)。向前操作:首先嘗試從目前所指的節(jié)點(diǎn)出發(fā),前進(jìn)一個(gè)位置(節(jié)點(diǎn)),由于節(jié)點(diǎn)被安置于list內(nèi),所以利用節(jié)點(diǎn)的next指針即可輕易完成前進(jìn)操作,如果目前正巧是list的尾端,就跳至下一個(gè)bucket身上,那正是指向下一個(gè)list的頭部節(jié)點(diǎn)。map如何創(chuàng)建?1.vector

底層數(shù)據(jù)結(jié)構(gòu)為數(shù)組,支持快速隨機(jī)訪問2.list

底層數(shù)據(jù)結(jié)構(gòu)為雙向鏈表,支持快速增刪3.deque

底層數(shù)據(jù)結(jié)構(gòu)為一個(gè)中央控制器和多個(gè)緩沖區(qū),詳細(xì)見STL源碼剖析P146,支持首尾(中間不能)快速增刪,也支持隨機(jī)訪問deque是一個(gè)雙端隊(duì)列(double-endedqueue),也是在堆中保存內(nèi)容的.它的保存形式如下:[堆1]-->

[堆2]-->[堆3]-->...每個(gè)堆保存好幾個(gè)元素,然后堆和堆之間有指針指向,看起來像是list和vector的結(jié)合品.4.stack

底層一般用list或deque實(shí)現(xiàn),封閉頭部即可,不用vector的原因應(yīng)該是容量大小有限制,擴(kuò)容耗時(shí)5.queue

底層一般用list或deque實(shí)現(xiàn),封閉頭部即可,不用vector的原因應(yīng)該是容量大小有限制,擴(kuò)容耗時(shí)(stack和queue其實(shí)是適配器,而不叫容器,因?yàn)槭菍θ萜鞯脑俜庋b)6.priority_queue

的底層數(shù)據(jù)結(jié)構(gòu)一般為vector為底層容器,堆heap為處理規(guī)則來管理底層容器實(shí)現(xiàn)7.set

底層數(shù)據(jù)結(jié)構(gòu)為紅黑樹,有序,不重復(fù)8.multiset

底層數(shù)據(jù)結(jié)構(gòu)為紅黑樹,有序,可重復(fù)

9.map

底層數(shù)據(jù)結(jié)構(gòu)為紅黑樹,有序,不重復(fù)10.multimap

底層數(shù)據(jù)結(jié)構(gòu)為紅黑樹,有序,可重復(fù)11.hash_set

底層數(shù)據(jù)結(jié)構(gòu)為hash表,無序,不重復(fù)12.hash_multiset底層數(shù)據(jù)結(jié)構(gòu)為hash表,無序,可重復(fù)

13.hash_map

底層數(shù)據(jù)結(jié)構(gòu)為hash表,無序,不重復(fù)14.hash_multimap底層數(shù)據(jù)結(jié)構(gòu)為hash表,無序,可重復(fù)

vector的增加刪除都是怎么做的?為什么是1.5倍?新增元素:vector通過一個(gè)連續(xù)的數(shù)組存放元素,如果集合已滿,在新增數(shù)據(jù)的時(shí)候,就要分配一塊更大的內(nèi)存,將原來的數(shù)據(jù)復(fù)制過來,釋放之前的內(nèi)存,在插入新增的元素;對vector的任何操作,一旦引起空間重新配置,指向原vector的所有迭代器就都失效了;初始時(shí)刻vector的capacity為0,塞入第一個(gè)元素后capacity增加為1;不同的編譯器實(shí)現(xiàn)的擴(kuò)容方式不一樣,VS2015中以1.5倍擴(kuò)容,GCC以2倍擴(kuò)容。對比可以發(fā)現(xiàn)采用采用成倍方式擴(kuò)容,可以保證常數(shù)的時(shí)間復(fù)雜度,而增加指定大小的容量只能達(dá)到O(n)的時(shí)間復(fù)雜度,因此,使用成倍的方式擴(kuò)容。考慮可能產(chǎn)生的堆空間浪費(fèi),成倍增長倍數(shù)不能太大,使用較為廣泛的擴(kuò)容方式有兩種,以2二倍的方式擴(kuò)容,或者以1.5倍的方式擴(kuò)容。以2倍的方式擴(kuò)容,導(dǎo)致下一次申請的內(nèi)存必然大于之前分配內(nèi)存的總和,導(dǎo)致之前分配的內(nèi)存不能再被使用,所以最好倍增長因子設(shè)置為(1,2)之間:

向量容器vector的成員函數(shù)pop_back()可以刪除最后一個(gè)元素.而函數(shù)erase()可以刪除由一個(gè)iterator指出的元素,也可以刪除一個(gè)指定范圍的元素。還可以采用通用算法remove()來刪除vector容器中的元素.不同的是:采用remove一般情況下不會(huì)改變?nèi)萜鞯拇笮?,而pop_back()與erase()等成員函數(shù)會(huì)改變?nèi)萜鞯拇笮 ause函數(shù)指針?什么是函數(shù)指針?函數(shù)指針指向的是特殊的數(shù)據(jù)類型,函數(shù)的類型是由其返回的數(shù)據(jù)類型和其參數(shù)列表共同決定的,而函數(shù)的名稱則不是其類型的一部分。一個(gè)具體函數(shù)的名字,如果后面不跟調(diào)用符號(即括號),則該名字就是該函數(shù)的指針(注意:大部分情況下,可以這么認(rèn)為,但這種說法并不很嚴(yán)格)。函數(shù)指針的聲明方法int

(*pf)(const

int&,

const

int&);(1)上面的pf就是一個(gè)函數(shù)指針,指向所有返回類型為int,并帶有兩個(gè)constint&參數(shù)的函數(shù)。注意*pf兩邊的括號是必須的,否則上面的定義就變成了:int

*pf(const

int&,

const

int&);(2)而這聲明了一個(gè)函數(shù)pf,其返回類型為int*,帶有兩個(gè)constint&參數(shù)。為什么有函數(shù)指針函數(shù)與數(shù)據(jù)項(xiàng)相似,函數(shù)也有地址。我們希望在同一個(gè)函數(shù)中通過使用相同的形參在不同的時(shí)間使用產(chǎn)生不同的效果。一個(gè)函數(shù)名就是一個(gè)指針,它指向函數(shù)的代碼。一個(gè)函數(shù)地址是該函數(shù)的進(jìn)入點(diǎn),也就是調(diào)用函數(shù)的地址。函數(shù)的調(diào)用可以通過函數(shù)名,也可以通過指向函數(shù)的指針來調(diào)用。函數(shù)指針還允許將函數(shù)作為變元傳遞給其他函數(shù);兩種方法賦值:指針名=函數(shù)名;指針名=&函數(shù)名說說你對c和c++的看法,c和c++的區(qū)別?第一點(diǎn)就應(yīng)該想到C是面向過程的語言,而C++是面向?qū)ο蟮恼Z言,一般簡歷上第一條都是熟悉C/C++基本語法,了解C++面向?qū)ο笏枷耄敲?,請問什么是面向?qū)ο螅緾和C++動(dòng)態(tài)管理內(nèi)存的方法不一樣,C是使用malloc/free函數(shù),而C++除此之外還有new/delete關(guān)鍵字;(關(guān)于malooc/free與new/delete的不同又可以說一大堆,最后的擴(kuò)展_1部分列出十大區(qū)別);接下來就不得不談到C中的struct和C++的類,C++的類是C所沒有的,但是C中的struct是可以在C++中正常使用的,并且C++對struct進(jìn)行了進(jìn)一步的擴(kuò)展,使struct在C++中可以和class一樣當(dāng)做類使用,而唯一和class不同的地方在于struct的成員默認(rèn)訪問修飾符是public,而class默認(rèn)的是private;C++支持函數(shù)重載,而C不支持函數(shù)重載,而C++支持重載的依仗就在于C++的名字修飾與C不同,例如在C++中函數(shù)intfun(int,int)經(jīng)過名字修飾之后變?yōu)開fun_int_int,而C是

_fun,一般是這樣的,所以C++才會(huì)支持不同的參數(shù)調(diào)用不同的函數(shù);C++中有引用,而C沒有;這樣就不得不提一下引用和指針的區(qū)別(文后擴(kuò)展_2);當(dāng)然還有C++全部變量的默認(rèn)鏈接屬性是外鏈接,而C是內(nèi)連接;C中用const修飾的變量不可以用在定義數(shù)組時(shí)的大小,但是C++用const修飾的變量可以(如果不進(jìn)行&,解引用的操作的話,是存放在符號表的,不開辟內(nèi)存);當(dāng)然還有局部變量的聲明規(guī)則不同,多態(tài),C++特有輸入輸出流之類的,很多,下面就不再列出來了;“`c/c++的內(nèi)存分配,詳細(xì)說一下棧、堆、靜態(tài)存儲(chǔ)區(qū)?1、棧區(qū)(stack)—

由編譯器自動(dòng)分配釋放,存放函數(shù)的參數(shù)值,局部變量的值等其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。

2、堆區(qū)(heap)

一般由程序員分配釋放,若程序員不釋放,程序結(jié)束時(shí)可能由OS(操作系統(tǒng))回收。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式倒是類似于鏈表。

3、全局區(qū)(靜態(tài)區(qū))(static)—,全局變量和靜態(tài)變量的存儲(chǔ)是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。程序結(jié)束后由系統(tǒng)釋放。

4、文字常量區(qū)

—常量字符串就是放在這里的。程序結(jié)束后由系統(tǒng)釋放。

5、程序代碼區(qū)—存放函數(shù)體的二進(jìn)制代碼。

堆與棧的區(qū)別?管理方式:對于棧來講,是由編譯器自動(dòng)管理,無需我們手工控制;對于堆來說,釋放工作由程序員控制,容易產(chǎn)生memoryleak。

空間大?。阂话銇碇v在32位系統(tǒng)下,堆內(nèi)存可以達(dá)到4G的空間,從這個(gè)角度來看堆內(nèi)存幾乎是沒有什么限制的。但是對于棧來講,一般都是有一定的空間大小的,例如,在VC6下面,默認(rèn)的??臻g大小是1M(好像是,記不清楚了)。當(dāng)然,我們可以修改:

打開工程,依次操作菜單如下:Project->Setting->Link,在Category中選中Output,然后在Reserve中設(shè)定堆棧的最大值和commit。注意:reserve最小值為4Byte;commit是保留在虛擬內(nèi)存的頁文件里面,它設(shè)置的較大會(huì)使棧開辟較大的值,可能增加內(nèi)存的開銷和啟動(dòng)時(shí)間。

碎片問題:對于堆來講,頻繁的new/delete勢必會(huì)造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。對于棧來講,則不會(huì)存在這個(gè)問題,因?yàn)闂J窍冗M(jìn)后出的隊(duì)列,他們是如此的一一對應(yīng),以至于永遠(yuǎn)都不可能有一個(gè)內(nèi)存塊從棧中間彈出,在他彈出之前,在他上面的后進(jìn)的棧內(nèi)容已經(jīng)被彈出,詳細(xì)的可以參考數(shù)據(jù)結(jié)構(gòu),這里我們就不再一一討論了。

生長方向:對于堆來講,生

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論