C++ 進階總復習 (1~15)
- 目的
- 1. 介紹下程序從編寫到可執行的整個過程
- 2. C++中的auto和decltype的區別
- 3. 介紹下多態的實現原理
- 4. C++中的new[] 和delete[] 為什么一定要配對使用?
- 5. C++中malloc申請的內存 可以使用delete釋放嘛
- 6. 什么情況下會出現內存泄漏
- 7. C++ String內部是使用堆內存還是棧內存
- 8. 平時開發C++程序的時候錯誤碼使用的多還是異常使用的多
- 9. C++中如何進行性能優化
- 10. 模板的實現一定要放在頭文件中嘛?
- 11. 什么場景下使用繼承 什么場景下使用組合
- 12. 什么情況下會出現死鎖 如何避免
- 13. 如何實現線程池 給出大致思路
- 14. C++針對返回值進行的優化
- 15. C++中動靜態庫的區別
目的
寫這一系列文章的目的主要是為了秋招時候應對計算機基礎問題能夠流暢的回答出來 (如果不整理下 磕磕絆絆的回答會被認為是不熟悉)
本文章題目的主要來源來自于 面試鴨
部分面試鴨上沒有而牛客網上有的博主會進行查缺補漏
題目編號按照面試鴨官網題號方便大家尋找
題解大部分是博主根據自己之前的博客再加上部分網上的內容進行口語化的表述 如果涉及到省略的部分博主會提供自己或者其他人的博客鏈接
1. 介紹下程序從編寫到可執行的整個過程
參考博客
預處理相關知識
程序從編寫到可執行分為下面幾個過程
預處理 編譯 匯編 鏈接
首先是預處理階段 在這個階段的時候程序會展開頭文件 宏替換 去注釋等操作
到了編譯階段 這個階段最大的作用就是進行語法詞法分析 并對程序進行優化 看看有沒有什么錯誤的代碼 之后轉化為匯編語言
匯編階段則是將匯編語言轉化為二進制語言
鏈接階段則會生成一個可執行程序 它會合并段表 符號表合并和重定位
2. C++中的auto和decltype的區別
auto關鍵字用于自動推導變量的類型
而decltype關鍵字用于推導表達式的類型 它會返回表達式對應的類型信息而不進行計算
decltype關鍵字在模板編程的時候十分好用
3. 介紹下多態的實現原理
參考博客
多態原理
這里多態主要包括兩點 一個是靜態多態 一個是動態多態
靜態多態的話 它的原理就是函數重載嘛 我們都知道在C語言鏈接階段的時候是通過函數名來標記一個函數的(參數不論) 但是C++的話 參數的個數 順序 類型都會影響函數標記 所以說這就是它的底層原理
然后動態多態的話就是通過虛指針和虛表來實現的嘛
如果說我們的類中有一個虛函數 那么實例化對象的時候 這個對象的大小就會多出四個字節 實際上就是多出一個指針的大小 這個指針就是虛指針
然后這個指針會指向一張表 我們把這張表叫做虛表每個類的虛表都不同 所以說自然會產生不同的函數效果
4. C++中的new[] 和delete[] 為什么一定要配對使用?
因為如果不這樣做可能會造成內存泄漏的情況
我們一般來說是建議new[] 和 delete[] 配對使用的 但是說也有特殊情況
比如說如果我們new []了內置類型的話 我們使用delete刪除就不會造成內存泄漏的情況
如果是自定義類型的話 就會造成這種情況
為什么自定義類型會造成而內置類型不會造成呢? 這里可以推斷一下 可能是因為內置類型重載了opeartor new 和opeartor delete
5. C++中malloc申請的內存 可以使用delete釋放嘛
不可以 malloc申請的內容應該使用free釋放
雖然說delete底層是調用free函數 但是delete還做了更多的事情
它首先會調用析構函數 之后才會釋放內存
如果說直接使用delete的話很可能會出現未定義行為 甚至報錯
6. 什么情況下會出現內存泄漏
參考博客
內存泄漏
其實很多情況下都會出現內存泄漏
比如說指針原本指向一塊分配好的內存 但是指針丟失了
比如說循環引用
比如說創建對象之后沒有釋放
那么如何解決呢?
當然也有很多種方法解決
我們可以使用內存泄漏檢測工具 valgrind
然后使用RAII思想去檢測
有一個良好的代碼編程習慣等等
7. C++ String內部是使用堆內存還是棧內存
參考博客
string的模擬實現
模擬實現過string之后就會知道 string的底層其實維護了一個指針 這個指向會指向堆上開辟的空間
所以說string內部使用的是堆內存
8. 平時開發C++程序的時候錯誤碼使用的多還是異常使用的多
錯誤碼使用的比較多
因為錯誤碼相對于異常來說
- 性能開銷更小
- 更適合處理復雜的流程 能夠只管的反應錯誤信息
但是錯誤碼也有缺點
就是我們寫需求的時候 每次寫新的需求 大概率就要寫新的錯誤碼 就會造成代碼臃腫 如果不注釋 就很難知道錯誤碼表示什么信息 可能出現意料之外的錯誤
9. C++中如何進行性能優化
這個可以從很多點上去回答
從代碼編寫的角度來說
- 選取合適的數據結構 比如說在頻繁查詢的場景下使用哈希表來存儲數據
- 選擇合適的算法 優化算法的時空復雜度
- 使用RAII思想 使用對象管理資源 避免資源泄漏
從架構層面上來說
根據需求來設計合適的架構 比如redis和mysql的冷熱數據分離
10. 模板的實現一定要放在頭文件中嘛?
參考博客
模板的編譯分離
我們一般來說是簡歷將模板和生命和定義放在一起的
但是也不是沒有辦法比如說我們可以顯示的實例化
這樣子鏈接的時候就不會報錯了
11. 什么場景下使用繼承 什么場景下使用組合
我們在is a的關系下使用繼承
在has a的關系下使用組合
就比如說人類和學生的關系 學生是人類 所以說這個時候我們推薦使用繼承
比如說輪胎和汽車的關系 輪胎是汽車的一部分 所以說這個時候我們推薦使用組合
12. 什么情況下會出現死鎖 如何避免
參考博客
多線程
這里其實是考察死鎖的四個必要條件
- 互斥 一個資源每次只能被一個執行流使用
- 請求與保持 一個執行流因請求資源而阻塞時 對已獲得的資源保持不放
- 不剝奪 一個執行流已獲得的資源 在未使用完之前 不能強行剝奪
- 循環等待 若干執行流之間形成一種頭尾相接的循環等待資源的關系
至于如何避免死鎖就很簡單了 我們只需要破壞死鎖的四個必要條件即可
13. 如何實現線程池 給出大致思路
這里具體參考我的這篇博客
線程池
這個問題是個很宏大的問題 能夠引出很多小問題 可以把上面那篇博客吃透之后再去回答
14. C++針對返回值進行的優化
在返回一個臨時對象的時候 如果按照一般的方式 可能要經歷
析構函數 拷貝構造
而C++優化可能會直接在返回值位置構建一個對象 這就是C++針對返回值的優化
15. C++中動靜態庫的區別
參考博客
動態庫和靜態庫
動靜態庫的本質其實都是半成品程序 也就是說.o文件 也可以說是二進制文件嘛
靜態庫的話其實就是在程序鏈接的時候將這個.o文件打包到程序里面嘛
動態庫的話是程序運行的時候將動態庫加載到共享區去
他們之前的區別主要是 靜態庫打包到程序里面 它就不需要依賴外部文件了嘛 但是有利有弊就會造成程序的膨脹
動態庫就是相反嘛 節省磁盤空間 減少冗余代碼 但是說如果缺少了庫文件就無法運行
打個比方的話就是 將庫文件比作電腦嘛
靜態庫就是電腦放在宿舍里面 隨時都能玩 但是占用空間
動態庫就是電腦放在網吧 想玩要去網吧玩 但是占用空間小