C++常用面試題大全

1. extern關鍵字的作用    

extern置於變量或函數前,用於標示變量或函數的定義在別的文件中,提示編譯器遇到此變量和函數時在其他模塊中尋找其定義。它只要有兩個作用:當它與“C”一起連用的時候,如:extern "C" void fun(int a,int b);則告訴編譯器在編譯fun這個函數時候按着C的規矩去翻譯,而不是C++的(這與C++的重載有關,C++語言支持函數重載,C語言不支持函數重載,函數被C++編譯器編譯後在庫中的名字與C語言的不同)當extern不與“C”在一起修飾變量或函數時,如:extern int g_Int;它的作用就是聲明函數或全局變量的作用範圍的關鍵字,其聲明的函數和變量可以在本模塊或其他模塊中使用。記住它是一個聲明不是定義!也就是說B模塊(編譯單元)要是引用模塊(編譯單元)A中定義的全局變量或函數時,它只要包含A模塊的頭文件即可,在編譯階段,模塊B雖然找不到該函數或變量,但它不會報錯,它會在連接時從模塊A生成的目標代碼中找到此函數。

2.static關鍵字的作用  

修飾局部變量    static修飾局部變量時,使得被修飾的變量成爲靜態變量,存儲在靜態區。存儲在靜態區的數據生命週期與程序相同,在main函數之前初始化,在程序退出時銷燬。(無論是局部靜態還是全局靜態)  修飾全局變量    全局變量本來就存儲在靜態區,因此static並不能改變其存儲位置。但是,static限制了其鏈接屬性。被static修飾的全局變量只能被該包含該定義的文件訪問(即改變了作用域)。  修飾函數     static修飾函數使得函數只能在包含該函數定義的文件中被調用。對於靜態函數,聲明和定義需要放在同一個文件夾中。  修飾成員變量      用static修飾類的數據成員使其成爲類的全局變量,會被類的所有對象共享,包括派生類的對象,所有的對象都只維持同一個實例。 因此,static成員必須在類外進行初始化(初始化格式:int base::var=10;),而不能在構造函數內進行初始化,不過也可以用const修飾static數據成員在類內初始化。  修飾成員函數     用static修飾成員函數,使這個類只存在這一份函數,所有對象共享該函數,不含this指針,因而只能訪問類的static成員變量。靜態成員是可以獨立訪問的,也就是說,無須創建任何對象實例就可以訪問。例如可以封裝某些算法,比如數學函數,如ln,sin,tan等等,這些函數本就沒必要屬於任何一個對象,所以從類上調用感覺更好,比如定義一個數學函數類Math,調用Math::sin(3.14);還可以實現某些特殊的設計模式:如Singleton;最重要的特性:隱藏    當同時編譯多個文件時,所有未加static前綴的全局變量和函數都具有全局可見性,其它的源文件也能訪問。利用這一特性可以在不同的文件中定義同名函數和同名變量,而不必擔心命名衝突。static可以用作函數和變量的前綴,對於函數來講,static的作用僅限於隱藏。不可以同時用const和static修飾成員函數。   C++編譯器在實現const的成員函數的時候爲了確保該函數不能修改類的實例的狀態,會在函數中添加一個隱式的參數const this*。但當一個成員爲static的時候,該函數是沒有this指針的。也就是說此時const的用法和static是衝突的。我們也可以這樣理解:兩者的語意是矛盾的。static的作用是表示該函數只作用在類型的靜態變量上,與類的實例沒有關係;而const的作用是確保函數不能修改類的實例的狀態,與類型的靜態變量沒有關係。因此不能同時用它們。

3.volatile的作用    

用來修飾變量的,表明某個變量的值可能會隨時被外部改變,因此這些變量的存取不能被緩存到寄存器,每次使用需要重新讀取。        假如有一個對象A裏面有一個boolean變量a,值爲true,現在有兩個線程T1,T2訪問變量a,T1把a改成了false後T2讀取a,T2這時讀到的值可能不是false,即T1修改a的這一操作,對T2是不可見的。發生的原因可能是,針對T2線程,爲了提升性能,虛擬機把a變量置入了寄存器(即C語言中的寄存器變量),這樣就會導致,無論T2讀取多少次a,a的值始終爲true,因爲T2讀取了寄存器而非內存中的值。聲明瞭volatile或synchronized 後,就可以保證可見性,確保T2始終從內存中讀取變量,T1始終在內存中修改變量。總結:防止髒讀,增加內存屏障。

4.const的作用

