【C++後臺開發面經】面試總結第二波:C++面試題合集

前言

     面試總結第二波,主要針對自己在看別人面經時,總結和C++面試相關的問題。

C++面試題合集

目錄

C++面試題合集

1、C和C++的區別?

2、C++多態

3、引用和指針的區別?

4、函數中值傳遞、引用傳遞、指針傳遞有什麼區別?

5、static關鍵字的作用

6、const關鍵字的作用

7、memset、memcpy、strcpy有什麼區別?

8、析構函數有哪些特點?

9、虛函數的作用?

10、分別給出bool、int、float、指針變量和0比較的if語句

11、內聯函數與普通函數有什麼區別?

12、拷貝構造函數自動調用的情況

13、函數模板和函數重載異同?

14、C++中explicit關鍵字有什麼作用?

15、C++中restrict關鍵字有什麼作用?

16、面向對象的三大特徵

17、在C++ 程序中調用被C 編譯器編譯後的函數,爲什麼要加extern “C”?

18、頭文件中的ifndef/define/endif有什麼作用?

19、爲什麼構造函數不能爲虛函數?

20、堆和棧的區別?

21、malloc/free和new/delete的區別

22、常見的內存錯誤及對策

23、爲什麼要使用字節對齊

24、四種強制類型轉換

25、C++中的內存分配

26、平衡二叉樹和紅黑樹的區別

1、C和C++的區別?

  • C語言是面向過程的語言,面向過程即加工的是一個一個的函數;而C++是面向對象的語言,面向對象即加工的是一個一個的類。
  • C語言中,表達式的結果放在寄存器,是一個數;C++中,表達式返回的變量的本身。
  • C語言中const是一個冒牌貨,是一個只讀的變量,有自己的存儲空間,通過指針可以修改;C++中const放在符號表中,當取地址時,會單獨開闢一個內存空間(編譯器編譯期間)。
  • C++對C函數進行了擴展,一個是inline函數代替了宏代碼,另一個是函數重載(名稱相同,參數不一樣)。

2、C++多態

C++多態分:靜態多態和動態多態

靜態多態:函數重載、函數模板,都是在編譯器間完成的,函數重載是因爲C++編譯器在編譯時,會對函數名進行擴展,比如fun(int,int)——>fun_int_int,以此來區分不同的函數;函數模板是因爲C++編譯器會進行兩次編譯,對於第二次編譯會按照不同的函數參數類型,生成多個對應的函數,以此來區分。

動態多態:指程序在運行期間,根據父類指針指向的對象,來判定會執行哪個對象的虛函數。

實現多態三個條件:

  • 要有繼承
  • 要有虛函數重寫
  • 用父類指針(引用)指向子類對象

虛析構函數

通過父類指針釋放所有子類資源,則需要把所有父類的析構函數寫成虛析構函數。 

重載重寫重定義

重載:必須在同一個類中進行

重寫:必須保證發生在子類與父類之間,加virtual就是多態,不加就是重定義

定義一個父類對象,遇到func函數,如果不會發生多態,則只執行父類中的函數;

定義一個子類對象,遇到func函數,如果子類已存在,則不會去父類查找,只會在子類中查找,子類沒有則再向父類查找

多態原理

用類定義對象的時候,C++編譯器會在對象中添加一個vptr指針,用來指向虛函數表(是一個存儲類成員的函數指針的數據結構),C++編譯器根本不需要區分是子類對象還是父類對象,父類對象和子類對象都有vptr指針,然後去找各自對應的虛函數表,再找對應的函數入口地址。 

vptr指針是分步初始化的

       構造函數中,調用虛函數能發生多態嗎?不能,當執行父類構造函數時,子類的vptr指針是指向父類的虛函數表的,當父類的構造函數執行完後,會把子類的vptr指針指向子類的虛函數表。 

把所有函數都聲明成虛函數好嗎?

       不好,vptr調用重寫函數是在程序運行時進行的,因此需要通過尋址操作才能確定真正應該調用的函數,出於效率考慮,沒有必要將所有函數都聲明成虛函數。

純虛函數和抽象類

抽象類不能被實例化。 

