C++面試基礎篇(二)

1.數組與指針的區別

數組下標運算實際上都是經過指針進行的。
數組名錶明着指向該數組中下標爲0的元素的指針,但有例外:sizeof(數組名)返回整個數組的大小,而非指針大小;&數組名返回一個指向數組的指針,而不是指向該數組中下標爲0的元素的指針的指針。
數組名做爲參數時,數組名會被轉換成指向該數組下標爲0的元素的指針。
指針操做可能比下標操做效率高,但可維護性卻不必定有下標操做好。
數組和指針不相等。數組

2.野指針是什麼?

野指針就是指向一個已刪除的對象或者未申請訪問受限內存區域的指針安全

3.介紹一下C++中的智能指針

智能指針主要用於管理在堆上分配的內存,它將普通的指針封裝爲一個棧對象。當棧對象的生存週期結束後,會在析構函數中釋放掉申請的內存,從而防止內存泄漏。C++ 11中最經常使用的智能指針類型爲shared_ptr,它採用引用計數的方法,記錄當前內存資源被多少個智能指針引用。該引用計數的內存在堆上分配。當新增一個時引用計數加1,當過時時引用計數減一。只有引用計數爲0時,智能指針纔會自動釋放引用的內存資源。對shared_ptr進行初始化時不能將一個普通指針直接賦值給智能指針,由於一個是指針,一個是類。能夠經過make_shared函數或者經過構造函數傳入普通指針。並能夠經過get函數得到普通指針。app

4.智能指針有沒有內存泄露的狀況

當兩個對象相互使用一個shared_ptr成員變量指向對方,會形成循環引用,使引用計數失效,從而致使內存泄漏。函數

5.智能指針的內存泄漏如何解決

爲了解決循環引用致使的內存泄漏,引入了weak_ptr弱指針,weak_ptr的構造函數不會修改引用計數的值,從而不會對對象的內存進行管理,其相似一個普通指針,但不指向引用計數的共享內存,可是其能夠檢測到所管理的對象是否已經被釋放,從而避免非法訪問。指針

6.爲何析構函數必須是虛函數?爲何C++默認的析構函數不是虛函數 考點:虛函數 析構函數

將可能會被繼承的父類的析構函數設置爲虛函數,能夠保證當咱們new一個子類,而後使用基類指針指向該子類對象,釋放基類指針時能夠釋放掉子類的空間,防止內存泄漏。
C++默認的析構函數不是虛函數是由於虛函數須要額外的虛函數表和虛表指針,佔用額外的內存。而對於不會被繼承的類來講,其析構函數若是是虛函數,就會浪費內存。所以C++默認的析構函數不是虛函數,而是隻有當須要看成父類時,設置爲虛函數。對象

7.C++中析構函數的做用

析構函數與構造函數對應,當對象結束其生命週期,如對象所在的函數已調用完畢時,系統會自動執行析構函數。
析構函數名也應與類名相同,只是在函數名前面加一個位取反符~,例如~stud( ),以區別於構造函數。它不能帶任何參數,也沒有返回值(包括void類型)。只能有一個析構函數,不能重載。
若是用戶沒有編寫析構函數,編譯系統會自動生成一個缺省的析構函數(即便自定義了析構函數,編譯器也老是會爲咱們合成一個析構函數,而且若是自定義了析構函數,編譯器在執行時會先調用自定義的析構函數再調用合成的析構函數),它也不進行任何操做。因此許多簡單的類中沒有用顯式的析構函數。
若是一個類中有指針,且在使用的過程當中動態的申請了內存,那麼最好顯示構造析構函數在銷燬類以前,釋放掉申請的內存空間,避免內存泄漏。
類析構順序:1)派生類自己的析構函數;2)對象成員析構函數;3)基類析構函數。繼承

8.靜態函數和虛函數的區別

靜態函數在編譯的時候就已經肯定運行時機,虛函數在運行的時候動態綁定。虛函數由於用了虛函數表機制,調用的時候會增長一次內存開銷生命週期

9.說一說重載和覆蓋

重載:兩個函數名相同,可是參數列表不一樣(個數,類型),返回值類型沒有要求,在同一做用域中
重寫:子類繼承了父類,父類中的函數是虛函數,在子類中從新定義了這個虛函數,這種狀況是重寫進程

10.說一說strcpy和strlen

strcpy是字符串拷貝函數,原型:
char strcpy(char dest, const char *src);內存

從src逐字節拷貝到dest,直到遇到'\0'結束,由於沒有指定長度,可能會致使拷貝越界,形成緩衝區溢出漏洞,安全版本是strncpy函數。
strlen函數是計算字符串長度的函數,返回從開始到'\0'之間的字符個數。

11.你理解的虛函數和多態

