c++實習崗面試問題集錦

c++實習崗面試問題集錦


本文的定位是c++實習崗,因此難度不可能是全職崗那麼深入,重點還是在對有一定c++基礎的人的知識點考察.以供臨陣磨槍之用.


面試技巧

回答問題的時候最最基本的兩個要求:

  • 不緊不慢,平心靜氣
  • 條理清晰

表達能力絕對是面試的時候重要的考察項目。咱們做的是程序員這一行,講究的是團隊協作,不是寫作、畫畫,一支筆、一個人就行了,一個表達能力不行的程序員,要來又有什麼用呢?

除此之外,就是保持良好的心態。古語說得好,只要功夫深,鐵杵磨成針,面試的成功與否,在於平時的積累,臨時抱抱佛腳,看兩道面試題是沒有用的,只要平時足夠努力,成功是水到渠成的事情,平時不怎麼研究技術的,那也就是個聽天由命的事情,只要充分地展示平時自己的所學就可以了。

因此在我看來,不要把面試當作面試,當做一次技術交流,把面試的心態從我要找到一份工作轉變爲我要通過面試去發現不足、提升自己,這樣就會平和多了,即使失敗也不會有太多失望的感覺。

另外,如果平時自己熱衷於研究技術的朋友,真的要有自信,不要覺得別人面試你別人就比你厲害。面試官未必比你優秀,他問的問題往往都是他平時研究得比較多的問題,你一樣有很多自己的研究面試官未必知道。


面試問題

new、delete、malloc、free關係

delete會調用對象的析構函數,和new對應free只會釋放內存,new調用構造函數。malloc與free是C++/C語言的標準庫函數,new/delete是C++的運算符。它們都可用於申請動態內存和釋放內存。對於非內部數據類型的對象而言,光用maloc/free無法滿足動態對象的要求。對象在創建的同時要自動執行構造函數,對象在消亡之前要自動執行析構函數。由於malloc/free是庫函數而不是運算符,不在編譯器控制權限之內,不能夠把執行構造函數和析構函數的任務強加於malloc/free。因此C++語言需要一個能完成動態內存分配和初始化工作的運算符new,以及一個能完成清理與釋放內存工作的運算符delete。注意new/delete不是庫函數。

多態,虛函數,純虛函數
  • 多態:是對於不同對象接收相同消息時產生不同的動作。C++的多態性具體體現在運行和編譯兩個方面:在程序運行時的多態性通過繼承和虛函數來體現;

在程序編譯時多態性體現在函數和運算符的重載上;

  • 虛函數:在基類中冠以關鍵字 virtual 的成員函數。 它提供了一種接口界面。允許在派生類中對基類的虛函數重新定義。

  • 純虛函數的作用:在基類中爲其派生類保留一個函數的名字,以便派生類根據需要對它進行定義。作爲接口而存在 純虛函數不具備函數的功能,一般不能直接被調用。

從基類繼承來的純虛函數,在派生類中仍是虛函數。如果一個類中至少有一個純虛函數,那麼這個類被稱爲抽象類(abstract class)。

抽象類中不僅包括純虛函數,也可包括虛函數。抽象類必須用作派生其他類的基類,而不能用於直接創建對象實例。但仍可使用指向抽象類的指針支持運行時多態性。

指針和引用的區別

本質上的區別是,指針是一個新的變量,只是這個變量存儲的是另一個變量的地址,我們通過訪問這個地址來修改變量。

而引用只是一個別名,還是變量本身。對引用進行的任何操作就是對變量本身進行操作,因此以達到修改變量的目的。

  • 指針:是一個變量類型;指針可以不進行初始化;指針初始化後可以改變,在寫代碼時需要大量的檢測
  • 引用:是一個別名;引用必須要初始化;引用初始化後不可改變,無需檢測
什麼是多態?多態有什麼用途?

C++ 多態有兩種:靜態多態(早綁定)、動態多態(晚綁定)。靜態多態是通過函數重載實現的;動態多態是通過虛函數實現的。

1.定義:“一個接口,多種方法”,程序在運行時才決定要調用的函數。

2.實現:C++多態性主要是通過虛函數實現的,虛函數允許子類重寫override(注意和overload的區別,overload是重載,是允許同名函數的表現,這些函數參數列表/類型不同)。

