日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

程序员面试宝典

發(fā)布時間:2024/9/27 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 程序员面试宝典 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

第一章 類

1.1 為什么構(gòu)造函數(shù)不可以是虛函數(shù)

①從存儲空間角度

? ? 虛函數(shù)對應(yīng)一個vtable(虛函數(shù)表),這大家都知道,可是這個vtable其實(shí)是存儲在對象的內(nèi)存空間的。問題出來了,如果構(gòu)造函數(shù)是虛的,就需要通過 vtable來調(diào)用,可是對象還沒有實(shí)例化,也就是內(nèi)存空間還沒有,無法找到vtable,所以構(gòu)造函數(shù)不能是虛函數(shù)。

②從使用角度

? ? ? ? 虛函數(shù)主要用于在信息不全的情況下,能使重載的函數(shù)得到對應(yīng)的調(diào)用。構(gòu)造函數(shù)本身就是要初始化實(shí)例,那使用虛函數(shù)也沒有實(shí)際意義呀。所以構(gòu)造函數(shù)沒有必要是虛函數(shù)。

虛函數(shù)的作用在于通過父類的指針或者引用來調(diào)用它的時候能夠變成調(diào)用子類的那個成員函數(shù)。而構(gòu)造函數(shù)是在創(chuàng)建對象時自動調(diào)用的,不可能通過父類的指針或者引用去調(diào)用,因此也就規(guī)定構(gòu)造函數(shù)不能是虛函數(shù)。

③構(gòu)造函數(shù)不需要是虛函數(shù),也不允許是虛函數(shù),因?yàn)閯?chuàng)建一個對象時我們總是要明確指定對象的類型,盡管我們可能通過實(shí)驗(yàn)室的基類的指針或引用去訪問它。但析構(gòu)卻不一定,我們往往通過基類的指針來銷毀對象。這時候如果析構(gòu)函數(shù)不是虛函數(shù),就不能正確識別對象類型從而不能正確調(diào)用析構(gòu)函數(shù)。

④從實(shí)現(xiàn)上看,vbtl在構(gòu)造函數(shù)調(diào)用后才建立,因而構(gòu)造函數(shù)不可能成為虛函數(shù) ?

? 從實(shí)際含義上看,在調(diào)用構(gòu)造函數(shù)時還不能確定對象的真實(shí)類型(因?yàn)樽宇悤{(diào)父類的構(gòu)造函數(shù));而且構(gòu)造函數(shù)的作用是提供初始化,在對象生命期只執(zhí)行一次,不是對象的動態(tài)行為,也沒有太大的必要成為虛函數(shù)

⑤當(dāng)一個構(gòu)造函數(shù)被調(diào)用時,它做的首要的事情之一是初始化它的V P T R。因此,它只能知道它是“當(dāng)前”類的,而完全忽視這個對象后面是否還有繼承者。當(dāng)編譯器為這個構(gòu)造函數(shù)產(chǎn)生代碼時,它是為這個類的構(gòu)造函數(shù)產(chǎn)生代碼- -既不是為基類,也不是為它的派生類(因?yàn)轭惒恢勒l繼承它)。

? ? ? ? 所以它使用的V P T R必須是對于這個類的V TA B L E。而且,只要它是最后的構(gòu)造函數(shù)調(diào)用,那么在這個對象的生命期內(nèi), V P T R將保持被初始化為指向這個V TA B L E, 但如果接著還有一個更晚派生的構(gòu)造函數(shù)被調(diào)用,這個構(gòu)造函數(shù)又將設(shè)置V P T R指向它的 V TA B L E,等.直到最后的構(gòu)造函數(shù)結(jié)束。V P T R的狀態(tài)是由被最后調(diào)用的構(gòu)造函數(shù)確定的。這就是為什么構(gòu)造函數(shù)調(diào)用是從基類到更加派生 類順序的另一個理由。

? ? ? ? 但是,當(dāng)這一系列構(gòu)造函數(shù)調(diào)用正發(fā)生時,每個構(gòu)造函數(shù)都已經(jīng)設(shè)置V P T R指向它自己的 V TA B L E。如果函數(shù)調(diào)用使用虛機(jī)制,它將只產(chǎn)生通過它自己的V TA B L E的調(diào)用,而不是最后的V TA B L E(所有構(gòu)造函數(shù)被調(diào)用后才會有最后的V TA B L E)。

1.2、為什么析構(gòu)函數(shù)可以是虛函數(shù)

? ? ? 編譯器總是根據(jù)類型來調(diào)用類成員函數(shù)。但是一個派生類的指針可以安全地轉(zhuǎn)化為一個基類的指針。這樣刪除一個基類的指針的時候,C++不管這個指針指向一個基類對象還是一個派生類的對象,調(diào)用的都是基類的析構(gòu)函數(shù)而不是派生類的。如果你依賴于派生類的析構(gòu)函數(shù)的代碼來釋放資源,而沒有重載析構(gòu)函數(shù),那么會有資源泄漏。
? ? ? 所以建議的方式是將析構(gòu)函數(shù)聲明為虛函數(shù)。如果你使用MFC,并且以CObject或其派生類為基類,那么MFC已經(jīng)為你做了這件事情;CObject的析構(gòu)函數(shù)是虛函數(shù)。一個函數(shù)一旦聲明為虛函數(shù),那么不管你是否加上virtual 修飾符,它在所有派生類中都成為虛函數(shù)。但是由于理解明確起見,建議的方式還是加上virtual 修飾符。
? ? ? C++不把虛析構(gòu)函數(shù)直接作為默認(rèn)值的原因是虛函數(shù)表的開銷以及和C語言的類型的兼容性。有虛函數(shù)的對象總是在開始的位置包含一個隱含的虛函數(shù)表指針成員。如果是對于MFC類CPoint和CSize這樣的小型類,增加一個指針就增加了很多內(nèi)存占用,而且使得其內(nèi)存表示和基類POINT和SIZE不一致。

2.虛函數(shù)表

? ? ?請移步:https://www.cnblogs.com/LUO77/p/5771237.html

3.動態(tài)綁定與靜態(tài)綁定

  • 靜態(tài)綁定發(fā)生在編譯期,動態(tài)綁定發(fā)生在運(yùn)行期;
  • 對象的動態(tài)類型可以更改,但是靜態(tài)類型無法更改;
  • 要想實(shí)現(xiàn)動態(tài),必須使用動態(tài)綁定;
  • 在繼承體系中只有虛函數(shù)使用的是動態(tài)綁定,其他的全部是靜態(tài)綁定;
  • 靜態(tài)多態(tài)是指通過模板技術(shù)或者函數(shù)重載技術(shù)實(shí)現(xiàn)的多態(tài),其在編譯器確定行為。動態(tài)多態(tài)是指通過虛函數(shù)技術(shù)實(shí)現(xiàn)在運(yùn)行期動態(tài)綁定的技術(shù)

動態(tài)綁定:有一個基類,兩個派生類,基類有一個virtual函數(shù),兩個派生類都覆蓋了這個虛函數(shù)。現(xiàn)在有一個基類的指針或者引用,當(dāng)該基類指針或者引用指向不同的派生類對象時,調(diào)用該虛函數(shù),那么最終調(diào)用的是該被指向?qū)ο髮?yīng)的派生類自己實(shí)現(xiàn)的虛函數(shù)。

4.虛函數(shù)表是針對類的還是針對對象的?同一個類的兩個對象的虛函數(shù)表是怎么維護(hù)的?

編譯器為每一個類維護(hù)一個虛函數(shù)表(本質(zhì)是一個函數(shù)指針數(shù)組,數(shù)組里面存放了一系列函數(shù)地址 ),每個對象的首地址保存著該虛函數(shù)表的指針,同一個類的不同對象實(shí)際上指向同一張?zhí)摵瘮?shù)表。調(diào)用形式:*(this指針+調(diào)整量)[虛函數(shù)在vftable內(nèi)的偏移]()

