騰訊、網易遊戲研發面試題彙總 —— C++篇(不定時更新)

題目來源:牛客網討論帖   答案主要來源:https://github.com/huihut/interview 、書籍、博客。

目錄

  1. C++  
  2. 數據結構與算法
  3. 計算機網絡
  4. 計算機系統  
  5. 計算機圖形學

一、C++ 

1.(解釋一下)多態

  • 多態,即多種狀態(形態)。簡單來說,我們可以將多態定義爲消息以多種形式顯示的能力。
  • 多態是以封裝和繼承爲基礎的。
  • C++ 多態分類及實現:
    1. 重載多態(Ad-hoc Polymorphism,編譯期):函數重載、運算符重載
    2. 子類型多態(Subtype Polymorphism,運行期):虛函數
    3. 參數多態性(Parametric Polymorphism,編譯期):類模板、函數模板
    4. 強制多態(Coercion Polymorphism,編譯期/運行期):基本類型轉換、自定義類型轉換

2.C++虛函數機制

虛函數指針、虛函數表

  • 虛函數指針:在含有虛函數類的對象中,指向虛函數表,在運行時確定。
  • 虛函數表:在程序只讀數據段(.rodata section,見:目標文件存儲結構),存放虛函數指針,如果派生類實現了基類的某個虛函數,則在虛表中覆蓋原本基類的那個虛函數指針,在編譯時根據類的聲明創建。

虛繼承、虛函數

  • 相同之處:都利用了虛指針(均佔用類的存儲空間)和虛表(均不佔用類的存儲空間)
  • 不同之處:
    • 虛繼承
      • 虛基類依舊存在繼承類中,只佔用存儲空間
      • 虛基類表存儲的是虛基類相對直接繼承類的偏移
    • 虛函數
      • 虛函數不佔用存儲空間
      • 虛函數表存儲的是虛函數地址二、數據結構

3.函數重載

C++允許在同一範圍中聲明幾個功能類似的同名函數,但是這些同名函數的形式參數(指參數的個數、類型或者順序)必須不同,也就是說用同一個運算符完成不同的運算功能。這就是重載函數。

4.構造函數是否能爲虛函數?

  • 構造函數不能是虛函數(因爲在調用構造函數時,虛表指針並沒有在對象的內存空間中,必須要構造函數調用完成後纔會形成虛表指針)

5.一個對象的內存分佈,多個虛函數佔多大空間?

()虛函數不佔用存儲空間

6.shared_ptr介紹原理,weak_ptr如何解決引用傳遞?

shared_ptr

多個智能指針可以共享同一個對象,對象的最末一個擁有着有責任銷燬對象,並清理與該對象相關的所有資源。

  • 支持定製型刪除器(custom deleter),可防範 Cross-DLL 問題(對象在動態鏈接庫(DLL)中被 new 創建,卻在另一個 DLL 內被 delete 銷燬)、自動解除互斥鎖

weak_ptr

weak_ptr 允許你共享但不擁有某對象,一旦最末一個擁有該對象的智能指針失去了所有權,任何 weak_ptr 都會自動成空(empty)。因此,在 default 和 copy 構造函數之外,weak_ptr 只提供 “接受一個 shared_ptr” 的構造函數。

  • 可打破環狀引用(cycles of references,兩個其實已經沒有被使用的對象彼此互指,使之看似還在 “被使用” 的狀態)的問題

unique_ptr

unique_ptr 是 C++11 纔開始提供的類型,是一種在異常時可以幫助避免資源泄漏的智能指針。採用獨佔式擁有,意味着可以確保一個對象和其相應的資源同一時間只被一個 pointer 擁有。一旦擁有着被銷燬或編程 empty,或開始擁有另一個對象,先前擁有的那個對象就會被銷燬,其任何相應資源亦會被釋放。

  • unique_ptr 用於取代 auto_ptr

7.右值引用

int i = 42;

int &&r = i * 42;//右值引用

右值引用就是必須綁定到右值(一個臨時對象、將要銷燬的對象)的引用,一般表示對象的值。

右值引用可實現轉移語義(Move Sementics)和精確傳遞(Perfect Forwarding),它的主要目的有兩個方面:

  • 消除兩個對象交互時不必要的對象拷貝,節省運算存儲資源,提高效率。
  • 能夠更簡潔明確地定義泛型函數。