定義變量爲只讀變量,不可修改修飾函數的參數和返回值(後者應用比較少,一般爲值傳遞)const成員函數(只需要在成員函數參數列表後加上關鍵字const,如char get() const;)可以訪問const成員變量和非const成員變量,但不能修改任何變量。在聲明一個成員函數時,若該成員函數並不對數據成員進行修改操作,應儘可能將該成員函數聲明爲const成員函數。const對象只能訪問const成員函數,而非const對象可以訪問任意的成員函數,包括const成員函數.即對於class A,有const A a;那麼a只能訪問A的const成員函數。而對於:A b;b可以訪問任何成員函數。使用const關鍵字修飾的變量,一定要對變量進行初始化

5.指針與引用的區別

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

6.new與malloc的區別

malloc與free是C++/C語言的標準庫函數,new/delete是C++的運算符。它們都可用於申請動態內存和釋放內存。對於非內部數據類型的對象而言,光用malloc/free無法滿足動態對象的要求。對象在創建的同時要自動執行構造函數,對象在消亡之前要自動執行析構函數。new可以認爲是malloc加構造函數的執行。new出來的指針是直接帶類型信息的。而malloc返回的都是void指針。

7.C++的多態性        

多態性可以簡單地概括爲“一個接口,多種方法”,程序在運行時才決定調用的函數 。C++多態性主要是通過虛函數實現的,虛函數允許子類重寫override(注意和overload的區別,overload是重載,是允許同名函數的表現,這些函數參數列表/類型不同)    多態與非多態的實質區別就是函數地址是早綁定還是晚綁定。如果函數的調用,在編譯器編譯期間就可以確定函數的調用地址,並生產代碼,是靜態的,就是說地址是早綁定的。而如果函數調用的地址不能在編譯器期間確定,需要在運行時才確定,這就屬於晚綁定。      在絕大多數情況下,程序的功能是在編譯的時候就確定下來的,我們稱之爲靜態特性。反之,如果程序的功能是在運行時刻才能確定下來的,則稱之爲動態特性。C++中,虛函數,抽象基類,動態綁定和多態構成了出色的動態特性。    最常見的用法就是聲明基類的指針,利用該指針指向任意一個子類對象,調用相應的虛函數,可以根據指向的子類的不同而實現不同的方法。          a、編譯時多態性:通過重載函數實現        b、運行時多態性:通過虛函數實現    有關重載,重寫

8.動態綁定與靜態綁定

靜態綁定發生在編譯期,動態綁定發生在運行期;對象的動態類型可以更改,但是靜態類型無法更改;要想實現動態,必須使用動態綁定;在繼承體系中只有虛函數使用的是動態綁定,其他的全部是靜態綁定;靜態多態是指通過模板技術或者函數重載技術實現的多態,其在編譯器確定行爲。動態多態是指通過虛函數技術實現在運行期動態綁定的技術   動態綁定:有一個基類,兩個派生類,基類有一個virtual函數,兩個派生類都覆蓋了這個虛函數。現在有一個基類的指針或者引用,當該基類指針或者引用指向不同的派生類對象時,調用該虛函數,那麼最終調用的是該被指向對象對應的派生類自己實現的虛函數。

10.虛函數表是針對類的還是針對對象的?

同一個類的兩個對象的虛函數表是怎麼維護的?    編譯器爲每一個類維護一個虛函數表(本質是一個函數指針數組,數組裏面存放了一系列函數地址 ),每個對象的首地址保存着該虛函數表的指針,同一個類的不同對象實際上指向同一張虛函數表。調用形式:*(this指針+調整量)[虛函數在vftable內的偏移]()      在類內部添加一個虛擬函數表指針,該指針指向一個虛擬函數表,該虛擬函數表包含了所有的虛擬函數的入口地址,每個類的虛擬函數表都不一樣,在運行階段可以循此脈絡找到自己的函數入口。純虛函數相當於佔位符, 先在虛函數表中佔一個位置由派生類實現後再把真正的函數指針填進去。除此之外和普通的虛函數沒什麼區別。     在單繼承形式下,子類的完全獲得父類的虛函數表和數據。子類如果重寫了父類的虛函數(如fun),就會把虛函數表原本fun對應的記錄(內容MyClass::fun)覆蓋爲新的函數地址(內容MyClassA::fun),否則繼續保持原本的函數地址記錄。     使用這種方式,就可以實現多態的特性。假設我們使用如下語句:MyClass*pc=new MyClassA;pc->fun();  因爲虛函數表內的函數地址已經被子類重寫的fun函數地址覆蓋了,因此該處調用的函數正是MyClassA::fun,而不是基類的MyClass::fun。如果使用MyClassA對象直接訪問fun,則不會出發多態機制,因爲這個函數調用在編譯時期是可以確定的,編譯器只需要直接調用MyClassA::fun即可。       注:對象不包含虛函數表,只有虛指針,類才包含虛函數表,派生類會生成一個兼容基類的虛函數表      