在類內(nèi)部添加一個虛擬函數(shù)表指針,該指針指向一個虛擬函數(shù)表,該虛擬函數(shù)表包含了所有的虛擬函數(shù)的入口地址,每個類的虛擬函數(shù)表都不一樣,在運(yùn)行階段可以循此脈絡(luò)找到自己的函數(shù)入口。純虛函數(shù)相當(dāng)于占位符, 先在虛函數(shù)表中占一個位置由派生類實(shí)現(xiàn)后再把真正的函數(shù)指針填進(jìn)去。除此之外和普通的虛函數(shù)沒什么區(qū)別。

在單繼承形式下,子類的完全獲得父類的虛函數(shù)表和數(shù)據(jù)。子類如果重寫了父類的虛函數(shù)(如fun),就會把虛函數(shù)表原本fun對應(yīng)的記錄(內(nèi)容MyClass::fun)覆蓋為新的函數(shù)地址(內(nèi)容MyClassA::fun),否則繼續(xù)保持原本的函數(shù)地址記錄。

使用這種方式,就可以實(shí)現(xiàn)多態(tài)的特性。假設(shè)我們使用如下語句:

  • MyClass*pc= new MyClassA;

  • pc->fun();??

  • 因?yàn)樘摵瘮?shù)表內(nèi)的函數(shù)地址已經(jīng)被子類重寫的fun函數(shù)地址覆蓋了,因此該處調(diào)用的函數(shù)正是MyClassA::fun,而不是基類的MyClass::fun。

    如果使用MyClassA對象直接訪問fun,則不會出發(fā)多態(tài)機(jī)制,因?yàn)檫@個函數(shù)調(diào)用在編譯時期是可以確定的,編譯器只需要直接調(diào)用MyClassA::fun即可。

    注:對象不包含虛函數(shù)表,只有虛指針,類才包含虛函數(shù)表,派生類會生成一個兼容基類的虛函數(shù)表

    詳情可以參考:http://www.cnblogs.com/fanzhidongyzby/archive/2013/01/14/2859064.html

    第二章 其他

    2.?extern關(guān)鍵字的作用

    ? ? ?2.1 概述

    ? ? ? ?extern置于變量或函數(shù)前,用于標(biāo)示變量或函數(shù)的定義在別的文件中,提示編譯器遇到此變量和函數(shù)時在其他模塊中尋找其定義。它只要有兩個作用:

    • 當(dāng)它與“C”一起連用的時候,如:extern "C" void fun(int a,int b);則告訴編譯器在編譯fun這個函數(shù)時候按著C的規(guī)矩去翻譯,而不是C++的(這與C++的重載有關(guān),C++語言支持函數(shù)重載,C語言不支持函數(shù)重載,函數(shù)被C++編譯器編譯后在庫中的名字與C語言的不同) #ifdef __cplusplus extern "C" { #endif/*...*/#ifdef __cplusplus } #endif

    • 當(dāng)extern不與“C”在一起修飾變量或函數(shù)時,如:extern int g_Int;它的作用就是聲明函數(shù)或全局變量的作用范圍的關(guān)鍵字,其聲明的函數(shù)和變量可以在本模塊或其他模塊中使用。記住它是一個聲明不是定義!也就是說B模塊(編譯單元)要是引用模塊(編譯單元)A中定義的全局變量或函數(shù)時,它只要包含A模塊的頭文件即可,在編譯階段,模塊B雖然找不到該函數(shù)或變量,但它不會報錯,它會在連接時從模塊A生成的目標(biāo)代碼中找到此函數(shù)。

    ? ?2.2 .為什么不將全局變量的定義放在頭文件中

    ? ? ? ?首先要說明什么是全局變量,c語言中全局變量一般是指定義在函數(shù)體外的變量。全局變量按可訪問性可分為外部變量內(nèi)部變量

    ? ? ? 內(nèi)部變量是指?使用了static關(guān)鍵字修飾的全局變量,它的可訪問范圍(作用域)被限定在本源文件所在的鏈接文件模塊中,不能被其它文件模塊引用。反之沒有被static關(guān)鍵字修飾的全局變量則是外部變量,其它文件模塊可以通過extern關(guān)鍵字引用該全局變量并訪問。

    要說明的是全局變量?無論是內(nèi)部變量還是外部變量,的存儲類別都是靜態(tài)的,也就是放到靜態(tài)內(nèi)存區(qū)域中,它編譯鏈接階段就已經(jīng)分配好了固定的內(nèi)存

    搞清楚上面的內(nèi)容,就很容易得出若把全局變量放在頭文件會有哪些問題;

    一 對內(nèi)部變量來說,每個include該頭文件的文件模塊中都會單獨(dú)為這個內(nèi)部變量分配靜態(tài)內(nèi)存空間,這個空間是相對獨(dú)立的,是一種空間浪費(fèi),同時還失去了全局變量訪問一致性的特點(diǎn),實(shí)在沒有什么意義。如果這個頭文件只被一個模塊使用,對于這個文件模塊來說應(yīng)該沒啥問題。

    二 對外部變量來講,這個頭文件被多個文件模塊include的情況下,鏈接過程會報錯,因?yàn)榉枦_突,所有include這個頭文件的模塊都會有這個全局符號。在這個頭文件僅僅只被一個模塊include的時候可以正常使用。

    經(jīng)上分析得出要避免全局變量定義在頭文件中,因?yàn)楫?dāng)這個頭文件被多方include的時候會產(chǎn)生一些不必要的麻煩,就這么多。

    全局變量作用域范圍較廣,被錯誤修改后排查定位問題比較困難,若非必要盡少使用。

    下面說一下比較好的方式就是全局變量只定義在實(shí)現(xiàn)文件(.c,.m)中,對內(nèi)部變量沒啥說的它只在文件模塊內(nèi)部使用,對外部變量可以在該模塊頭文件中使用extern關(guān)鍵字修飾一下,這樣其它文件模塊只要直接include該頭文件就可以使用模塊中的外部變量了。

    2.2.static關(guān)鍵字的作用

    • ? 修飾局部變量

    ? ??static修飾局部變量時,使得被修飾的變量成為靜態(tài)變量,存儲在靜態(tài)區(qū)。存儲在靜態(tài)區(qū)的數(shù)據(jù)生命周期與程序相同,在main函數(shù)之前初始化,在程序退出時銷毀。(無論是局部靜態(tài)還是全局靜態(tài))

    • ? 修飾全局變量

    ? ? 全局變量本來就存儲在靜態(tài)區(qū),因此static并不能改變其存儲位置。但是,static限制了其鏈接屬性。被static修飾的全局變量只能被該包含該定義的文件訪問(即改變了作用域)。

    • ? 修飾函數(shù)

    ? ? ?static修飾函數(shù)使得函數(shù)只能在包含該函數(shù)定義的文件中被調(diào)用。對于靜態(tài)函數(shù),聲明和定義需要放在同一個文件夾中。

    • ? 修飾成員變量

    ? ? ??用static修飾類的數(shù)據(jù)成員使其成為類的全局變量,會被類的所有對象共享,包括派生類的對象,所有的對象都只維持同一個實(shí)例。?因此,static成員必須在類外進(jìn)行初始化(初始化格式:int base::var=10;),而不能在構(gòu)造函數(shù)內(nèi)進(jìn)行初始化,不過也可以用const修飾static數(shù)據(jù)成員在類內(nèi)初始化。

    • ? 修飾成員函數(shù)

    ? ? ?用static修飾成員函數(shù),使這個類只存在這一份函數(shù),所有對象共享該函數(shù),不含this指針,因而只能訪問類的static成員變量。靜態(tài)成員是可以獨(dú)立訪問的,也就是說,無須創(chuàng)建任何對象實(shí)例就可以訪問。例如可以封裝某些算法,比如數(shù)學(xué)函數(shù),如ln,sin,tan等等,這些函數(shù)本就沒必要屬于任何一個對象,所以從類上調(diào)用感覺更好,比如定義一個數(shù)學(xué)函數(shù)類Math,調(diào)用Math::sin(3.14);還可以實(shí)現(xiàn)某些特殊的設(shè)計模式:如Singleton;

    • 最重要的特性:隱藏

    ? ? 當(dāng)同時編譯多個文件時,所有未加static前綴的全局變量和函數(shù)都具有全局可見性,其它的源文件也能訪問。利用這一特性可以在不同的文件中定義同名函數(shù)和同名變量,而不必?fù)?dān)心命名沖突。static可以用作函數(shù)和變量的前綴,對于函數(shù)來講,static的作用僅限于隱藏。

    不可以同時用const和static修飾成員函數(shù)。

    ? ?C++編譯器在實(shí)現(xiàn)const的成員函數(shù)的時候?yàn)榱舜_保該函數(shù)不能修改類的實(shí)例的狀態(tài),會在函數(shù)中添加一個隱式的參數(shù)const this*。但當(dāng)一個成員為static的時候,該函數(shù)是沒有this指針的。也就是說此時const的用法和static是沖突的。我們也可以這樣理解:兩者的語意是矛盾的。static的作用是表示該函數(shù)只作用在類型的靜態(tài)變量上,與類的實(shí)例沒有關(guān)系;而const的作用是確保函數(shù)不能修改類的實(shí)例的狀態(tài),與類型的靜態(tài)變量沒有關(guān)系。因此不能同時用它們。

    2.3.const的作用

    • 定義變量為只讀變量,不可修改
    • 修飾函數(shù)的參數(shù)和返回值(后者應(yīng)用比較少,一般為值傳遞)
    • const成員函數(shù)(只需要在成員函數(shù)參數(shù)列表后加上關(guān)鍵字const,如char?get()?const;)可以訪問const成員變量和非const成員變量,但不能修改任何變量。在聲明一個成員函數(shù)時,若該成員函數(shù)并不對數(shù)據(jù)成員進(jìn)行修改操作,應(yīng)盡可能將該成員函數(shù)聲明為const成員函數(shù)。
    • const對象只能訪問const成員函數(shù),而非const對象可以訪問任意的成員函數(shù),包括const成員函數(shù).即對于class A,有const A a;那么a只能訪問A的const成員函數(shù)。而對于:A b;b可以訪問任何成員函數(shù)。

      使用const關(guān)鍵字修飾的變量,一定要對變量進(jìn)行初始化

    2.4.指針與引用的區(qū)別

    • 指針只是一個變量,只不過這個變量存儲的是一個地址;而引用跟原來的變量實(shí)質(zhì)上是同一個東西,只不過是原變量的一個別名而已,不占用內(nèi)存空間。
    • 引用必須在定義的時候初始化,而且初始化后就不能再改變;而指針不必在定義的時候初始化,初始化后可以改變。
    • 指針可以為空,但引用不能為空(這就意味著我們拿到一個引用的時候,是不需要判斷引用是否為空的,而拿到一個指針的時候,我們則需要判斷它是否為空。這點(diǎn)經(jīng)常在判斷函數(shù)參數(shù)是否有效的時候使用。)
    • “sizeof 引用" = 指向變量的大小 , "sizeof 指針"= 指針本身的大小
    • 指針可以有多級,而引用只能是一級

    2.5.new與malloc的區(qū)別

    • malloc與free是C++/C語言的標(biāo)準(zhǔn)庫函數(shù),new/delete是C++的運(yùn)算符。它們都可用于申請動態(tài)內(nèi)存和釋放內(nèi)存。
    • 對于非內(nèi)部數(shù)據(jù)類型的對象而言,光用malloc/free無法滿足動態(tài)對象的要求。對象在創(chuàng)建的同時要自動執(zhí)行構(gòu)造函數(shù),對象在消亡之前要自動執(zhí)行析構(gòu)函數(shù)。
    • new可以認(rèn)為是malloc加構(gòu)造函數(shù)的執(zhí)行。new出來的指針是直接帶類型信息的。而malloc返回的都是void指針。

    2.6.智能指針怎么實(shí)現(xiàn)?什么時候改變引用計數(shù)?

    • 構(gòu)造函數(shù)中計數(shù)初始化為1;
    • 拷貝構(gòu)造函數(shù)中計數(shù)值加1;
    • 賦值運(yùn)算符中,左邊的對象引用計數(shù)減一,右邊的對象引用計數(shù)加一;
    • 析構(gòu)函數(shù)中引用計數(shù)減一;
    • 在賦值運(yùn)算符和析構(gòu)函數(shù)中,如果減一后為0,則調(diào)用delete釋放對象。

    2.7.內(nèi)聯(lián)函數(shù),宏定義和普通函數(shù)的區(qū)別

    • 內(nèi)聯(lián)函數(shù)要做參數(shù)類型檢查,這是內(nèi)聯(lián)函數(shù)跟宏相比的優(yōu)勢
    • 宏定義是在預(yù)編譯的時候把所有的宏名用宏體來替換,簡單的說就是字符串替換,?內(nèi)聯(lián)函數(shù)則是在編譯的時候進(jìn)行代碼插入,編譯器會在每處調(diào)用內(nèi)聯(lián)函數(shù)的地方直接把內(nèi)聯(lián)函數(shù)的內(nèi)容展開,這樣可以省去函數(shù)的調(diào)用的壓棧出棧的開銷,提高效率。
    • 內(nèi)聯(lián)函數(shù)是指嵌入代碼,就是在調(diào)用函數(shù)的地方不是跳轉(zhuǎn),而是把代碼直接寫到那里去。對于短小簡單的代碼來說,內(nèi)聯(lián)函數(shù)可以帶來一定的效率提升,而且和C時代的宏函數(shù)相比,內(nèi)聯(lián)函數(shù)?更安全可靠。可是這個是以增加空間消耗為代價的
    • const與#define的區(qū)別:宏在預(yù)處理階段替換,const在編譯階段替換;宏沒有類型,不做安全檢查,const有類型,在編譯階段進(jìn)行安全檢查

    2.8. C++內(nèi)存管理

    :? 存放函數(shù)參數(shù)以及局部變量 , 在出作用域時 , 將自動被釋放 . 棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中 , 效率 很 高 , 但分配的內(nèi)存容量有限 .

    ?:new 分配的內(nèi)存塊 ( 包括數(shù)組 , 類實(shí)例等 ), 需 delete 手動釋放 . 如果未釋放 , 在整個程序結(jié)束后 ,OS 會幫你回收掉 .

    自由存儲區(qū):?malloc 分配的內(nèi)存塊 , 需 free 手動釋放 . 它和堆有些相似 .

    全局/靜態(tài)區(qū):?保存自動全局變量和static變量(包括static全局和局部變量)。靜態(tài)區(qū)的內(nèi)容在整個程序的生命周期內(nèi)都存在,有編譯器在編譯的時候分配(數(shù)據(jù)段(存儲全局?jǐn)?shù)據(jù)和靜態(tài)數(shù)據(jù))和代碼段(可執(zhí)行的代碼/只讀常量))。

    常量存儲區(qū):?常量 (const) 存于此處 , 此存儲區(qū)不可修改 .

    棧與堆的區(qū)別:?? ? ? ?

    管理方式不同:?棧是編譯器自動管理的,堆需手動釋放

    空間大小不同:?在32位OS下,堆內(nèi)存可達(dá)到4GB的的空間,而棧就小得可憐.(VC6中,棧默認(rèn)大小是1M,當(dāng)然,你可以修改它)

    能否產(chǎn)生碎片不同:對于棧來說,進(jìn)棧/出棧都有著嚴(yán)格的順序(先進(jìn)后出),不會產(chǎn)生碎片;而堆頻繁的new/delete,會造成內(nèi)存空間的不連續(xù),容易產(chǎn)生碎片.

    生長方向不同:棧向下生長,以降序分配內(nèi)存地址;堆向上生長,以升序分配內(nèi)在地址.

    分配方式不同:堆動態(tài)分配,無靜態(tài)分配;棧分為靜態(tài)分配和動態(tài)分配,比如局部變量的分配,就是動態(tài)分配(alloca函數(shù)),函數(shù)參數(shù)的分配就是動態(tài)分配(我想的…).

    分配效率不同:棧是系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計算機(jī)會在底層對棧提供支持,進(jìn)棧/出棧都有專門的指令,這就決定了棧的效率比較高.堆則不然,它由C/C++函數(shù)庫提供,機(jī)制復(fù)雜,堆的效率要比棧低得多.

    可以看出,棧的效率要比堆高很多,所以,推薦大家盡量用棧.不過,雖然棧有如此多的好處,但遠(yuǎn)沒有堆使用靈活.

    第四章 實(shí)戰(zhàn)

    4.1 .手寫strcpy,memcpy,strcat,strcmp等函數(shù)

    https://blog.csdn.net/gao1440156051/article/details/51496782

    https://blog.csdn.net/wilsonboliu/article/details/7919773

    16.i++是否為原子操作?

    不是。操作系統(tǒng)原子操作是不可分割的,在執(zhí)行完畢不會被任何其它任務(wù)或事件中斷,分為兩種情況(兩種都應(yīng)該滿足)

    ?(1) 在單線程中, 能夠在單條指令中完成的操作都可以認(rèn)為是" 原子操作",因?yàn)橹袛嘀荒馨l(fā)生于指令之間。

    ?(2) 在多線程中,不能被其它進(jìn)程(線程)打斷的操作就叫原子操作。

    i++分為三個階段:

    內(nèi)存到寄存器
    寄存器自增
    寫回內(nèi)存
    這三個階段中間都可以被中斷分離開.

    17.有關(guān)數(shù)組,指針,函數(shù)的三者結(jié)合問題

    數(shù)組指針和指針數(shù)組的區(qū)別:https://blog.csdn.net/men_wen/article/details/52694069

    右左法則的說明:http://www.cnblogs.com/zhangjing0502/archive/2012/06/08/2542059.html

    指針常量和常量指針:https://www.zhihu.com/question/19829354

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://blog.csdn.net/xingjiarong/article/details/47282563

    注意:所謂指向常量的指針或引用(即常量引用、常量指針),不過是指針或引用“自以為是”罷了,它們覺得自己指向了常量,所以自覺地不去改變所指對象的值,但這些對象卻可以通過其他途徑改變。

    const int *a; ?等價于int const *a; ? ?const在前面所以內(nèi)容不可以改變,但是指針指向可以改變。也就是常量指針

    int *const a; ?表示的是指針指向不可改變,但是指針?biāo)娣诺膬?nèi)容可以改變,也即是指針常量

    補(bǔ)充:

    18.C++中類與結(jié)構(gòu)體的區(qū)別?

    • 最本質(zhì)的一個區(qū)別就是默認(rèn)的訪問控制:?struct作為數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)體,它默認(rèn)的數(shù)據(jù)訪問控制是public的,而class作為對象的實(shí)現(xiàn)體,它默認(rèn)的成員變量訪問控制是private的。
    • “class”這個關(guān)鍵字還用于定義模板參數(shù),就像“typename”。但關(guān)鍵字“struct”不用于定義模板參數(shù)。

    19.析構(gòu)函數(shù)的作用?

    析構(gòu)函數(shù)是用來釋放所定義的對象中使用的指針,默認(rèn)的析構(gòu)函數(shù)不用顯示調(diào)用,自建的析構(gòu)函數(shù)要在程序末尾調(diào)用。

    如果你的類里面只用到的基本類型,如int char double等,系統(tǒng)的默認(rèn)析構(gòu)函數(shù)其實(shí)什么都沒有做

    但如果你使用了其他的類如vector,string等,系統(tǒng)的默認(rèn)析構(gòu)函數(shù)就會調(diào)用這些類對象的析構(gòu)函數(shù)

    如果是自己寫析構(gòu)函數(shù)的話,如果你的類里面分配了系統(tǒng)資源,如new了內(nèi)存空間,打開了文件等,那么在你的析構(gòu)函數(shù)中就必須釋放相應(yīng)的內(nèi)存空間和關(guān)閉相關(guān)的文件;這樣系統(tǒng)就會自動調(diào)用你的析構(gòu)函數(shù)釋放資源,避免內(nèi)存泄漏

    例如:

  • class A

  • {

  • private:

  • char *data;

  • public:

  • A()

  • {

  • data = new char[ 10];

  • }

  • ~A()

  • {

  • delete[] data;

  • }

  • };

    • 1

    A? a;
    a?中將?new?10個?char
    當(dāng)?a?這個變量消亡的時候,將自動執(zhí)行?~A(),釋放空間

    對象消亡時,自動被調(diào)用,用來釋放對象占用的空間,避免內(nèi)存泄漏

    20.虛函數(shù)的作用?

    虛函數(shù)可以讓成員函數(shù)操作一般化,用基類的指針指向不同的派生類的對象時,基類指針調(diào)用其虛成員函數(shù),則會調(diào)用其真正指向?qū)ο蟮某蓡T函數(shù),而不是基類中定義的成員函數(shù)(只要派生類改寫了該成員函數(shù))。若不是虛函數(shù),則不管基類指針指向的哪個派生類對象,調(diào)用時都會調(diào)用基類中定義的那個函數(shù)。虛函數(shù)是C++多態(tài)的一種表現(xiàn),可以進(jìn)行靈活的動態(tài)綁定。

    ? ?? 重點(diǎn)可參考:https://www.cnblogs.com/wangxiaobao/p/5850949.html

    ? ? ? ? ? ? ? ? ? ? ?http://www.cnblogs.com/fanzhidongyzby/archive/2013/01/14/2859064.html

    21.操作系統(tǒng)和編譯器如何區(qū)分全局變量和局部變量?

    ? ? ? ?操作系統(tǒng)只管調(diào)度進(jìn)程,編譯器通過內(nèi)存分配的位置來知道的,全局變量分配在全局?jǐn)?shù)據(jù)段并且在程序開始運(yùn)行的時候被加載。局部變量則分配在棧里面 。

    22. Makefile文件的作用?

    ? ?makefile關(guān)系到了整個工程的編譯規(guī)則。一個工程中的源文件不計數(shù),其按類型、功能、模塊分別放在若干個目錄中,makefile定義了一系列的規(guī)則來指定,哪些文件需要先編譯,哪些文件需要后編譯,哪些文件需要重新編譯,甚至于進(jìn)行更復(fù)雜的功能操作,因?yàn)閙akefile就像一個Shell腳本一樣,其中也可以執(zhí)行操作系統(tǒng)的命令。

    23.結(jié)構(gòu)體和聯(lián)合體的區(qū)別?

    ? ? ? 結(jié)構(gòu)和聯(lián)合都是由多個不同的數(shù)據(jù)類型成員組成,?但在任何同一時刻,?聯(lián)合中只存放了一個被選中的成員(所有成員共用一塊地址空間),?而結(jié)構(gòu)的所有成員都存在(不同成員的存放地址不同)。?

    ? ? ?對于聯(lián)合的不同成員賦值,?將會對其它成員重寫,?原來成員的值就不存在了,?而對于結(jié)構(gòu)的不同成員賦值是互不影響的。

    24.列表初始化問題?

    ? ? ??使用初始化列表主要是基于性能問題,對于內(nèi)置類型,如int, float等,使用初始化列表和在構(gòu)造函數(shù)體內(nèi)初始化差別不是很大;但是對于類類型來說,最好使用初始化列表。這樣就可以直接調(diào)用拷貝構(gòu)造函數(shù)初始化,省去了一次調(diào)用默認(rèn)構(gòu)造函數(shù)的過程。

  • struct Test1

  • {

  • Test1() // 無參構(gòu)造函數(shù)

  • {

  • cout << "Construct Test1" << endl ;

  • }

  • Test1( const Test1& t1) // 拷貝構(gòu)造函數(shù)

  • {

  • cout << "Copy constructor for Test1" << endl ;

  • this->a = t1.a ;

  • }

  • Test1& operator = ( const Test1& t1) // 賦值運(yùn)算符

  • {

  • cout << "assignment for Test1" << endl ;

  • this->a = t1.a ;

  • return * this;

  • }

  • int a ;

  • };

  • struct Test2 //普通初始化

  • {

  • Test1 test1 ;

  • Test2(Test1 &t1)

  • {

  • test1 = t1 ;

  • }

  • };

    • 1
  • struct Test2 //2.列表初始化

  • {

  • Test1 test1 ;

  • Test2(Test1 &t1):test1(t1){}

  • }

    • 1
  • Test1 t1 ; //調(diào)用

  • Test2 t2(t1) ;

    • 1

    普通初始化:

    列表初始化:

    下列情況一定要使用初始化成員列表

    • 常量成員,因?yàn)槌A恐荒艹跏蓟荒苜x值,所以必須放在初始化列表里面
    • 引用類型,引用必須在定義的時候初始化,并且不能重新賦值,所以也要寫在初始化列表里面
    • 需要初始化的數(shù)據(jù)成員是對象的情況

    ? 參考地址:https://www.cnblogs.com/weizhixiang/p/6374430.html?

    25. 重載與重寫的區(qū)別?

    ? ? 從定義上來說:重載:是指允許存在多個同名函數(shù),而這些函數(shù)的參數(shù)表不同(或許參數(shù)個數(shù)不同,或許參數(shù)類型不同,或許兩者都不同)。重寫:是指子類重新定義父類虛函數(shù)的方法。

    ? ?從實(shí)現(xiàn)原理上來說:重載:編譯器根據(jù)函數(shù)不同的參數(shù)表,對同名函數(shù)的名稱做修飾,然后這些同名函數(shù)就成了不同的函數(shù)。重寫:當(dāng)子類重新定義了父類的虛函數(shù)后,父類指針根據(jù)賦給它的不同的子類指針,動態(tài)的調(diào)用屬于子類的該函數(shù),這樣的函數(shù)調(diào)用在編譯期間是無法確定的(調(diào)用的子類的虛函數(shù)的地址無法給出)。

    ? ?補(bǔ)充:“隱藏”是指派生類的函數(shù)屏蔽了與其同名的基類函數(shù)。規(guī)則如下:?
    (1)如果派生類的函數(shù)與基類的函數(shù)同名,但是參數(shù)不同。此時,不論有無virtual關(guān)鍵字,基類的函數(shù)將被隱藏(注意別與重載混淆)。?
    (2)如果派生類的函數(shù)與基類的函數(shù)同名,并且參數(shù)也相同,但是基類函數(shù)沒有virtual 關(guān)鍵字。此時,基類的函數(shù)被隱藏(注意別與覆蓋混淆)。

    26.類型安全以及C++中的類型轉(zhuǎn)換?

    ? ? ?類型安全很大程度上可以等價于內(nèi)存安全,類型安全的代碼不會試圖訪問自己沒被授權(quán)的內(nèi)存區(qū)域。C只在局部上下文中表現(xiàn)出類型安全,比如試圖從一種結(jié)構(gòu)體的指針轉(zhuǎn)換成另一種結(jié)構(gòu)體的指針時,編譯器將會報告錯誤,除非使用顯式類型轉(zhuǎn)換。然而,C中相當(dāng)多的操作是不安全的。

    ?詳情可以移步:https://blog.csdn.net/chengonghao/article/details/50974022

    ? ?四種類型轉(zhuǎn)換:

    • static_cast <T*> (content)? 靜態(tài)轉(zhuǎn)換.在編譯期間處理,可以實(shí)現(xiàn)C++中內(nèi)置基本數(shù)據(jù)類型之間的相互轉(zhuǎn)換。如果涉及到類的話,static_cast只能在有相互聯(lián)系的類型中進(jìn)行相互轉(zhuǎn)換,不一定包含虛函數(shù)。
    • dynamic_cast<T*>(content) 動態(tài)類型轉(zhuǎn)換;也是向下安全轉(zhuǎn)型;是在運(yùn)行的時候執(zhí)行;基類中一定要有虛函數(shù),否則編譯不通過。在類層次間進(jìn)行上行轉(zhuǎn)換時(如派生類指針轉(zhuǎn)為基類指針),dynamic_cast和static_cast的效果是一樣的。在進(jìn)行下行轉(zhuǎn)換時(如基類指針轉(zhuǎn)為派生類指針),dynamic_cast具有類型檢查的功能,比static_cast更安全。
    • const_cast<T*>(content) 去常轉(zhuǎn)換;編譯時執(zhí)行;
    • reinterpret_cast<T*>(content) 重解釋類型轉(zhuǎn)換;

    詳情可以移步:https://blog.csdn.net/u010025211/article/details/48626687

    ? ? ? ? ? ? ? ? ? ? ? ? https://blog.csdn.net/xtzmm1215/article/details/46475565

    ? ? ? ? ? ? ? ? ? ? ? ??https://blog.csdn.net/xingkongfenqi/article/details/49148885

    27.內(nèi)存對齊的原則以及作用?

    • 結(jié)構(gòu)體內(nèi)的成員按自身長度自對齊(32位機(jī)器上,如char=1,short=2,int=4,double=8),所謂自對齊是指該成員的起始地址必須是它自身長度的整數(shù)倍。如int只能以0,4,8這類地址開始。
    • 結(jié)構(gòu)體的總大小為結(jié)構(gòu)體的有效對齊值的整數(shù)倍(默認(rèn)以結(jié)構(gòu)體中最長的成員長度為有效值的整數(shù)倍,當(dāng)用#pragrma pack(n)指定時,以n和結(jié)構(gòu)體中最長的成員的長度中較小者為其值)。即sizeof的值,必須是其內(nèi)部最大成員的整數(shù)倍,不足的要補(bǔ)齊。

    ?例如:

  • class A

  • {

  • char c;

  • int a;

  • char d;

  • };

  • cout << sizeof(A) << endl;

  • class B

  • {

  • char c;

  • char d;

  • int a;

  • };

  • cout << sizeof(B) << endl;

    • 1

    sizeof(A)=12,sizeof(B)=8;

    因?yàn)樽筮吺?+(3)+4+1+(3)=12,而右邊是1+1+(2)+4=8。括號中為補(bǔ)齊的字節(jié)。

    內(nèi)存對齊的作用:

    1、平臺原因(移植原因):不是所有的硬件平臺都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。

    2、性能原因:經(jīng)過內(nèi)存對齊后,CPU的內(nèi)存訪問速度大大提升。

    詳情可以移步:https://blog.csdn.net/chy19911123/article/details/48894579

    28.關(guān)鍵字registr,typdef的作用?

    register關(guān)鍵字的作用:

    請求CPU盡可能讓變量的值保存在CPU內(nèi)部的寄存器中,減去CPU從內(nèi)存中抓取數(shù)據(jù)的時間,提高程序運(yùn)行效率。

    使用register關(guān)鍵字應(yīng)注意什么?

    1.只有局部變量才可以被聲明用register修飾

    (register不能修飾全局變量和函數(shù)的原因:全局變量可能被多個進(jìn)程訪問,而用register修飾的變量,只能被當(dāng)前進(jìn)程訪問)

    2.不能用取地址獲取用register修飾的變量的地址(原因:變量保存在寄存器中,而取地址獲取的地址的是內(nèi)存的地址)

    3. 用register修飾的變量一定要是CPU所接受的數(shù)據(jù)類型

    typedef關(guān)鍵字的作用:

    給數(shù)據(jù)類型定義一個新名字,

    1.? 提高了移植性

    2.? 簡化復(fù)雜的類型聲明,提高編碼效率

    3.? 解釋數(shù)據(jù)類型的作用

    29.什么情況下需要將析構(gòu)函數(shù)定義為虛函數(shù)?

    ? ? 當(dāng)基類指針指向派生類的對象(多態(tài)性)時。如果定義為虛函數(shù),則就會先調(diào)用該指針指向的派生類析構(gòu)函數(shù),然后派生類的析構(gòu)函數(shù)再又自動調(diào)用基類的析構(gòu)函數(shù),這樣整個派生類的對象完全被釋放。如果析構(gòu)函數(shù)不被聲明成虛函數(shù),則編譯器實(shí)施靜態(tài)綁定,在刪除基類指針時,只會調(diào)用基類的析構(gòu)函數(shù)而不調(diào)用派生類析構(gòu)函數(shù),這樣就會造成派生類對象析構(gòu)不完全。所以,將析構(gòu)函數(shù)聲明為虛函數(shù)是十分必要的。

    ? 詳情可以移步:https://blog.csdn.net/jiadebin890724/article/details/7951461

    30.有關(guān)純虛函數(shù)的理解?

    ? ? ?純虛函數(shù)是為你的程序制定一種標(biāo)準(zhǔn),純虛函數(shù)只是一個接口,是個函數(shù)的聲明而已,它要留到子類里去實(shí)現(xiàn)。

  • class A{

  • protected:

  • void foo(); //普通類函數(shù)

  • virtual void foo1(); //虛函數(shù)

  • virtual void foo2() = 0; //純虛函數(shù)

  • }

    • 1

    ? ? 帶純虛函數(shù)的類抽象類,這種類不能直接生成對象,而只有被繼承,并重寫其虛函數(shù)后,才能使用。
    ? ? 虛函數(shù)是為了繼承接口和默認(rèn)行為

    ? ? 純虛函數(shù)只是繼承接口,行為必須重新定義

    ? ?(在很多情況下,基類本身生成對象是不合情理的。例如,動物作為一個基類可以派生出老虎、孔雀等子類,但動物本身生成對象明顯不合常? 理。所以引入了純虛函數(shù)的概念)

    ? ? 詳情可以參考:https://blog.csdn.net/ybhjx/article/details/51788396

    30.基類指針指向派生類,派生類指針指向基類?

    ? ? 基類指針可以指向派生類對象,從而實(shí)現(xiàn)多態(tài),例如:

  • #include <iostream>

  • using namespace std;

  • class Shape {

  • public:

  • virtual double area() const = 0; //純虛函數(shù)

  • };

  • class Square : public Shape {

  • double size;

  • public:

  • Square( double s) {

  • size = s;

  • }

  • virtual double area() const {

  • return size * size;

  • }

  • };

  • class Circle : public Shape {

  • double radius;

  • public:

  • Circle( double r) {

  • radius = r;

  • }

  • virtual double area() const {

  • return 3.14159 * radius * radius;

  • }

  • };

  • int main()

  • {

  • Shape* array[ 2]; //定義基類指針數(shù)組

  • Square Sq(2.0);

  • Circle Cir(1.0);

  • array[ 0] = &Sq;

  • array[ 1] =&Cir;

  • for ( int i = 0; i < 2; i++) /

  • {

  • cout << array[i]->area() << endl;

  • }

  • return 0;

  • }

    • 1

    ? ? ?上面的不同對象Sq,Cir(來自繼承同一基類的不同派生類)接受同一消息(求面積,來自基類的成員函數(shù)area()),但是卻根據(jù)自身情況調(diào)用不同的面積公式(執(zhí)行了不同的行為,它是通過虛函數(shù)實(shí)現(xiàn)的)。我們可以理解為,繼承同一基類的不同派生對象,對來自基類的同一消息執(zhí)行了不同的行為,這就是多態(tài),它是通過繼承和虛函數(shù)實(shí)現(xiàn)的。而接受同一消息的實(shí)現(xiàn)就是基于基類指針。?

    ? ? ?但是要注意的是,這個指針只能用來調(diào)用基類的成員函數(shù)。

    ? ? ?如果試圖通過基類指針調(diào)用派生類才有的成員函數(shù),則編譯器會報錯。

    ? ? ?為了避免這種錯誤,必須將基類指針強(qiáng)制轉(zhuǎn)化為派生類指針。然后派生類指針可以用來調(diào)用派生類的功能。這稱為向下強(qiáng)制類型轉(zhuǎn)換,這是一種潛在的危險操作。

    ? ? ?派生類指針不可以指向基類對象,例如:

    有個people類是基類,成員有姓名和身份證號,有個派生類學(xué)生student,添加了成員學(xué)號,現(xiàn)在如果你說的這個情況成立student的指針----pt讓他指向people成員t,則t只有兩個成員變量,而*pt有3個,現(xiàn)在pt->學(xué)號這個變量在pt下是可以使用的,但它指向的實(shí)體卻沒有這個變量,所以出錯,于是C++直接就避免了這樣的隱式轉(zhuǎn)換。 所以根據(jù)上述信息我們可以知道: 進(jìn)行上行轉(zhuǎn)換(把派生類的指針或引用轉(zhuǎn)換成基類表示)是安全的;進(jìn)行下行轉(zhuǎn)換(把基類指針或引用轉(zhuǎn)換成派生類表示)是不安全的。

    ? ? 參考鏈接:https://blog.csdn.net/flyingbird_sxf/article/details/41358737

    ? ? ? ? ? ? ? ? ? ? ?https://www.cnblogs.com/rednodel/p/5800142.html

    ? ?31.?繼承機(jī)制中引用和指針之間如何轉(zhuǎn)換??
    ? ? 基類——>派生類:用dynamic_cast轉(zhuǎn)換(顯示轉(zhuǎn)換),首先檢查基類指針(引用)是否真正指向一個派生類對象,然后再做相應(yīng)處理,對指針進(jìn)行dynamic_cast,成功返回派生類對象,失敗返回空指針,對引用進(jìn)行dynamic_cast,成功返回派生類對象,失敗拋出一個異常。 不允許隱式轉(zhuǎn)換。
    ? ? 派生類——>基類:可以用dynamic_cast或者直接進(jìn)行類型轉(zhuǎn)換(直接賦值)。

    ? ??32.c語言和c++有什么區(qū)別??
    ? ? C語言是結(jié)構(gòu)化的編程語言,它是面向過程的,而C++是面向?qū)ο蟮摹?
    ? ??封裝:將數(shù)據(jù)和函數(shù)等集合在一個單元中(即類)。被封裝的類通常稱為抽象數(shù)據(jù)類型。封裝的意義在于保護(hù)或者防止代碼(數(shù)據(jù))被我們無意中破壞。?
    ? ??繼承:繼承主要實(shí)現(xiàn)重用代碼,節(jié)省開發(fā)時間。它可以使用現(xiàn)有類的所有功能,并在無需重新編寫原來的類的情況下對這些功能進(jìn)行擴(kuò)展。?
    ? ??多態(tài):同一操作作用于不同的對象,可以有不同的解釋,產(chǎn)生不同的執(zhí)行結(jié)果。在運(yùn)行時,可以通過指向派生類的基類指針,來調(diào)用實(shí)現(xiàn)派生類中的方法。有編譯時多態(tài)和運(yùn)行時多態(tài)。

    ???33.C++中的公有,私有,保護(hù)的問題??

    類對象

    公有繼承派生類

    私有繼承派生類

    保護(hù)繼承派生類

    公有繼承派生類對象

    私有繼承派生類對象

    保護(hù)繼承派生類對象

    公有成員

    X

    X

    私有成員

    X

    X

    X

    X

    X

    X

    X

    保護(hù)成員

    X

    X

    X

    X

    ? ?√:代表可以訪問,X代表不能訪問。? ? ?

    ?參考鏈接:https://zhidao.baidu.com/question/551075894.html

    34.如何實(shí)現(xiàn)類對象只能靜態(tài)分配或動態(tài)分配?

    ?C++中建立類的對象有兩種方式:

    (1)靜態(tài)建立,例如 A a;

    ? ? ?靜態(tài)建立一個類對象,就是由編譯器為對象在棧空間中分配內(nèi)存。使用這種方法,是直接調(diào)用類的構(gòu)造函數(shù)。

    (2)動態(tài)建立,例如 A* p = new A();

    ? ? ?動態(tài)建立一個類對象,就是使用new運(yùn)算符為對象在堆空間中分配內(nèi)存。這個過程分為兩步:第一步執(zhí)行operator new( )函數(shù),在堆空間中搜索一塊內(nèi)存并進(jìn)行分配;第二步調(diào)用類的構(gòu)造函數(shù)構(gòu)造對象。這種方法是間接調(diào)用類的構(gòu)造函數(shù)。

    只能動態(tài)分配:??

    ? ? ? 其實(shí),編譯器在為類對象分配棧空間時,會先檢查類的析構(gòu)函數(shù)的訪問性(其實(shí)不光是析構(gòu)函數(shù),只要是非靜態(tài)的函數(shù),編譯器都會進(jìn)行檢查)。如果類的析構(gòu)函數(shù)在類外部無法訪問,則編譯器拒絕在棧空間上為類對象分配內(nèi)存。 因此,可以將析構(gòu)函數(shù)設(shè)為private,這樣就無法在棧上建立類對象了。但是為了子類可以繼承,最好設(shè)置成protected。

  • class?A??
  • {??
  • protected:??
  • ?????A(){}??
  • ?????~A(){}??
  • public:??
  • ?????static?A*?create(){return?new?A();}??
  • ?????void?destory(){delete?this;}??
  • };??
  • 只能靜態(tài)分配:

    ? ??只有使用new運(yùn)算符,對象才會被建立在堆上。因此只要限制new運(yùn)算符就可以實(shí)現(xiàn)類對象只能建立在棧上。可以將new運(yùn)算符設(shè)為私有。

  • class?A??
  • {??
  • private:??
  • ?????void*?operator?new(size_t?t){}? ? ? ? ? ??//注意函數(shù)的第一個參數(shù)和返回值都是固定的??
  • ?????void? operator delete(void*?ptr)()????????//重載了new就需要重載delete??
  • public:??
  • ?????A(){}??
  • ?????~A(){}??
  • };??
  • 35.explicit關(guān)鍵字的作用?

    C++中, 一個參數(shù)的?構(gòu)造函數(shù)(或者除了第一個參數(shù)外其余參數(shù)都有默認(rèn)值的多參構(gòu)造函數(shù)), 承擔(dān)了兩個角色。 1 是個?構(gòu)造器?,2 是個默認(rèn)且隱含的類型轉(zhuǎn)換操作符。

    所以, 有時候在我們寫下如 AAA = XXX, 這樣的代碼, 且恰好XXX的類型正好是AAA單參數(shù)構(gòu)造器的參數(shù)類型, 這時候?編譯器就自動調(diào)用這個構(gòu)造器, 創(chuàng)建一個AAA的對象。

    這樣看起來好象很酷, 很方便。 但在某些情況下(見下面權(quán)威的例子), 卻違背了我們(程序員)的本意。 這時候就要在這個構(gòu)造器前面加上explicit修飾, 指定這個構(gòu)造器只能被明確的調(diào)用/使用, 不能作為類型轉(zhuǎn)換操作符被隱含的使用。

  • class Test1

  • {

  • public:

  • Test1( int n)

  • {

  • num=n;

  • } //普通構(gòu)造函數(shù)

  • private:

  • int num;

  • };

  • class Test2

  • {

  • public:

  • explicit Test2(int n)

  • {

  • num=n;

  • } //explicit(顯式)構(gòu)造函數(shù)

  • private:

  • int num;

  • };

  • int main()

  • {

  • Test1 t1= 12; //隱式調(diào)用其構(gòu)造函數(shù),成功

  • Test2 t2= 12; //編譯錯誤,不能隱式調(diào)用其構(gòu)造函數(shù)

  • Test2 t2(12); //顯式調(diào)用成功

  • return 0;

  • }

    • 1

    Test1的?構(gòu)造函數(shù)帶一個int型的參數(shù),代碼23行會隱式轉(zhuǎn)換成調(diào)用Test1的這個構(gòu)造函數(shù)。而Test2的構(gòu)造函數(shù)被聲明為explicit(顯式),這表示不能通過隱式轉(zhuǎn)換來調(diào)用這個構(gòu)造函數(shù),因此代碼24行會出現(xiàn)編譯錯誤。

    普通構(gòu)造函數(shù)能夠被?隱式調(diào)用。而explicit構(gòu)造函數(shù)只能被顯式調(diào)用。

    36.內(nèi)存溢出,內(nèi)存泄漏的原因?

    ? ? 內(nèi)存溢出是指程序在申請內(nèi)存時,沒有足夠的內(nèi)存空間供其使用。原因可能如下:

    • ?內(nèi)存中加載的數(shù)據(jù)量過于龐大,如一次從數(shù)據(jù)庫取出過多數(shù)據(jù)
    • ?代碼中存在死循環(huán)或循環(huán)產(chǎn)生過多重復(fù)的對象實(shí)體
    • ?遞歸調(diào)用太深,導(dǎo)致堆棧溢出等
    • ?內(nèi)存泄漏最終導(dǎo)致內(nèi)存溢出

    ? ??內(nèi)存泄漏是指向系統(tǒng)申請分配內(nèi)存進(jìn)行使用(new),但是用完后不歸還(delete),導(dǎo)致占用有效內(nèi)存。常見的幾種情況:

    ? ?(1)?在類的構(gòu)造函數(shù)和析構(gòu)函數(shù)中沒有匹配的調(diào)用new和delete函數(shù)

    ? ? ????兩種情況下會出現(xiàn)這種內(nèi)存泄露:一是在堆里創(chuàng)建了對象占用了內(nèi)存,但是沒有顯示地釋放對象占用的內(nèi)存;二是在類的構(gòu)造函數(shù)中動態(tài)的分配了內(nèi)存,但是在析構(gòu) 函數(shù)中沒有釋放內(nèi)存或者沒有正確的釋放內(nèi)存

    ? ?(2)?在釋放對象數(shù)組時在delete中沒有使用方括號

    ? ? ??方括號是告訴編譯器這個指針指向的是一個對象數(shù)組,同時也告訴編譯器正確的對象地址值病調(diào)用對象的析構(gòu)函數(shù),如果沒有方括號,那么這個指針就被默認(rèn)為只指向一個對象,對象數(shù)組中的其他對象的析構(gòu)函數(shù)就不會被調(diào)用,結(jié)果造成了內(nèi)存泄露。

    ? ? (3)沒有將基類的析構(gòu)函數(shù)定義為虛函數(shù)

    ? ? ???當(dāng)基類指針指向子類對象時,如果基類的析構(gòu)函數(shù)不是virtual,那么子類的析構(gòu)函數(shù)將不會被調(diào)用,子類的資源沒有正確是釋放,因此造成內(nèi)存泄露

    ? ? ?參考鏈接: https://blog.csdn.net/hyqwmxsh/article/details/52813307? ??



    ? ? ?緩沖區(qū)溢出(棧溢出)

    ? ? ?程序?yàn)榱伺R時存取數(shù)據(jù)的需要,一般會分配一些內(nèi)存空間稱為緩沖區(qū)。如果向緩沖區(qū)中寫入緩沖區(qū)無法容納的數(shù)據(jù),機(jī)會造成緩沖區(qū)以外的存儲單元被改寫,稱為緩沖區(qū)溢出。而棧溢出是緩沖區(qū)溢出的一種,原理也是相同的。分為上溢出和下溢出。其中,上溢出是指棧滿而又向其增加新的數(shù)據(jù),導(dǎo)致數(shù)據(jù)溢出;下溢出是指空棧而又進(jìn)行刪除操作等,導(dǎo)致空間溢出。

    37.auto_ptr類與shared_ptr類?

    ??? ??從c++11開始, auto_ptr已經(jīng)被標(biāo)記為棄用, 常見的替代品為shared_ptr。shared_ptr的不同之處在于引用計數(shù), 在復(fù)制(或賦值)時不會像auto_ptr那樣直接轉(zhuǎn)移所有權(quán)。?兩者都是模板類,卻可以像指針一樣去使用。只是在指針上面的一層封裝。

    ?? ???auto_ptr實(shí)際也是一種類, 擁有自己的析構(gòu)函數(shù), 生命周期結(jié)束時能自動釋放資源,正因?yàn)槟茏詣俞尫刨Y源, 特別適合在單個函數(shù)內(nèi)代替new/delete的調(diào)用, 不用自己調(diào)用delete,也不用擔(dān)心意外退出造成內(nèi)存的泄漏。

    ? ?atuo_ptr的缺陷:

    • ?auto_ptr不能共享所有權(quán),即不要讓兩個auto_ptr指向同一個對象(因?yàn)樗捎玫氖寝D(zhuǎn)移語義的拷貝,原指針會變?yōu)镹ULL)。
    • ?auto_ptr不能管理對象數(shù)組(因?yàn)樗鼉?nèi)部的析構(gòu)函數(shù)調(diào)用的是delete而不是delete[])。
    • ?auto_ptr不能作為容器對象,STL容器中的元素經(jīng)常要支持拷貝,賦值等操作,在這過程中auto_ptr會傳遞所有權(quán)。

    ? ?詳情原因可以參考:https://blog.csdn.net/uestclr/article/details/51316001

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??https://blog.csdn.net/kezunhai/article/details/38514823

    ? ? ?

    ? ? ?shared_ptr 使用引用計數(shù)的方式來實(shí)現(xiàn)對指針資源的管理。同一個指針資源,可以被多個 shared_ptr 對象所擁有,直到最后一個 shared_ptr 對象析構(gòu)時才釋放所管理的對象資源。

    ? ? ? 可以說,shared_ptr 是最智能的智能指針,因?yàn)槠涮攸c(diǎn)最接近原始的指針。不僅能夠自由的賦值和拷貝,而且可以安全的用在標(biāo)準(zhǔn)容器中。

    38.???有4種情況,編譯器必須為未聲明的constructor的classes合成一個default constructor:

    l? “帶有默認(rèn)構(gòu)造函數(shù)”的成員對象

    l? “帶有默認(rèn)構(gòu)造函數(shù)”的基類

    l? “帶有虛函數(shù)”的類

    l? “帶有虛擬基類”的類

    被合成的構(gòu)造函數(shù)只能滿足編譯器(而非程序員)的需要。在合成默認(rèn)的構(gòu)造函數(shù)中,只有基類的子對象和成員對象會被初始化,其他非靜態(tài)的數(shù)據(jù)成員(如整數(shù),指針等)都不會被初始化。

    所以并不是任何的類如果沒有定義默認(rèn)的構(gòu)造函數(shù),都會被合成一個出來。

    39.???虛基類

    在C++中,如果在多條繼承路徑上有一個公共的基類,那么在這些路徑中的某幾條路徑的匯合處,這個公共的基類就會產(chǎn)生多個實(shí)例(從而造成二義性).如果想使這個公共的基類只產(chǎn)生一個實(shí)例,則可將這個基類說明為虛基類. 這要求在從base類派生新類時,使用關(guān)鍵字virtual將base類說明為虛基類.

    用例子說明吧。

    class base{protected:int b};
    clase base1:public base{..};
    clase base2:public base{..};
    clase derived:public base1,public base2 {..};
    derived d;
    d.b //錯誤.
    d.base::b //錯誤. 因?yàn)椴恢怯胐.base1::b還是d.base2::b
    =================================================
    class base{protected:int b..};
    clase base1:virtual public base{..}; //說明base為base1虛基類
    clase base2:virtual public base{..}; //說明base為base2虛基類
    clase derived:public base1,public base2 {..};
    derived d;
    d.b //對.
    d.base::b //對. 因?yàn)閐.base::b和d.base1::b還是d.base2::b都是引用同一虛基類成員b,具有相同的值.

    40.??模板的特例化

    引入原因:編寫單一的模板,它能適應(yīng)大眾化,使每種類型都具有相同的功能,但對于某種特定類型,如果要實(shí)現(xiàn)其特有的功能,單一模板就無法做到,這時就需要模板特例化。?
    定義:是對單一模板提供的一個特殊實(shí)例,它將一個或多個模板參數(shù)綁定到特定的類型或值上。

    函數(shù)模板特例化:必須為原函數(shù)模板的每個模板參數(shù)都提供實(shí)參,且使用關(guān)鍵字template后跟一個空尖括號對<>,表明將原模板的所有模板參數(shù)提供實(shí)參。

    1.???template?<typename?T>??

    2.???void?fun(T?a)??

    3.?? {??

    4.?? ????cout?<<?"The?main?template?fun():?"?<<?a?<<?endl;??

    5.?? }??

    6.?? ??

    7.???template?<>???//?對int型特例化??

    8.???void?fun(int?a)??

    9.?? {??

    10. ????cout?<<?"Specialized?template?for?int?type:?"?<<?a?<<?endl;??

    11. }??

    12. ??

    13.?int?main()??

    14. {??

    15. ????fun<char>('a');??

    16. ????fun<int>(10);??

    17. ????fun<float>(9.15);??

    18. ????return?0;??

    19. }??

    對于除int型外的其他數(shù)據(jù)類型,都會調(diào)用通用版本的函數(shù)模板fun(T a);對于int型,則會調(diào)用特例化版本的fun(int a)。注意,一個特例化版本的本質(zhì)是一個實(shí)例,而非函數(shù)的重載。因此,特例化不影響函數(shù)匹配。

    ?

    類模板的特例化

    1.???template?<typename?T>??

    2.???class?Test{??

    3.???public:??

    4.?? ????void?print(){??

    5.?? ????????cout?<<?"General?template?object"?<<?endl;??

    6.?? ????}??

    7.?? };??

    8.?? ??

    9.???template<>???//?對int型特例化??

    10.?class?Test<int>{??

    11.?public:??

    12. ????void?print(){??

    13. ????????cout?<<?"Specialized?template?object"?<<?endl;??

    14. ????}??

    15. };??

    另外,與函數(shù)模板不同,類模板的特例化不必為所有模板參數(shù)提供實(shí)參。我們可以只指定一部分而非所有模板參數(shù),這種叫做類模板的偏特化?或部分特例化(partial specialization)。例如,C++標(biāo)準(zhǔn)庫中的類vector的定義:

    [cpp]?view plain?copy

    1.???template?<typename?T,?typename?Allocator>??

    2.???class?vector??

    3.?? {??

    4.?? ????/*......*/??

    5.?? };??

    6.?? ??

    7.?? //?部分特例化??

    8.???template?<typename?Allocator>??

    9.???class?vector<bool,?Allocator>??

    10. {??

    11. ????/*......*/??

    12. };??

    在vector這個例子中,一個參數(shù)被綁定到bool類型,而另一個參數(shù)仍未綁定需要由用戶指定。注意,一個類模板的部分特例化版本仍然是一個模板,因?yàn)槭褂盟鼤r用戶還必須為那些在特例化版本中未指定的模板參數(shù)提供實(shí)參。?

    第二部分:嵌入式

    總結(jié)

    以上是生活随笔為你收集整理的程序员面试宝典的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。