注:多態與非多態的實質區別就是函數地址是靜態綁定還是動態綁定。如果函數的調用在編譯器編譯期間就可以確定函數的調用地址,併產生代碼,說明地址是靜態綁定的;如果函數調用的地址是 需要在運行期間才確定,屬於動態綁定。

3.目的:接口重用。封裝可以使得代碼模塊化,繼承可以擴展已存在的代碼,他們的目的都是爲了代碼重用。而多態的目的則是爲了接口重用。

4.用法:聲明基類的指針,利用該指針指向任意一個子類對象,調用相應的虛函數,可以根據指向的子類的不同而實現不同的方法。

用一句話概括:在基類的函數前加上virtual關鍵字,在派生類中重寫該函數,運行時將會根據對象的實際類型來調用相應的函數。如果對象類型是派生類,就調用派生類的函數;如果對象類型是基類,就調用基類的函數。

重載(overload)和重寫(overried,有的書也叫做“覆蓋”)的區別?

常考的題目。從定義上來說:

  • 重載:是指允許存在多個同名函數,而這些函數的參數表不同(或許參數個數不同,或許參數類型不同,或許兩者都不同)。

  • 重寫:是指子類重新定義父類虛函數的方法。

從實現原理上來說:

  • 重載:編譯器根據函數不同的參數表,對同名函數的名稱做修飾,然後這些同名函數就成了不同的函數(至少對於編譯器來說是這樣的)。如,有兩個同名函數:function func(p:integer):integer;和function func(p:string):integer;。那麼編譯器做過修飾後的函數名稱可能是這樣的:int_func、str_func。對於這兩個函數的調用,在編譯器間就已經確定了,是靜態的。也就是說,它們的地址在編譯期就綁定了(早綁定),因此,重載和多態無關!

  • 重寫:和多態真正相關。當子類重新定義了父類的虛函數後,父類指針根據賦給它的不同的子類指針,動態的調用屬於子類的該函數,這樣的函數調用在編譯期間是無法確定的(調用的子類的虛函數的地址無法給出)。因此,這樣的函數地址是在運行期綁定的(晚綁定)。

內存的分配方式有幾種?

一、從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變量。

二、在棧上創建。在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。

三、從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc或new申請任意多少的內存,程序員自己負責在何時用free或delete釋放內存。動態內存的生存期由我們決定,使用非常靈活,但問題也最多。

new和malloc的區別?

new是運算符,malloc()是一個庫函數;
new會調用構造函數,malloc不會;
new返回指定類型指針,malloc返回void*指針,需要強制類型轉換;
new會自動計算需分配的空間,malloc不行;
new可以被重載,malloc不能。

C++的內存分區
  • 棧區(stack):主要存放函數參數以及局部變量,由系統自動分配釋放。
  • 堆區(heap):由用戶通過 malloc/new 手動申請,手動釋放。注意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表。
  • 全局/靜態區:存放全局變量、靜態變量;程序結束後由系統釋放。
  • 字符串常量區:字符串常量就放在這裏,程序結束後由系統釋放。
  • 代碼區:存放程序的二進制代碼。
內存泄漏怎麼產生的?如何避免?

內存泄漏一般是指堆內存的泄漏,也就是程序在運行過程中動態申請的內存空間不再使用後沒有及時釋放,導致那塊內存不能被再次使用。

更廣義的內存泄漏還包括未對系統資源的及時釋放,比如句柄、socket等沒有使用相應的函數釋放掉,導致系統資源的浪費。

C和C++區別?

C++在C的基礎上增添類,C是一個結構化語言,它的重點在於算法和數據結構。C程序的設計首要考慮的是如何通過一個過程,對輸入(或環境條件)進行運算處理得到輸出(或實現過程(事務)控制),而對於C++,首要考慮的是如何構造一個對象模型,讓這個模型能夠契合與之對應的問題域,這樣就可以通過獲取對象的狀態信息得到輸出或實現過程(事務)控制。

深拷貝與淺拷貝的區別?
  • 什麼時候用到拷貝函數?
    a.一個對象以值傳遞的方式傳入函數體;
    b.一個對象以值傳遞的方式從函數返回;
    c.一個對象需要通過另外一個對象進行初始化。

    如果在類中沒有顯式地聲明一個拷貝構造函數,那麼,編譯器將會自動生成一個默認的拷貝構造函數,該構造函數完成對象之間的位拷貝。位拷貝又稱淺拷貝;

  • 是否應該自定義拷貝函數?