11.智能指針怎麼實現?

什麼時候改變引用計數?構造函數中計數初始化爲1;拷貝構造函數中計數值加1;賦值運算符中,左邊的對象引用計數減一,右邊的對象引用計數加一;析構函數中引用計數減一;在賦值運算符和析構函數中,如果減一後爲0,則調用delete釋放對象。12.內聯函數,宏定義和普通函數的區別內聯函數要做參數類型檢查,這是內聯函數跟宏相比的優勢宏定義是在預編譯的時候把所有的宏名用宏體來替換,簡單的說就是字符串替換, 內聯函數則是在編譯的時候進行代碼插入,編譯器會在每處調用內聯函數的地方直接把內聯函數的內容展開,這樣可以省去函數的調用的壓棧出棧的開銷,提高效率。內聯函數是指嵌入代碼,就是在調用函數的地方不是跳轉,而是把代碼直接寫到那裏去。對於短小簡單的代碼來說,內聯函數可以帶來一定的效率提升,而且和C時代的宏函數相比,內聯函數 更安全可靠。可是這個是以增加空間消耗爲代價的const與#define的區別:宏在預處理階段替換,const在編譯階段替換;宏沒有類型,不做安全檢查,const有類型,在編譯階段進行安全檢查13. C++內存管理棧: 存放函數參數以及局部變量,在出作用域時,將自動被釋放.棧內存分配運算內置於處理器的指令集中,效率很高,但分配的內存容量有限.堆:new分配的內存塊(包括數組,類實例等),需delete手動釋放.如果未釋放,在整個程序結束後,OS會幫你回收掉.自由存儲區:malloc分配的內存塊,需free手動釋放.它和堆有些相似.全局/靜態區:保存自動全局變量和static變量(包括static全局和局部變量)。靜態區的內容在整個程序的生命週期內都存在,有編譯器在編譯的時候分配(數據段(存儲全局數據和靜態數據)和代碼段(可執行的代碼/只讀常量))。常量存儲區:常量(const)存於此處,此存儲區不可修改.

18.C++中類與結構體的區別?最本質的一個區別就是默認的訪問控制: struct作爲數據結構的實現體,它默認的數據訪問控制是public的,而class作爲對象的實現體,它默認的成員變量訪問控制是private的。“class”這個關鍵字還用於定義模板參數,就像“typename”。但關鍵字“struct”不用於定義模板參數。

19.析構函數的作用?

析構函數是用來釋放所定義的對象中使用的指針,默認的析構函數不用顯示調用,自建的析構函數要在程序末尾調用。如果你的類裏面只用到的基本類型,如int char double等,系統的默認析構函數其實什麼都沒有做但如果你使用了其他的類如vector,string等,系統的默認析構函數就會調用這些類對象的析構函數如果是自己寫析構函數的話,如果你的類裏面分配了系統資源,如new了內存空間,打開了文件等,那麼在你的析構函數中就必須釋放相應的內存空間和關閉相關的文件;這樣系統就會自動調用你的析構函數釋放資源,避免內存泄漏

20.虛函數的作用?      虛函數可以讓成員函數操作一般化,用基類的指針指向不同的派生類的對象時,基類指針調用其虛成員函數,則會調用其真正指向對象的成員函數,而不是基類中定義的成員函數(只要派生類改寫了該成員函數)。若不是虛函數,則不管基類指針指向的哪個派生類對象,調用時都會調用基類中定義的那個函數。虛函數是C++多態的一種表現,可以進行靈活的動態綁定。

25. 重載與重寫的區別?    從定義上來說:重載:是指允許存在多個同名函數,而這些函數的參數表不同(或許參數個數不同,或許參數類型不同,或許兩者都不同)。重寫:是指子類重新定義父類虛函數的方法。   從實現原理上來說:重載:編譯器根據函數不同的參數表,對同名函數的名稱做修飾,然後這些同名函數就成了不同的函數。重寫:當子類重新定義了父類的虛函數後,父類指針根據賦給它的不同的子類指針,動態的調用屬於子類的該函數,這樣的函數調用在編譯期間是無法確定的(調用的子類的虛函數的地址無法給出)。   補充:“隱藏”是指派生類的函數屏蔽了與其同名的基類函數。規則如下: (1)如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆)。 (2)如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有virtual 關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)。

發佈了33 篇原創文章 · 獲贊 8 · 訪問量 4904
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章