8.編譯器如何處理模版

template <typename T>
int compare(const T &v1, const T &v2)
{
    if(v1 < v2) return -1;
    if(v2 < v1) return 1;
    return 0;
}

當我們調用一個函數模板時,編譯器通常用函數實參來爲我們推斷模板實參。例如實參類型爲int,編譯器會推斷出模板實參爲int,並將它綁定到模板參數T。編譯器用推斷出的模板參數來爲我們實例化一個特定版本的函數。

***9.C/C++編譯和鏈接過程

***10.反彙編時符號表的狀態

11.比較C++與java

java運行在虛擬機上,可以跨平臺,擁有內存管理和垃圾回收機制。

C++直接編譯成可執行文件,運行效率比java高。

java擁有根類Object,C++擁有模板。

12.介紹一下STL的list,查找list複雜度

雙向鏈表,是序列容器,允許在序列中的任何地方進行常數時間插入和擦除操作,並在兩個方向上進行迭代。

13.unorder_map插入複雜度

底層用哈希表實現插入、刪除、查找 O(1) 最差 O(n)

***14.STL迭代器重載

15.遍歷vector的幾種寫法

https://blog.csdn.net/ls306196689/article/details/35787955

16.static的作用

  1. 修飾普通變量,修改變量的存儲區域和生命週期,使變量存儲在靜態區,在 main 函數運行前就分配了空間,如果有初始值就用初始值初始化它,如果沒有初始值系統用默認值初始化它。
  2. 修飾普通函數,表明函數的作用範圍,僅在定義該函數的文件內才能使用。在多人開發項目時,爲了防止與他人命名空間裏的函數重名,可以將函數定位爲 static。
  3. 修飾成員變量,修飾成員變量使所有的對象只保存一個該變量,而且不需要生成對象就可以訪問該成員。
  4. 修飾成員函數,修飾成員函數使得不需要生成對象就可以訪問該函數,但是在 static 函數內不能訪問非靜態成員。

17.std::map 與 std::sort的實現 

底層使用紅黑樹實現。底層使用內省排序即這個排序算法首先從快速排序開始,當遞歸深度超過一定深度(深度爲排序元素數量的對數值)後轉爲堆排序,時間複雜度爲O(n*logn)。

***18.手寫strncpy

***19.f(int, float), f(float, int), 調用f(1, 1)用的哪個?
***20.編譯和鏈接分別做了什麼事情,什麼情況下鏈接失敗?

21.static_cast dynamic_cast區別

對於上行轉換,static_cast和dynamic_cast效果一樣,都安全;

對於下行轉換:你必須確定要轉換的數據確實是目標類型的數據,即需要注意要轉換的父類類型指針是否真的指向子類對象,如果是,static_cast和dynamic_cast都能成功;如果不是static_cast能返回,但是不安全,可能會出現訪問越界錯誤,而dynamic_cast在運行時類型檢查過程中,判定該過程不能轉換,返回NULL。

22.volatile 與 assert();

volatile int i = 10;

  • volatile 關鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素(操作系統、硬件、其它線程等)更改。所以使用 volatile 告訴編譯器不應對這樣的對象進行優化。
  • volatile 關鍵字聲明的變量,每次訪問時都必須從內存中取出值(沒有被 volatile 修飾的變量,可能由於編譯器的優化,從 CPU 寄存器中取值)
  • const 可以是 volatile (如只讀的狀態寄存器)
  • 指針可以是 volatile

assert()

斷言,是宏,而非函數。assert 宏的原型定義在 <assert.h>(C)、<cassert>(C++)中,其作用是如果它的條件返回錯誤,則終止程序執行。可以通過定義 NDEBUG 來關閉 assert,但是需要在源代碼的開頭,include <assert.h> 之前。

23.C++內存模型

根據c/c++對象生命週期不同,c/c++的內存模型有三種不同的內存區域,即

  1. 自由存儲區,動態區、靜態區。
  2. 自由存儲區:局部非靜態變量的存儲區域,即平常所說的棧
  3. 動態區: 用operator new ,malloc分配的內存,即平常所說的堆
  4. 靜態區:全局變量 靜態變量 字符串常量存在位置

而代碼雖然佔內存,但不屬於c/c++內存模型的一部分

24.構造函數與析構函數可以調用虛函數嗎?

不要調用,因爲這類調用從不下降至derived class

 

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