自定義拷貝構造函數是一種良好的編程風格,它可以阻止編譯器形成默認的拷貝構造函數,提高源碼效率。

  • 什麼叫深拷貝?什麼是淺拷貝?兩者異同?
    如果一個類擁有資源,當這個類的對象發生複製過程的時候,資源重新分配,這個過程就是深拷貝,反之,沒有重新分配資源,就是淺拷貝。

  • 深拷貝好還是淺拷貝好?

如果實行位拷貝,也就是把對象裏的值完全複製給另一個對象,如A=B。這時,如果B中有一個成員變量指針已經申請了內存,那A中的那個成員變量也指向同一塊內存。這就出現了問題:當B把內存釋放了(如:析構),這時A內的指針就是野指針了,出現運行錯誤。

C++中*和&同時使用是什麼意思?
template <class T>
void InsertFront(Node<T>* & head, T item)

上面一個函數的聲明,其中第一個參數*和&分別是什麼意思?head是個指針,前面爲什麼加個& ?

本來“* head”代表的是傳指針的,但是隻能改變head指向的內容,而“* &head”意思是說head是傳進來的指針的同名指針,就能既改變head指向的內容,又能改變head這個指針。比如:main()有個Node p,int t;當調用insertFront(p,t)是,如果template void InsertFront(Node* & head, T item)中有對head進行賦值改變時,main()中的p也會跟着改變,如果沒有&這個別名標識時,p則不會隨着head的改變而改變。

全局變量和局部變量有什麼區別?是怎麼實現的?操作系統和編譯器是怎麼知道的
  • 生命週期不同:

全局變量隨主程序創建和創建,隨主程序銷燬而銷燬;局部變量在局部函數內部,甚至局部循環體等內部存在,退出就不存在;

  • 使用方式不同:通過聲明後全局變量程序的各個部分都可以用到;局部變量只能在局部使用;分配在棧區。

操作系統和編譯器通過內存分配的位置來知道的,全局變量分配在全局數據段並且在程序開始運行的時候被加載。局部變量則分配在堆棧裏面 。

進程與線程的區別?

(1)進程又自己的獨立地址空間,線程沒有
(2)進程是資源分配的最小單位,線程是CPU調度的最小單位
(3)進程和線程通信方式不同
(4)進程切換上下文開銷大,線程開銷小
(5)一個進程掛掉了不會影響其他進程,而線程掛掉了會影響其他線程
(6)對進程進程操作一般開銷都比較大,對線程開銷就小了

析構函數的作用?

析構函數是用來釋放所定義的對象中使用的指針,默認的析構函數不用顯示調用,自建的析構函數要在程序末尾調用。

如果你的類裏面只用到的基本類型,如int char double等,系統的默認析構函數其實什麼都沒有做

但如果你使用了其他的類如vector,string等,系統的默認析構函數就會調用這些類對象的析構函數

淺談 const 與 #define 的區別?

const用於類成員變量的定義,同時const本身就是一個語言結構,而define是一個函數,const在編譯的時候要比define快很多,所以建議,在能使用const中的場合就使用const;

區別:

const用於類成員變量的定義,只要一定義,不可修改。define 不可用於類成員變量的定義,但是可以用於全局變量。
const不能在條件語法中定義,而define可以,比如在if…else…中
const採用一個普通的常量名稱,define可以採用表達式作爲名稱
const只能接受靜態的標量,而define可以採用任何表表達式
const定義常量時大小寫是敏感的,而define可通過第三個參數(true)來指定大小寫是否敏感

虛函數原理(虛函數表)

每一個含有虛函數(無論是其本身的,還是繼承而來的)的類都至少有一個與之對應的虛函數表,其中存放着該類所有的虛函數對應的函數指針。例:

在這裏插入圖片描述虛函數表構造過程:從編譯器的角度來說,B的虛函數表很好構造,D的虛函數表構造過程相對複雜。下面給出了構造D的虛函數表的一種方式:

在這裏插入圖片描述


參考/引用文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章