多態的實現主要分爲靜態多態和動態多態,靜態多態主要是重載,在編譯的時候就已經肯定;動態多態是用虛函數機制實現的,在運行期間動態綁定。舉個例子:一個父類類型的指針指向一個子類對象時候,使用父類的指針去調用子類中重寫了的父類中的虛函數的時候,會調用子類重寫事後的函數,在父類中聲明爲加了virtual關鍵字的函數,在子類中重寫時候不須要加virtual也是虛函數。
虛函數的實現:在有虛函數的類中,類的最開始部分是一個虛函數表的指針,這個指針指向一個虛函數表,表中放了虛函數的地址,實際的虛函數在代碼段(.text)中。當子類繼承了父類的時候也會繼承其虛函數表,當子類重寫父類中虛函數時候,會將其繼承到的虛函數表中的地址替換爲從新寫的函數地址。使用了虛函數,會增長訪問內存開銷,下降效率。

12.回答一下++i和i++的區別

++i先自增1,再返回,i++先返回i,再自增1

13.說一下C++裏是怎麼定義常量的?常量存放在內存的哪一個位置?

常量在C++裏的定義就是一個top-level const加上對象類型,常量定義必須初始化。對於局部對象,常量存放在棧區,對於全局對象,常量存放在全局/靜態存儲區。對於字面值常量,常量存放在常量存儲區。

14.回答一下new/delete與malloc/free的區別是什麼

首先,new/delete是C++的關鍵字,而malloc/free是C語言的庫函數,後者使用必須指明申請內存空間的大小,對於類類型的對象,後者不會調用構造函數和析構函數

15.說說虛函數表具體是怎樣實現運行時多態的?

子類若重寫父類虛函數,虛函數表中,該函數的地址會被替換,對於存在虛函數的類的對象,在VS中,對象的對象模型的頭部存放指向虛函數表的指針,經過該機制實現多態。

16.說說C語言是怎麼進行函數調用的?

每個函數調用都會分配函數棧,在棧內進行函數執行過程。調用前,先把返回地址壓棧,而後把當前函數的esp指針壓棧。

17.說說fork,wait,exec函數

父進程產生子進程使用fork拷貝出來一個父進程的副本,此時只拷貝了父進程的頁表,兩個進程都讀同一塊內存,當有進程寫的時候使用寫實拷貝機制分配內存,exec函數能夠加載一個elf文件去替換父進程,今後父進程和子進程就能夠運行不一樣的程序了。fork從父進程返回子進程的pid,從子進程返回0.調用了wait的父進程將會發生阻塞,直到有子進程狀態改變,執行成功返回0,錯誤返回-1。exec執行成功則子進程重新的程序開始運行,無返回值,執行失敗返回-1

18.回答一下靜態函數和虛函數的區別

靜態函數在編譯的時候就已經肯定運行時機,虛函數在運行的時候動態綁定。虛函數由於用了虛函數表機制,調用的時候會增長一次內存開銷

19.說一說C++的內存管理是怎樣的?

在C++中,虛擬內存分爲代碼段、數據段、BSS段、堆區、文件映射區以及棧區六部分。
代碼段:包括只讀存儲區和文本區,其中只讀存儲區存儲字符串常量,文本區存儲程序的機器代碼。
數據段:存儲程序中已初始化的全局變量和靜態變量
bss 段:存儲未初始化的全局變量和靜態變量(局部+全局),以及全部被初始化爲0的全局變量和靜態變量。
堆區:調用new/malloc函數時在堆區動態分配內存,同時須要調用delete/free來手動釋放申請的內存。
映射區:存儲動態連接庫以及調用mmap函數進行的文件映射
棧:使用棧空間存儲函數的返回地址、參數、局部變量、返回值

20.說一下C++/C的內存分配

32bitCPU可尋址4G線性空間,每一個進程都有各自獨立的4G邏輯地址,其中0~3G是用戶態空間,3~4G是內核空間,不一樣進程相同的邏輯地址會映射到不一樣的物理地址中。其邏輯地址其劃分以下: 各個段說明以下: 3G用戶空間和1G內核空間 靜態區域: text segment(代碼段):包括只讀存儲區和文本區,其中只讀存儲區存儲字符串常量,文本區存儲程序的機器代碼。 data segment(數據段):存儲程序中已初始化的全局變量和靜態變量 bss segment:存儲未初始化的全局變量和靜態變量(局部+全局),以及全部被初始化爲0的全局變量和靜態變量,對於未初始化的全局變量和靜態變量,程序運行main以前時會統一清零。即未初始化的全局變量編譯器會初始化爲0 動態區域: heap(堆):當進程未調用malloc時是沒有堆段的,只有調用malloc時採用分配一個堆,而且在程序運行過程當中能夠動態增長堆大小(移動break指針),從低地址向高地址增加。分配小內存時使用該區域。 堆的起始地址由mm_struct 結構體中的start_brk標識,結束地址由brk標識。 memory mapping segment(映射區):存儲動態連接庫等文件映射、申請大內存(malloc時調用mmap函數) stack(棧):使用棧空間存儲函數的返回地址、參數、局部變量、返回值,從高地址向低地址增加。在建立進程時會有一個最大棧大小,Linux能夠經過ulimit命令指定。

相關文章
相關標籤/搜索