3、引用和指針的區別?

      引用不是一個變量,而指針是一個變量。引用實質上是一個常量指針,而C++中的常量是存在一個符號表中的,不是一個變量,變量是存儲空間的別名。而由於引用也是一個指針所以它和指針一樣所佔的存儲空間大小一樣,和類型無關。

4、函數中值傳遞、引用傳遞、指針傳遞有什麼區別?

  • 值傳遞:會爲形參分配內存,然後將實參的值拷貝給形參,形參值的改變不會影響實參,函數結束後,形參內存釋放;
  • 引用傳遞:不會爲形參分配內存,因爲引用就是原實參變量的一個別名,所以改變形參就是改變實參,函數結束後,形參不會被釋放;
  • 指針傳遞:形參爲指針變量,傳遞進來的是實參的內存地址,可以改變實參的值,調用時爲形參指針分配內存,結束時釋放指針變量。

5、static關鍵字的作用

  • 函數體內的static變量只在該函數類可以使用且只分配一次內存,其值調用時維持上次的值。
  • 在一個模塊內全局static變量只被該文件的所有函數使用,不能被其他文件的函數使用;函數也一樣
  • 在一個類中的static變量,只屬於整個類,對類的對象只有一份拷貝
  • 在一個類中的static函數,只屬於整個類,不接受this指針,所以只能訪問static變量

6、const關鍵字的作用

  • 對於一個變量來說,如果不想被改變,可以定義成const,需要初始化。
  • 對於一個指針來說,可以定義指針本身是const,即該指針不能改變指向的對象,也可以定義所指對象是const,即不能通過*p來改變所指的對象值,或者二者都可以是const
  • 對於類的成員函數定義成const,則不能改變類中的成員變量
  • 對於類的成員函數,有時候需要定義其返回值是const,以使得其返回值不是左值

7、memset、memcpy、strcpy有什麼區別?

  • memset:用來對一段內存空間全部賦值爲某一個值
  • memcpy:用來做內存拷貝,可以拷貝任何數據對象,還可以指定拷貝的數據長度
  • strcpy:只能拷貝字符串,遇到\0截至

8、析構函數有哪些特點?

     特殊的類成員函數,沒有返回類型,沒有參數,沒有重載;public、protected、private等權限對於析構函數無效;析構函數不能手動調用,只能在類對象結束生命週期時,由系統自動調用釋放放在構造函數中分配的資源。

9、虛函數的作用?

  • 虛函數就是使子類用同樣的函數可以對父類進行覆蓋,並且通過父類指針調用時,如果覆蓋則調用子類覆蓋後的函數,如果沒有覆蓋,則調用父類中的函數,實現多態的效果
  • 如果是純虛函數,則純粹爲了子類函數進行覆蓋時統一函數名而已,子類必須覆蓋純虛函數,否則子類也是抽象類
  • 含有純虛函數的類是抽象類,不能實例化,常用作接口

10、分別給出bool、int、float、指針變量和0比較的if語句

bool:if(!var)
int:if(var==0)
float:const float EPSINON=1e-6; if((x>=-EPSION)&&(x<=EPSINON))
指針變量:if(var==NULL)

11、內聯函數與普通函數有什麼區別?

  • 聲明和函數體必須一起
  • C++編譯器直接將函數體插入到被調用的地方(沒有額外的壓棧、跳轉、返回的開銷)
  • 函數體必須比較簡單,沒有過多的for、if語句

12、拷貝構造函數自動調用的情況

  • 當類的一個對象去初始化另一個對象時
  • 如果函數的形參是一個類對象時,實參賦值給形參時
  • 返回一個類對象時

13、函數模板和函數重載異同?

  • 函數重載指幾個函數,函數名字相同,函數參數類型不同或參數個數不同的函數
  • 函數模板是指幾個函數具體算法相同,但參數類型不同
  • 模板函數可以減少重載函數,但也可能引發錯誤

14、C++中explicit關鍵字有什麼作用?

     和構造函數一起用,表示這個函數必須顯式調用,防止不必要的隱式調用類型轉換構造函數。

15、C++中restrict關鍵字有什麼作用?

     用來優化,修飾指針的,限制多個指針指向同一個地址。

16、面向對象的三大特徵

  • 封裝:就是把一個客觀的事物抽象成一個類,然後用不同的修飾屬性:public、protected、private對數據和方法進行修飾,對可信的類或者對象進行操作,對不可信的進行信息隱藏;
  • 繼承:可以使用現有類的所有功能,無需重新編寫原先類的一些功能,可以直接進行擴展,通過繼承創建的類稱爲子類或者派生類,被繼承的類稱爲父類或基類。
  • 多態:通過父類指針根據所指的對象,所表示出不同的特性。

17、在C++ 程序中調用被C 編譯器編譯後的函數,爲什麼要加extern “C”?

       C++支持函數重載,而C不支持重載,C++編譯器編譯後的函數名和C是不同的,包含了函數名、函數參數數量及類型信息,C++就是靠這種機制實現重載的。被extern "C"修飾的變量和函數是按照C語言方式編譯和連接的,所以這個聲明的真實目的就是解決名字匹配問題,實現C++和C的混合編程。

18、頭文件中的ifndef/define/endif有什麼作用?

     這是C++預編譯頭文件保護符,保證即使文件被多次包含,頭文件也只定義一次。

19、爲什麼構造函數不能爲虛函數?

     虛函數是採用一種虛調用方法,虛調用是一種可以在只有部分信息情況下工作的機制。但是創建一個對象,則需要知道對象的準確類型,因此構造函數不能爲虛函數。

20、堆和棧的區別?

  • 棧:有系統自動分配和釋放內存,速度快,棧是向低地址擴展的數據結構,大小有限。
  • 堆:由程序員自己申請和釋放內存,一般較慢,且容易產生碎片,堆是向高地址擴展的不連續內存區域,空間大且相對靈活。

21、malloc/free和new/delete的區別

     new會自動執行類的構造函數,delete會自動執行類的析構函數,而malloc和free不會。

22、常見的內存錯誤及對策

  • 內存還沒分配,就已經使用:在使用內存前應該判斷下是否不爲空
  • 內存雖然分配成功,但沒有初始化就使用了它:就是創建變量時,記得賦初值
  • 內存分配成功也初始化了,但超過了內存邊界:寫程序自己注意
  • 忘記釋放內存,導致內存泄漏
  • 釋放了內存卻繼續使用了它

23、爲什麼要使用字節對齊

      在可接受的空間浪費前提下,儘可能提高對相同元素的快速處理,就是爲了提高CPU訪問數據的效率。

24、四種強制類型轉換

  • static_cast:隱式類型轉換
  • reinterpret_cast:強制類型轉換
  • dynamic_cast:用於繼承中父類向子類轉換
  • const_cast:去掉const屬性

25、C++中的內存分配

  • 全局/靜態存儲區域:全局變量和靜態變量
  • 棧:局部變量、函數參數
  • 堆:new分配的內存塊,需要程序員手動調用delete進行釋放
  • 常量存儲區:存儲常量,不能被修改
  • 代碼區:存放代碼(如函數),不允許修改,但可以執行

26、平衡二叉樹和紅黑樹的區別

  • 平衡二叉樹:嚴格意義上的平衡二叉樹,即左右子樹高度差不會超過1,插入和刪除過於複雜,便於查找
  • 紅黑樹:弱平衡二叉樹,只會保證沒有一個分支的高度大於另一個分支的兩倍,插入和刪除效率較高

27、分佈式的基本原理

28、操作系統相關問題(進程怎麼維護已打開的文件描述符)

29、鎖的實現?信號的詳細實現?TCP狀態的切換?Time-Wait的作用?

30、

31、

Linux啓動原理、Linux日誌3/4分別是什麼意思等 

31、內聯函數、構造函數、靜態成員函數可以是虛函數嗎?

不可以

內聯函數:在編譯時展開,必須有實體;

靜態成員函數:是屬於這個類的,也必須要有實體

構造函數:如果構造函數也是虛函數,那麼它也是存在於虛函數表中,需要通過vptr指針進行查表可得,但構造函數本身都不存在,創建不了實例,class的一些成員函數是不能被訪問的(除靜態成員函數外)。

 

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