網易互娛遊戲研發崗準備

歷史經驗

Python大類

  1. Python 都用了哪些模塊?用的是 Python 2 還是 Python 3?2 和 3 有什麼區別?
  2. range() 函數返回什麼?
  3. import 是如何加載的?如果想 import 一個下載的包要怎麼做
  4. 寫一個 Python 的列表生成式,生成 0 到 100 的所有奇數

基礎

  1. 閉包是什麼?閉包裏的函數怎麼傳參?怎麼把函數外的變量傳到函數內
  2. print函數從執行到打印到屏幕上都經歷了什麼?

操作系統

  1. 線程是怎麼切換的?進程是怎麼切換的?一個線程掛了,其他的線程會怎麼樣?
  2. 多線程瞭解嗎?多線程可能會因爲什麼問題?舉個例子說明。爲什麼加了鎖就可以避免?加鎖操作不會出問題嗎?

多線程技術一般又被叫做併發編程,目的是爲了程序運行得更快。其基本原理是,是由cpu進行不同線程的調度,從而實現多個線程的同時運行效果。多進程和多線程類似,只是多進程不會共享內存資源,切換開銷更大,所以多線程是更明智的選擇。
cpu通過時間片分配算法來循環執行任務,當前任務執行一個時間片後會切換到下一個任務,但是在切換前會保存上一個任務的狀態,以便下次切換回這個任務時,可以再加載到這個任務的狀態————這個從保存任務狀態到再加載的過程就叫做一次上下文切換。如果只運行與cpu能力範圍內的n線程,那是絕對ok的。但當你線程數超過這個n時,就會涉及到cpu的調度問題,調度時即會涉及一個上下文切換問題,這是要耗費時間和資源的東西。當cpu疲於奔命調度切換時,則多線程就是一個負擔了。

簡而言之,一個程序至少有一個進程,一個進程至少有一個線程. 線程的劃分尺度小於進程,使得多線程程序的併發性高。另外,進程在執行過程中擁有獨立的內存單元,而多個線程共享內存,從而極大地提高了程序的運行效率。線程在執行過程中與進程還是有區別的。每個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口。但是線程不能夠獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。從邏輯角度來看,多線程的意義在於一個應用程序中,有多個執行部分可以同時執行。但操作系統並沒有將多個線程看做多個獨立的應用,來實現進程的調度和管理以及資源分配。這就是進程和線程的重要區別。

進程是具有一定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位.線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源.一個線程可以創建和撤銷另一個線程;同一個進程中的多個線程之間可以併發執行.

進程和線程的主要差別在於它們是不同的操作系統資源管理方式。進程有獨立的地址空間,一個進程崩潰後,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不同執行路徑。線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉,所以多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對於一些要求同時進行並且又要共享某些變量的併發操作,只能用線程,不能用進程。

當多個線程訪問某個方法時,不管你通過怎樣的調用方式或者說這些線程如何交替的執行,我們在主程序中不需要去做任何的同步,這個類的結果行爲都是我們設想的正確行爲,那麼我們就可以說這個類時線程安全的。

  1. 作業調度的算法
    https://www.cnblogs.com/kxdblog/p/4798401.html
  2. 進程和線程的區別

根本區別:進程是操作系統資源分配的基本單位,而線程是任務調度和執行的基本單位

在開銷方面:每個進程都有獨立的代碼和數據空間(程序上下文),程序之間的切換會有較大的開銷;線程可以看做輕量級的進程,同一類線程共享代碼和數據空間,每個線程都有自己獨立的運行棧和程序計數器(PC),線程之間切換的開銷小。

所處環境:在操作系統中能同時運行多個進程(程序);而在同一個進程(程序)中有多個線程同時執行(通過CPU調度,在每個時間片中只有一個線程執行)

內存分配方面:系統在運行的時候會爲每個進程分配不同的內存空間;而對線程而言,除了CPU外,系統不會爲線程分配內存(線程所使用的資源來自其所屬進程的資源),線程組之間只能共享資源。

包含關係:沒有線程的進程可以看做是單線程的,如果一個進程內有多個線程,則執行過程不是一條線的,而是多條線(線程)共同完成的;線程是進程的一部分,所以線程也被稱爲輕權進程或者輕量級進程。
4. 死鎖的四個必要條件
https://blog.csdn.net/wljliujuan/article/details/79614019

計算機網絡

  1. TCP 和UDP 有什麼區別?
    https://blog.csdn.net/zhang6223284/article/details/81414149

  2. UDP 包如何實現可靠?
    https://www.jianshu.com/p/6c73a4585eba

  3. 數據是怎麼在 TCP 收發端傳輸的?.

  4. udp的上層協議(會話層以上)有哪些?(不會)
    https://www.jianshu.com/p/700fcf630fe4

  5. 怎麼建立連接(3握)和斷開連接(4揮)

  6. TCP爲什麼是三次握手,兩次握手什麼時候會出錯,select和epoll的區別
    →7.1 講一下3握
    →7.2 講一下4揮

  7. TCP和UDP簡單問了下,三次握手的過程,各自的特點

  8. 學過網絡嗎,都學了些啥(樓主也不可能詳細展開啊,就說主要是學7個層嘛)

  9. 有實現過TCP嗎,知道是怎麼實現的嗎(樓主內心一慌,就說以前實驗有模擬過一次,但是太久遠記不太到了,主要就是模擬客戶端和服務端,好像調用了一個system庫)

  10. 再然後他問socket編程用過嗎?我說我在本科用java寫了個局域網棋牌遊戲,那時候用過一點,之後沒怎麼用過了。他問那網絡你瞭解嗎?我說我就只大概看了TCP和UDP什麼的。他就問那你說說二者區別。我就大概說TCP需要連接比較安全,UDP不需要連接,比較高效,發出去也不保證送達。一般聊天就是TCP遊戲用UDP(我看文章看回來的,就順口一說……)他一下就抓住了後半句,你確定遊戲都用UDP嗎?我說遊戲數據交換量大嘛,用UDP更快速。他說那我玩推箱子,向前向左向前,向左指令發丟了會發生什麼?我說那你就向前了兩次,這就是玩遊戲丟包嘛。他說那你覺得這可以嗎?我說像推箱子這種數據量小的可以用TCP嘛,數據量實在大的才用UDP。他說那moba遊戲呢?比如我的英雄放兩個互相配合的技能,前一個技能丟包了,你覺得可以接受嗎?我說不能接受,但遊戲丟包沒辦法嘛,我玩英雄聯盟就經歷過丟包。他說那你想想辦法改進一下。接下來就開始了我長達十分鐘的痛苦的UDP改進之旅。我其實想說那要不用回TCP吧,但他一直說“既然你那麼堅持UDP”,弄得我都不好意思回頭了。不過這部分應該就是他引導我思考的一個過程,看我能拿出怎麼個解決方案吧。不過網絡編程我是真不熟,根據他的意思反覆改了幾次,他才放過了我。

  11. TCP和UDP的區別及應用場景
    區別

面向連接VS無連接
TCP建立一個連接需要3次握手IP數據包,斷開連接需要4次握手。另外斷開連接時發起方可能進入TIME_WAIT狀態長達數分鐘(視系統設置,windows一般爲120秒),在此狀態下連接(端口)無法被釋放。
UDP不需要建立連接,可以直接發起。
可靠VS不可靠
TCP利用握手、ACK和重傳機制,udp沒有。
1,校驗和(校驗數據是否損壞);
2,定時器(分組丟失則重傳);
3,序列號(用於檢測丟失的分組和重複的分組);
4,確認應答ACK(接收方告知發送方正確接收分組以及期望的下一個分組);
5,否定確認(接收方通知發送方未被正確接收的分組);
6,窗口和流水線(用於增加信道的吞吐量)。(窗口大小:無需等待確認應答而可以繼續發送數據的最大值)
有序性
TCP利用seq序列號對包進行排序,udp沒有。
面向字節流vs面向報文
面向報文
面向報文的傳輸方式是應用層交給UDP多長的報文,UDP就照樣發送,即一次發送一個報文。因此,應用程序必須選擇合適大小的報文。若報文太長,則IP層需要分片。UDP對應用層交下來的報文,既不合並,也不拆分,而是保留這些報文的邊界。這也就是說,應用層交給UDP多長的報文,UDP就照樣發送,即一次發送一個報文。(一個upd的最大報文長度2^16-1-20-8,20是ip報文頭,8是udp報文頭)
面向字節流
面向字節流的話,雖然應用程序和TCP的交互是一次一個數據塊(大小不等),但TCP把應用程序看成是一連串的無結構的字節流。TCP有一個緩衝,當應用程序傳送的數據塊太長,TCP就可以把它劃分短一些再傳送。如果應用程序一次只發送一個字節,TCP也可以等待積累有足夠多的字節後再構成報文段發送出去。
tcp有流量控制,udp沒有
tcp的頭部比20bytes,udp8byres
TCP應用場景:
效率要求相對低,但對準確性要求相對高的場景。因爲傳輸中需要對數據確認、重發、排序等操作,相比之下效率沒有UDP高。舉幾個例子:文件傳輸(準確高要求高、但是速度可以相對慢)、接受郵件、遠程登錄。

UDP應用場景:
效率要求相對高,對準確性要求相對低的場景。舉幾個例子:QQ聊天、在線視頻、網絡語音電話(即時通訊,速度要求高,但是出現偶爾斷續不是太大問題,並且此處完全不可以使用重發機制)、廣播通信(廣播、多播)。

  1. TCP連接的建立和斷開
  2. 死鎖的條件及解決的辦法
    死鎖的條件
    必須同時存在以下的四個條件才能發生死鎖。

互斥條件
即某個資源在一段時間內只能由一個進程佔有,不能同時被兩個或兩個以上的進程佔有。這種獨佔資源如CD-ROM驅動器,打印機等等,必須在佔有該資源的進程主動釋放它之後,其它進程才能佔有該資源。這是由資源本身的屬性所決定的。
不可搶佔條件。
進程所獲得的資源在未使用完畢之前,資源申請者不能強行地從資源佔有者手中奪取資源,而只能由該資源的佔有者進程自行釋放。
佔有且申請條件。
進程至少已經佔有一個資源,但又申請新的資源;由於該資源已被另外進程佔有,此時該進程阻塞;但是,它在等待新資源之時,仍繼續佔用已佔有的資源。
循環等待條件
存在一個進程等待序列{P1,P2,…,Pn},其中P1等待P2所佔有的某一資源,P2等待P3所佔有的某一源,…,而Pn等待P1所佔有的的某一資源,形成一個進程循環等待環。
死鎖的預防
死鎖的預防是保證系統不進入死鎖狀態的一種策略。

算法

  1. 說一下數組和隊列的區別,如何用數組表示一個隊列?

  2. TOP-K 問題
    https://blog.csdn.net/qq_24629159/article/details/86516666

  3. 講一下了解的排序算法
    https://blog.csdn.net/hellozhxy/article/details/79911867

  4. 給一個英文句子,怎麼把單詞逆序

  5. 如何判斷交叉鏈表?怎麼找到交叉點?

  6. 什麼是八叉樹?

  7. 堆和棧有什麼區別?

  8. 給一個數組,如何把所有的 0 都移到數組最後?手寫代碼

  9. 講一下快排原理,分析時間複雜度

  10. 給一副有序牌,設計一個打亂算法使得每張牌到每個位子的概率一樣(Knuth,不過當時不記得具體怎麼實現了,跪

  11. 給一個無限長鏈表,怎麼隨機抽K個數,保證每個數抽到概率一致(蓄水池抽樣,完全沒印象

  12. 給你一個數,字符串的,返回浮點數(先判合法性、再轉
    →12.1 合法性解釋
    →12.2 如果是16進制的科學計數法呢?

  13. 手擼反轉鏈表,top-K(大家可以看一下BFPRT O(N))的算法,似乎很加分

  14. 2,3,5元的硬幣,湊出M元的方法 (動態規劃,動態轉移方程,遞推式)

  15. 一個數軸上,有一堆點,每個點向左向右有一個速度,問兩點相遇的最小時間 O(N)的算法(我現在才意識到。。好簡單啊。。。)

  16. 一道海量數據(哈希函數的設定)(之前看面經 看着簡單,做起來好無奈啊)

  17. 給你兩個字符串,比如abc和aeabbqqqcc,問第二個串中包含多少個第一個串,可以不連續,並且任意兩個串不能有公共部分,例子答案是2個
    這個題和PAT上的一個題差不多,O(n)掃一遍就出答案了

  18. 給你一個無序數組,找兩個元素ai和aj,使得ai-aj最大,並且i>j
    直接貪心就行了吧,但是面試官要求用dp搞。。把序列a當成一個前綴和,還原出原始序列,求最大連續子段和

  19. 給你一個數組,每個數組有一個正數ai,表示從i位置可以一步往後走到i+ai,當然也可以一步從i走到i+1,問從1走到n最少幾步
    比較簡單的dp吧,或者直接BFS一下

  20. 在之前回答完A*算法後又讓我簡介下最小路徑算法(樓主說了Dijkstra和floyd算法)

  21. 找出一個無序數組n中大小前K個數據(樓主說了O(n)的類似快排的計數算法)

  22. 這個算法時間複雜度是多少(O(n))

  23. 爲什麼是O(n)(理想情況下每次都在一半里面尋找,1/2+1/4+1/8+…,最後就是O(n))

  24. 這個算法時間複雜度的係數是多少(樓主慌忙之中給他算了一下,2)

  25. 如果k很小怎麼辦(樓主說了O(nlogk)的最大堆算法)

  26. 你說的最大堆算法時間複雜度是多少?(O(nlogk))

  27. 你覺得比之前的O(n)好嗎?(樓主心想好像確實不太對,但是該怎麼編呢,就說如果k特別小的話logk是可能小於2的)

  28. 那k要多小呢?(4)

  29. 那假如說k=5呢(樓主此時已經冷靜下來了,然後就說前面那個算法比較理想的情況係數纔等於2,可能分的情況不好的時候就性能很差,而我後面這個算法就很確定是O(nlogk),後來他就肯定了樓主說其實就是想考察前面這個算法不穩定)

  30. 如果說內存很小怎麼辦(樓主就說還是會採用第二個算法,因爲只要滿足k個大小的內存,後面的數據只要依次調入內存和最大堆堆頂元素進行比較就可以了)

  31. 如果k比內存還大怎麼辦(樓主就慌了,想到外部排序和歸併,但具體怎麼實現的就記不太清了,慌忙中就說了下外部排序和敗者樹(亞軍一定是和冠軍比輸了的),其實也沒怎麼答好吧,後來他就沒有繼續問算法了)

  32. 如何實現一個定時任務的模塊,支持大量,不同時間的定時任務 ,最小堆實現,任務過多即使是o(log n)也不行,如何解決。多個不同時間範圍的堆, 問了會不會linux時間輪算法,不會

  33. 紅黑樹,插入這麼做。算法導論書上有

  34. 接下來一個算法題,給一個英語句子,每個單詞用空格隔開,怎麼將這些單詞按倒轉的順序輸出。
    我第一反應當然是棧啊。用cin逐個單詞讀入壓棧再逐個pop出來。他說這可以,但如果不能用額外的容器呢?我想了想說那用一個臨時的char數組,從後向前遍歷將字符插到臨時的char數組的頭部,遍歷到空格就輸出臨時數組並清空。他說那你這個臨時數組要聲明多大?如果我這個字符串有3個G呢?
    不讓用臨時那就是本地轉換咯。萬幸還是讓我想出個辦法,先將整個字符串倒轉,再逐個單詞倒回來。他聽上去不知道是否滿意這個方案,不過也沒有繼續問下去了。

圖形學

  1. 圖形學是否瞭解?簡單講一下座標變換
  2. 問了一個空間座標系轉換的問題,大概是給了一個三角形,怎麼把這個三角形轉移到相機的那個空間
  3. 我說我圖形學學了一點關於碰撞檢測的東西,問我如何判斷一道光線是否與物體碰撞,怎麼找到碰撞點?
  4. 光照模型,渲染方程,BRDF,延遲渲染balabala

https://blog.csdn.net/hcbbt/article/details/42779341

c++

  1. c++ 的多態是怎麼實現的(必考,面前實操了一遍感覺海星)
    →1.1 對象們是怎麼知道要調用哪個函數的
    →1.2 虛函數表長什麼樣子

  2. 函數重載是在什麼時候進行的

  3. vector,list的區別,map,unordered_map的區別,4種容器的內部實現(手撕紅黑樹v2.0?)

  4. C++ 空類,sizeof?(1)
    →4.1 爲什麼是1?(沒答出來,怒跪)
    →4.2 如果只定義了一個函數,sizeof?(1)
    →4.3 如果定義了一個虛函數,sizeof?(4[for 32bit])——實際就是指針的sizeof

  5. new/malloc區別

  6. 虛函數 多態

  7. 列表初始化

  8. const的各種作用,static的各種作用

  9. 智能指針

  10. 內存對齊(給一個類問類的大小,爲什麼要內存對齊)

  11. 設計模式(隨便介紹一個,我就簡單說了observer的原理,用什麼結構實現? 鏈表)

  12. c++11特性

  13. 智能指針(底層實現,引用計數)

  14. 然後問了下C++的虛函數的實現,能不能inline,以及宏和inline函數的區別

  15. 虛函數的作用以及實現原理?

  16. 熟練STL嗎? STL中有什麼類?

  17. Vector是怎麼實現的?

  18. 如果vector最開始是24,然後每次不夠就翻倍,要經過幾次的變換達到10000的內存(樓主慌忙之中算了一下大概是214接近於10000,就說了大概在13和14次之間吧,然後他就問你忘了最開始的4次方了嗎,樓主連忙說“啊,對,應該是10次,不好意思啊”)

  19. c++多態的實現。講了c++虛函數表,單繼承,多繼承,虛繼承以及爲什麼虛繼承,調用過程

  20. 熟悉stl的什麼結構。我說的是看過sgi 的stl源碼。就問了什麼情況用vector什麼情況用list,以及vector的insert,erase,remove的實現還有重新申請內存的情況

  21. 多態定義
    然後對面問我主要是用什麼語言,我說現在主要學C++。他就問你怎麼理解多態?我說多態就是通過實現繼承讓程序可以在運行時決定被調用的方法是哪一個。雖然這個問題我上次面另外一家也被問過,但我還是沒有準備這個答案,所以實際說得比較亂。不過還好聽對面的語氣他好像也還算滿意,意思是表達出來了。他說你就舉個例子吧。我就舉例子說比如有個職員類,它有個發工資方法,現在再實現它的兩個子類經理類和普通員工類,它們各自有不同的發工資方法。然後在程序執行時就可以用一個職員類的指針指向經理類或者普通員工類對象,然後用這個父類指針調用發工資就會觸發動態綁定,調用到指向對象自己的方法。(雖然回答重點是突出多態的思想就行,但是事後想想要是舉的例子是英雄類,子類是戰士和法師,方法是攻擊就更完美了。職員發工資太無聊了)

  22. 虛函數
    再然後他就順勢問虛函數,問虛函數調用是怎麼實現的。動態綁定發生時是怎麼調用到子類的函數。多繼承的子類有幾個虛函數表。虛函數這個部分我看了應該不下十遍了。學C++應該都會仔細看過這個部分我就不詳細說了。

  23. STL容器
    然後就是問C++的容器,STL的容器我有沒有用過。當然實際上我沒怎麼用過,但是我全都看過,所以就說了幾個vector、list、deque之類的。他就問vector和list有什麼區別。向量和鏈表區別大了去了,一個是連續內存一個是鏈表。一個動態空間靠重分配一個本身就是離散的理論上沒有空間限制。一個可以隨機訪問一個不行……等等等等,不詳細列了。
    他又問map用過嗎,我說也用過。他map底層是怎麼實現的?我說就是紅黑樹嘛。每個值是key-value對,然後他們按key值存在紅黑樹裏。他就問紅黑樹定義,我憑印象大概給他說了一遍。說的時候我說紅黑樹不同於平衡二叉樹,他就問那爲什麼有平衡二叉樹了還要紅黑樹?紅黑樹有哪裏好嗎?我這裏就卡住了,畢竟我沒有思考過這個問題,我就憑想象說紅黑維護代價應該比平衡二叉樹簡單一點吧。畢竟紅黑再重新調整時只涉及單邊子樹,平衡涉及整棵樹。這個因爲是我臨時謅出來的答案,我說的時候也沒那麼自信,他也反覆追問“是指寫程序的時候方便一些?”“你是說平衡效率更高?”最後我才整理出單邊子樹的說法。事後網上查了查,好像確實大概是這麼回事,lucky。
    紅黑樹結束後他又問了大小頂堆,問這是怎麼實現的。我說說是堆實際上是個數組。第i個數的左子節點是2i+1,右是2i+2,每次插入新元素就從最後一個分支節點也就是n/2開始檢查它和子節點的大小關係。這是我面試前專門看過的。他聽了挺滿意,然後問了個用大小頂堆做的問題。給定一個數組,求前n大的數。(這問題我研究生面試也被問過,當時我還不知道大頂堆……)我就很順口,那就維護一個長度爲10的大頂堆嘛。他說你確定是大頂堆?我還覺得奇怪,找最大不是大頂堆?然後他就要我詳細講,我說着說着才反應過來,哦應該是小頂堆……我改過來後他又詳細問了一遍,我就詳細說了實現過程。改了過來應該也算是可以……吧?

24 . 類型轉換
接下來問C++有幾種類型轉換。靜態動態const和重編譯四種,這個定義到處都是,這裏就不寫了。

  1. static
    他又問static關鍵字是什麼用。我說類裏面就是靜態成員函數,無論有幾個類實例都只會有這一份拷貝。他說除了這個還有別的用處嗎?我又說在方法裏同樣也是方法無論調用幾次都只有這一份拷貝,而且只能在方法內被訪問。他問還有嗎?我想想說大概還能作爲全局靜態吧……記不太清了。(這個我確實沒有認真記住,這部分答得也不算好。static具體用法請到別處搜索)
  2. 講一下C++的三大特性封裝、繼承和多態
    封裝可以使得代碼模塊化,繼承可以擴展已存在的代碼,他們的目的都是爲了代碼重用。而多態的目的則是爲了接口重用

封裝:封裝是在設計類的一個基本原理,是將抽象得到的數據和行爲(或功能)相結合,形成一個有機的整體,也就是將數據與對數據進行的操作進行有機的結合,形成“類”,其中數據和函數都是類的成員。

繼承:如果一個類別B“繼承自”另一個類別A,就把這個B稱爲“A的子類”,而把A稱爲“B的父類別”也可以稱“A是B的超類”。繼承可以使得子類具有父類別的各種屬性和方法,而不需要再次編寫相同的代碼。在令子類別繼承父類別的同時,可以重新定義某些屬性,並重寫某些方法,即覆蓋父類別的原有屬性和方法,使其獲得與父類別不同的功能。

  1. 訪問權限

public: 父類對象內部、父類對象外部、子類對象內部、子類對象外部都可以訪問。
protected:父類對象內部、子類對象內部可以訪問,父類對象外部、子類對象外部都不可訪問。
private:父類對象內部可以訪問,其他都不可以訪問。

訪問對象 public protected private
父類 可見 可見 可見
子類 可見 可見 不可見
父類外部 可見 不可見 不可見
子類外部 可見 不可見 不可見
  1. 繼承方式
    ps.三種繼承方式不影響子類對父類的訪問權限,子類對父類只看父類的訪問控制權。繼承方式是爲了控制子類(也稱派生類)的調用方(也叫用戶)對父類(也稱基類)的訪問權限。public、protected、private三種繼承方式,相當於把父類的public訪問權限在子類中變成了對應的權限。 如protected繼承,把父類中的public成員在本類中變成了protected的訪問控制權限;private繼承,把父類的public成員和protected成員在本類中變成了private訪問控制權。

ps.友元是類級別的,不存在繼承的問題。

  1. 多態:多態性可以簡單地概括爲“一個接口,多種方法”,程序在運行時才決定調用的函數,它是面向對象編程領域的核心概念。多態(polymorphism),字面意思多種形狀。

靜態多態:靜態多態也稱爲靜態綁定或早綁定。編譯器在編譯期間完成的,編譯器根據函數實參的類型(可能會進行隱式類型轉換),可推斷出要調用那個函數,如果有對應的函數就調用該函數,否則出現編譯錯誤。

  1. 函數重載

編譯器根據函數不同的參數表,對同名函數的名稱做修飾,然後這些同名函數就成了不同的函數(至少對於編譯器來說是這樣的)。函數的調用,在編譯器間就已經確定了,是靜態的。也就是說,它們的地址在編譯期就綁定了(早綁定)。

  1. 泛型編程

泛型編程就是指編寫獨立於特定類型的代碼,泛型在C++中的主要實現爲模板函數和模板類。
泛型的特性:

  1. 函數模板並不是真正的函數,它只是C++編譯生成具體函數的一個模子。
  2. 函數模板本身並不生成函數,實際生成的函數是替換函數模板的那個函數,比如上例中的add(sum1,sum2),這種替換是編譯期就綁定的。
  3. 函數模板不是隻編譯一份滿足多重需要,而是爲每一種替換它的函數編譯一份。
  4. 函數模板不允許自動類型轉換。
  5. 函數模板不可以設置默認模板實參。比如template 不可以。
    動態多態
    c++的動態多態是基於虛函數的。對於相關的對象類型,確定它們之間的一個共同功能集,然後在基類中,把這些共同的功能聲明爲多個公共的虛函數接口。各個子類重寫這些虛函數,以完成具體的功能。客戶端的代碼(操作函數)通過指向基類的引用或指針來操作這些對象,對虛函數的調用會自動綁定到實際提供的子類對象上去。

宏多態(?)
帶變量的宏可以實現一種初級形式的靜態多態:

#include
#include

// 定義泛化記號:宏ADD
#define ADD(A, B) (A) + (B);

int main()
{
int i1(1), i2(2);
std::string s1("Hello, "), s2(“world!”);
int i = ADD(i1, i2); // 兩個整數相加
std::string s = ADD(s1, s2); // 兩個字符串“相加”
std::cout << "i = " << i << “/n”;
std::cout << "s = " << s << “/n”;
}
動態多態和靜態多態的比較

靜態多態
優點:
由於靜多態是在編譯期完成的,因此效率較高,編譯器也可以進行優化;
有很強的適配性和鬆耦合性,比如可以通過偏特化、全特化來處理特殊類型;
最重要一點是靜態多態通過模板編程爲C++帶來了泛型設計的概念,比如強大的STL庫。
缺點:
由於是模板來實現靜態多態,因此模板的不足也就是靜多態的劣勢,比如調試困難、編譯耗時、代碼膨脹、編譯器支持的兼容性不能夠處理異質對象集合
動態多態
優點:
OO設計,對是客觀世界的直覺認識;
實現與接口分離,可複用
處理同一繼承體系下異質對象集合的強大威力
缺點:
運行期綁定,導致一定程度的運行時開銷;
編譯器無法對虛函數進行優化
笨重的類繼承體系,對接口的修改影響整個類層次;
不同點:
本質不同,早晚綁定。靜態多態在編譯期決定,由模板具現完成,而動態多態在運行期決定,由繼承、虛函數實現;
動態多態中接口是顯式的,以函數簽名爲中心,多態通過虛函數在運行期實現,靜態多臺中接口是隱式的,以有效表達式爲中心,多態通過模板具現在編譯期完成
相同點:
都能夠實現多態性,靜態多態/編譯期多態、動態多態/運行期多態;
都能夠使接口和實現相分離,一個是模板定義接口,類型參數定義實現,一個是基類虛函數定義接口,繼承類負責實現;
2. 虛函數、純虛函數

  • 概述

    它虛就虛在所謂“推遲聯編”或者“動態聯編”上,一個類函數的調用並不是在編譯時刻被確定的,而是在運行時刻被確定的。由於編寫代碼的時候並不能確定被調用的是基類的函數還是哪個派生類的函數,所以被成爲“虛”函數。

    虛函數只能藉助於指針或者引用來達到多態的效果。常用的方式是父類指針指向子類對象。當有多個對於父類的繼承時,可以統一用父類指針來表示各子類對象,但是事實上所指向的對象具體是哪一個,或者說所調用的函數是哪一個子類的對象的函數需要在運行時才知道。這就實現了多態。

    class A
    {
        public:
            virtual void foo()
            {
                cout<<"A::foo() is called"<<endl;
            }
    };
    class B:public A
    {
        public:
            void foo()
            {
                cout<<"B::foo() is called"<<endl;
            }
    };
    int main(void)
    {
        A *a = new B();
        a->foo();   // 在這裏,a雖然是指向A的指針,但是被調用的函數(foo)卻是B的!
        return 0;
    }
    
  • 語法

    virtual void fun1()=0 //純虛函數
    virtual void fun1()  // 虛函數
    
  • 純虛函數
    純虛函數是在基類中聲明的虛函數,它在基類中沒有定義,但要求任何派生類都要定義自己的實現方法。含有虛函數的類成爲抽象類,不能生成對象。

  • 虛函數表
    虛表是屬於類的,而不是屬於某個具體的對象,一個類只需要一個虛表即可 。同一個類的所有對象都使用同一個虛表。爲了指定對象的虛表,對象內部包含一個虛表的指針,來指向自己所使用的虛表。爲了讓每個包含虛表的類的對象都擁有一個虛表指針,編譯器在類中添加了一個指針,*__vptr,用來指向虛表。這樣,當類的對象在創建時便擁有了這個指針,且這個指針的值會自動被設置爲指向類的虛表。
    C++的編譯器應該是保證虛函數表的指針存在於對象實例中最前面的位置(這是爲了保證取到虛函數表的有最高的性能——如果有多層繼承或是多重繼承的情況下)。

  1. 引用和指針的區別
    指針-對於一個類型T,T* 就是指向T的指針類型,也即一個T*類型的變量能夠保存一個T對象的地址,而類型T是可以加一些限定詞的,如const、volatile等等。
    引用-引用是一個對象的別名,主要用於函數參數和返回值類型,符號X&表示X類型的引用。

不同點:

本質: 指針指向一塊內存,它的內容是所指內存的地址;而引用則是某塊內存的別名,引用不改變指向。

引用不可以爲空,但指針可以爲空。定義一個引用的時候,必須初始化。因此使用指針之前必須做判空操作,而引用就不必。
引用不可以改變指向,對一個對象"至死不渝";但是指針可以改變指向,而指向其它對象。
引用的大小是所指向的變量的大小,因爲引用只是一個別名而已;指針是指針本身的大小,4個字節(32位)。
const int* p-> 指向常量的指針,int * const p->本身是常量的指針.後者需要在定義的時候初始化。對於引用來講int const & p=i;和 const int &p=i;沒什麼區別,都是指指向的對象是常量。
引用和指針的++自增運算符意義不同,指針的++表示的地址的變化,一般是向下4個字節的大小(一個只針的大小),引用的++就是對應元素的++操作。
指針傳遞和引用傳遞
指針傳遞參數本質上是值傳遞的方式,它所傳遞的是一個地址值。值傳遞過程中,被調函數的形式參數作爲被調函數的局部變量處理,即在棧中開闢了內存空間以存放由主調函數放進來的實參的值,從而成爲了實參的一個副本。值傳遞的特點是被調函數對形式參數的任何操作都是作爲局部變量進行,不會影響主調函數的實參變量的值。
引用傳遞過程中,被調函數的形式參數也作爲局部變量在棧中開闢了內存空間,但是這時存放的是由主調函數放進來的實參變量的地址。被調函數對形參的任何操作都被處理成間接尋址,即通過棧中存放的地址訪問主調函數中的實參變量。正因爲如此,被調函數對形參做的任何操作都影響了主調函數中的實參變量。
4. 深拷貝和淺拷貝(值拷貝和位拷貝)
對於build-in類型來說,複製是簡單的,都是開闢新的內存,將對應的值放入新開闢的位置即可,之後他們不再有關係(指針類型就是地址的複製,和指向的地方沒關係)。
對於類而言,如果存在指針和引用類型,(const類型?),需要考慮自己來寫拷貝構造函數和重載=操作符。

淺拷貝(位拷貝)
指將一個對象的內存映像按位原封不動的複製給另一個對象,所謂值拷貝就是指,將原對象的值複製一份給新對象。 在用"bitwise assignment"時會直接將對象的內存映像複製給另一個對象,這樣兩個對象會指向同一個內存區域,當一個對象被釋放後,另一個對象的指針會成爲野指針(懸垂指針)。這時,就應該編寫operator=和copy constructor來實現值拷貝 。
默認的拷貝構造函數”和“缺省的賦值函數”均採用“位拷貝”而非“值拷貝”的方式來實現,倘若類中含有指針變量,這兩個函數註定將出錯。

深拷貝(值拷貝)
在“深拷貝”的情況下,對於對象中動態成員,就不能僅僅簡單地賦值了,而應該重新動態分配空間。

產生複製的場景:

建立一個新對象,並用另一個同類的已有對象對新對象進行初始化
當函數的參數爲類的對象時,這時調用此函數時使用的是值傳遞,也會產生對象的複製
函數的返回值是類的對象時,在函數調用結束時,需要將函數中的對象複製一個臨時對象並傳給改函數的調用處
這裏通過查資料存疑, 默認拷貝構造函數基本工作原理:對所有的非POD類型成員變量執行拷貝構造,POD類型成員變量則進行位拷貝,當成員變量都是POD類型的時候,編譯器也許不會產生拷貝構造函數,只是對對象進行位拷貝。 默認operator=基本工資原理:對所有的非POD類型成員變量執行operator=操作,POD類型成員變量則進行位拷貝,當成員變量都是POD類型的時候,編譯器也許不會產生operator=,只是對對象進行位拷貝。 從上面兩個工作原理看到: 1.當class成員變量是有const修飾、引用類型、不能夠提供operator=操作(例如boost::nocopyable)時候就不會產生operator=,我們對這個class對象進行賦值操作的時候就是編譯出錯。 2.當class成員變量是不能夠提供拷貝構造操作(例如boost::nocopyable)時候就不會產生拷貝構造函數,我們對這個class對象進行拷貝構造操作的時候就是編譯出錯。 以前(2005年前?)c++各種參考文獻對構造、析構和拷貝關注較多的是“深拷貝”和“淺拷貝”問題,現在的c++可能考慮更多的是如何保證不被拷貝_(個人見解)。

破壞互斥條件
有些資源不能被共享。–沒用
2. 破壞不可搶佔條件。
可搶佔式,即要求申請失敗的進程釋放自己佔有的資源給別人用,降低系統性能。
3. 破壞佔有且申請條件。
直接申請自己所需要的所有資源。–1.不可預知自己需要什麼資源 2.資源利用率低,長期佔有自己可能不用的資源。
4. 破壞循環等待條件
資源分類、編號,按序申請。 --·1.編號可能是困難的,維護相應的序列是困難的
死鎖的避免
死鎖的避免指的是不限制進程有關申請資源的命令,而是對進程所發出的每一個申請資源命令加以動態地檢查,並根據檢查結果決定是否進行資源分配。
銀行家算法。當一個進程申請使用資源的時候,銀行家算法通過先 試探 分配給該進程資源,然後通過安全性算法判斷分配後的系統是否處於安全狀態,若不安全則試探分配作廢,讓該進程繼續等待。
判定安全狀態需要已分配資源、還需要的資源、可用資源、finish判定符

  1. 複雜度爲O(nlogn)的排序算法有哪些?是否穩定?選一個講一下
    快速排序
    歸併排序
    堆排序
  2. 介紹一下哈希表?哈希函數中如果碰到衝突如何解決?
    哈希表(Hash Table,也叫散列表),是根據關鍵碼值 (Key-Value) 而直接進行訪問的數據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。哈希表的實現主要需要解決兩個問題,哈希函數和衝突解決。

哈希函數
哈希函數也叫散列函數,它對不同的輸出值得到一個固定長度的消息摘要。理想的哈希函數對於不同的輸入應該產生不同的結構,同時散列結果應當具有同一性(輸出值儘量均勻)和雪崩效應(微小的輸入值變化使得輸出值發生巨大的變化)。

衝突解決
鏈地址法的基本思想是,爲每個 Hash 值建立一個單鏈表,當發生衝突時,將記錄插入到鏈表中。

  1. 設計模式瞭解嗎?
    SOLID
    S(單一功能原則Single responsibility principle)規定每個類都應該有一個單一的功能,並且該功能應該由這個類完全封裝起來。所有它的(這個類的)服務都應該嚴密的和該功能平行(功能平行,意味着沒有依賴)。
    O(開閉原則)軟件中的對象(類,模塊,函數等等)應該對於擴展是開放的,但是對於修改是封閉的。
    L(里氏替換原則Liskov Substitution principle)派生類必須能完全取代其基類
    I(接口隔離原則interface-segregation principle) 客戶不依賴於他不使用的方法。
    D(控制反轉原則Inversion of Control)高層級的模塊不應該依賴於低層級的模塊,他們應該都依賴於抽象。細節依賴於抽象,而不是抽象依賴於細節。
    依賴注入:Class A中用到了Class B的對象b,一般情況下,需要在A的代碼中顯式的new一個B的對象。
    採用依賴注入技術之後,A的代碼只需要定義一個私有的B對象,不需要直接new來獲得這個對象,而是通過相關的容器控制程序來將B對象在外部new出來並注入到A類裏的引用中。而具體獲取的方法、對象被獲取時的狀態由配置文件(如XML)來指定。

二叉樹的先序遍歷、中序遍歷、後續遍歷

  1. 如何判斷鏈表是否有環
    快慢指針:定義兩個指針,同時從鏈表的頭節點出發,一個指針一次走一步,另一個指針一次走兩步。如果走得快的指針追上了走得慢的指針,那麼鏈表就是環形鏈表;如果走得快的指針走到了鏈表的末尾(next指向 NULL)都沒有追上第一個指針,那麼鏈表就不是環形鏈表。
    map:如果不考慮空間複雜度,可以使用一個map記錄走過的節點,當遇到第一個在map中存在的節點時,就說明回到了出發點,即鏈表有環,同時也找到了環的入口。

所以尋找環入口的方法就是採用兩個指針,一個從表頭出發,一個從相遇點出發,一次都只移動一步,當二者相等時便是環入口的位置

  1. 如何判斷鏈表是否相交
    我們可以從頭遍歷兩個鏈表。創建兩個棧,第一個棧存儲第一個鏈表的節點,第二個棧存儲第二個鏈表的節點。每遍歷到一個節點時,就將該節點入棧。兩個鏈表都入棧結束後。則通過top判斷棧頂的節點是否相等即可判斷兩個單鏈表是否相交。因爲我們知道,若兩個鏈表相交,則從第一個相交節點開始,後面的節點都相交。
    若兩鏈表相交,則循環出棧,直到遇到兩個出棧的節點不相同,則這個節點的後一個節點就是第一個相交的節點。

遍歷鏈表記錄長度。
同時遍歷兩個鏈表到尾部,同時記錄兩個鏈表的長度。若兩個鏈表最後的一個節點相同,則兩個鏈表相交。
有兩個鏈表的長度後,我們就可以知道哪個鏈表長,設較長的鏈表長度爲len1,短的鏈表長度爲len2。
則先讓較長的鏈表向後移動(len1-len2)個長度。然後開始從當前位置同時遍歷兩個鏈表,當遍歷到的鏈表的節點相同時,則這個節點就是第一個相交的節點。
如何用兩個棧實現一個隊列
二叉樹的廣度優先搜索
二叉樹的深度優先搜索
一個整數數組,一個整數n,如何判斷數組中是否存在兩個數的和等於n
3. 有m個玩家,取遊戲排行榜中排名前k的玩家進行決鬥,如何選取
快排:只排
容量爲k最大堆

遊戲的幀率爲60幀,每秒更新六十次,每次都要上傳玩家數據,假設上傳一次需要100ms,但是一次上傳最多16ms,如何解決
開服創建賬號時,需要對玩家在數據庫中的ID進行分配(數據庫只有一個,但玩家會被分配到多個物理機中進行處理),每個新玩家創建都需要去數據庫中查找,如何減少查找數據庫的次數(提示,一臺物理機向數據庫請求一千條ID,下一臺請求下一千條ID,當一千條分配完後再向數據庫請求)
玩家戰鬥結束後根據積分更新排行榜,在排行榜太大更新速度慢的情況下如何實時更新玩家排名(提示:可以區分粒度)
陰陽師的進度條如何實現?當一個式神的回合結束後進度條如何變化?有沒有影響進度條的情況?
有什麼想問的

實習進去之後會接觸到什麼技術?
主要是使用python語言,引擎可能會使用C++,沒有python基礎不影響,但是最好先自學
如何知道自己是否通過面試?
等待通知,五到十個工作日之內,目前只面了幾個,還不能確定
我的涼了的和沒涼的面試記錄地址(持續更新),https://github.com/darkmoon233/GreenPills-CSNotes

C++:
C++的特性
類的默認生成成員
拷貝構造方式
結合內存對齊的類大小問題
模板實例化
操作系統:
虛擬內存
動態鏈接和靜態鏈接
計算機網絡:
TCP、UDP區別、所在層
ping的相關協議
數據結構算法:
紅黑樹
排序算法
拓撲排序
深度搜索、廣度搜索
編程題:
給了玩家列表,隨機選出N個玩家列表
給了一個整數,得到一個逆序的整數
動態規劃找和最小的路徑

4.多態,虛函數機制?
5.進程,線程,通信?
6.socket,阻塞非阻塞,io複用?
7.虛擬內存作用?
8.電梯算法?用什麼數據結構比較好?
9.tcp,udp,三次握手四次揮手詳細過程?
10.流量控制,擁塞控制?
11.ping是什麼協議?icmp的作用?
12.vector很具體那種實現機制?
13.list排序,鏈表排序怎麼用快排?
14.c 11都有什麼新特性,智能指針?
15.圖形學瞭解嗎,完全不知道?
代碼:
1.求數組中除了當前數的其他數的乘積放到新數組對應位置,從on方一直改到on
2.k個有序數組中位數,只實現了on,面試官讓用二分做,沒思路。
感覺回答了70%吧,代碼完成60%吧,重點來了😁你筆試成績挺好的啊,今天發揮不怎樣啊,下去多看點,感謝你抽出時間參加我們的面試😁😁

C++方面的問題:

1、虛函數的作用以及實現原理

2、overload以及overwrite的區別

1)覆蓋override:派生類函數覆蓋基類函數, 基類函數必須有virtual 關鍵字。

2)重載overload:可以將語義、功能相似的幾個函數用同一個名字表示,但參數不同(包括類型、順序不同),即函數重載。

3)重寫overwrite:派生類的函數屏蔽了與其同名的基類函數

3、overload的話,只有函數返回值類型不同,會重載嗎

不會

4、一個空的class類裏有什麼

1)構造函數

2)拷貝構造函數

3)析構函數

4)賦值運算符重載

5)取地址操作符重載

6)被const修飾的取地址操作符重載

5、struct S{char a; int b; static long c; }請問sizeof(S)是多少?爲什麼,有什麼好處?

涉及到內存對齊機制

靜態數據成員被編譯器放在程序的一個global data members中,它是類的一個數據成員.但是它不影響類的大小,不管這個類實際產生了多少實例,還是派生了多少新的類,靜態成員數據在類中永遠只有一個實體存在。

而類的非靜態數據成員只有被實例化的時候,他們才存在.但是類的靜態數據成員一旦被聲明,無論類是否被實例化,它都已存在.可以這麼說,類的靜態數據成員是一種特殊的全局變量.

所以該類的size爲:32位系統上,是8個字節,字節對齊,方便尋址操作(當CPU試圖讀取的數值沒有正確的對齊時,CPU可以執行兩種操作之一:產生一個異常條件;執行多次對齊的內存訪問,以便讀取完整的未對齊數據,若多次執行內存訪問,應用程序的運行速度就會慢)

6、用過動態指針嗎

7、熟練STL嗎

8、STL中有什麼類(STL中有向量類)

1)vector: 內部實現是數組,一段連續的內存。

2)list, 內部實現是雙鏈表

3)deque 內部實現是內存塊的鏈表。

4)string: 連續的內存

5)set,map: 紅黑樹(平衡二叉樹的一種)

6)hash_map, hash_set 用哈希表(散列表)來實現。

7)stack: 用vector或者是deque來實現

8)queue,用deque實現

9、由於用的是Pure C,把純虛函數和虛函數弄混了,vtable答了上來,普通的繼承多態啥的根本不問,問我有沒有用過 C++和C交叉編譯(沒用過)、STL庫(我沒用過), 圖形學的一些知識(不會)我當時真是尷尬萬分…不過我說我用Lambda表達式,Java是1.8 的特性,C++裏面是C++11的特性,面試官就問我Lambda的用法和好處,然後問我其他C++11的新特性有沒有用過(沒…)

10、const的作用,宏定義與const的區別

11、定義一個class,編譯器的內存分配

1)類的大小爲類的非靜態成員數據的類型大小之和,也 就是說靜態成員數據不作考慮。

2)普通成員函數與sizeof無關。

3)虛函數由於要維護在虛函數表,所以要佔據一個指針大小,也就是4字節。

4)類的總大小也遵守類似class字節對齊的,調整規則。

12、函數重載,模板template,用法和區別

13、多態、虛函數、智能指針

14、指針與引用的區別

15、還有一些C++庫函數的實現。

16、inline關鍵字是做什麼用的?inline關鍵字在什麼情況下會展開失敗?

inline類似於宏替換,使用函數體替換調用處的函數名,省去了調用函數的開銷,增快了代碼的執行效率。但是又不是宏替換,inline函數是真正的函數,編譯器會考慮語義。

函數體內代碼長度過大,包含複雜的結構控制語句(while,switch),包含內聯函數本身,含有遞歸均會導致展開失敗。

17、sizeof一個空類是多大?爲什麼?編譯器爲什麼這麼做?如果添加一個構造函數和析構函數呢?

1個字節,任何一個實例在內存中都佔有一定的空間,也就有一個獨一無二的地址,爲了達到這個目的,編譯器往往會給一個空類隱含的加一個字節,這樣空類在實例化後在內存得到了獨一無二的地址

調用構造函數和析構函數只需要知道函數的地址即可,而這些函數的地址只與類型相關,而與類型的實例無關,編譯器也不會因爲這兩個函數而在實例內添加任何額外信息

18、在這個類中添加一個virtual函數後再sizeof,這時是多大?爲什麼?

C++編譯器一旦發現一個類型有虛函數,就會爲該類型生成虛函數表,並在該類型的每一個實例中添加一個指向虛函數表的指針,在32位系統中,一個指針佔4個字節,因此sizeof得到4個字節

19、將這個類再virtual繼承一個其它的空類,這是多大?爲什麼?

12個字節,這個類本身大小爲4個字節,空類的大小爲1個字節,加上虛基類偏移量表指針4個指針,又因爲要指針對齊(4個字節),故一起12個字節(虛基類偏移量表不佔用類的存儲空間,表中記錄了虛基類與本類的偏移地址;通過偏移地址,這樣就找到了虛基類成員)

20、類有哪幾種權限,分別說明?

private,protected,public

21、class A :class B{},A是私有繼承還是? 私有繼承是做什麼用的?

默認是私有繼承,私有繼承後,基類所有成員在派生類中爲private成員。私有基類的public成員和protected成員在私有派生類中的訪問屬性相當於派生類中的私有成員,即派生類的成員函數能訪問它們,而在派生類外不能訪問它們。私有基類的私有成員在派生類中稱爲不可訪問的成員,只有基類的成員函數可以引用它們。

22、子類的虛函數中能不能調用父類的虛函數,爲什麼?

23、有純虛函數的類能不能實例化?

不能,有純虛函數的類是抽象類,只能被繼承,不能實例化。包含純虛函是的類派生出來的類都必須重寫這個純虛函數

24、C++多態有哪幾種?

靜態多態(函數重載和運算符重載),是在編譯的時候,就確定調用函數的類型;動態多態(虛函數實現),在運行的時候,才能確定調用的是哪個函數,動態綁定。運行基類指針指向派生類的對象,並調用派生類的函數。

a.應用形式上:靜多態是發散式的,讓相同的實現代碼應用於不同的場合。動多態是收斂式的,讓不同的實現代碼應用於相同的場合。

b.思維方式上:靜多態是泛型式編程風格,它看重的是算法的普適性;動多態是對象式編程風格,它看重的是接口和實現的分離度。

25、C++是怎麼實現動態多態的?

虛函數表和指向虛函數表的vptr指針。這個需要注意vptr指針的分佈初始化問題,是在構造函數之後,初始化列表和函數體之前完成的。

26、對象中的VPTR指針什麼時候被初始化?

Vptr指針初始化的過程:

a.對象在創建的時,由編譯器對VPTR指針進行初始化

b.只有當對象的構造完全結束後VPTR的指向才最終確定

c.父類對象的VPTR指向父類虛函數表

d.子類對象的VPTR指向子類虛函數表

當定義一個子類對象的時候比較麻煩,因爲構造子類對象的時候會首先調用父類的構造函數然後再調用子類的構造函數。當調用父類的構造函數的時候,此時會創建Vptr指針(也可以認爲Vptr指針是屬於父類的成員,所以在子類中重寫虛函數的時候virtual關鍵字可以省略,因爲編譯器會識別父類有虛函數,然後就會生成Vptr指針變量),該指針會指向父類的虛函數表;然後再調用子類的構造函數,此時Vptr又被賦值指向子類的虛函數表。

(執行父類的構造函數的時候Vptr指針指向的是父類的虛函數表,所以只能執行父類的虛函數)

上面的過程是Vptr指針初始化的過程。

這是因爲這個原因,在構造函數中調用虛函數不能實現多態。

27、簡要說說C++的靜態多態?

函數重載和運算符重載,見上上題。

28、C++編譯後的函數符號和C語言編譯後的函數符號有哪些區別?爲什麼

C++語言支持函數重載,C語言不支持函數重載。函數被C++編譯後在庫中的名字與C語言的不同。假設某個函數的原型爲void func(int x,int y)。該函數被C編譯器編譯後在庫中的名字爲_foo,而C++編譯器則會產生像_foo_int_int之類的名字。 C++中提供了C連接交換指定符號 extern “C” 解決名字匹配問題。

29、C++智能指針有哪些?auto_ptr和share_ptr有什麼區別?他們有什麼作用?

STL一共給我們提供了四種智能指針:auto_ptr , unique_ptr , shared_ptr和weak_ptr

auto_ptr的初衷是用來實現智能指針的,實現內存的自動回收。那麼如何實現智能的呢?智能指針最基本的概念是引用計數,也就是智能指針內部有一個計數器,記錄了當前內存資源到底有多少指針在引用(可以引用這個資源),當新增加一個可以訪問這個資源的引用時,計數器會加1,反之會減去1,當計數器爲0時,智能指針會自動釋放它所管理的資源。手動申請,自動釋放,就是智能的體現。

30、有序vector和list二分查找的時間複雜度分別是多少?

vector的二分相當於數組的二分,時間複雜度是O(logn),list沒辦法二分,只能每次從頭到尾找,時間複雜度爲O(n)。

31、vector自動擴容是按什麼大小進行的?

缺省的情況下vector的擴展機制是按2倍大小進行擴展的。在整個大小擴展的過程中,主要的步驟是:a.爲需要的新容量分配足夠的內存;b.將元素從原來的內存拷貝到新內存中去;c.銷燬原來內存中的元素;d.歸還原來的內存。

32、構造函數可以調用虛函數嗎?語法上通過嗎?語義上可以通過嗎?

語法可以通過,但是語義不對。

總結來說:基類部分在派生類部分之前被構造,當基類構造函數執行時派生類中的數據成員還沒被初始化。如果基類構造函數中的虛函數調用被解析成調用派生類的虛函數,而派生類的虛函數中又訪問到未初始化的派生類數據,將導致程序出現一些未定義行爲和bug,因此c++不讓你走這條路。
33、析構函數可以拋出異常嗎?爲什麼不能拋出異常?除了資源泄露,還有其他需考慮的因素嗎?

1)如果析構函數拋出異常,則異常點之後的程序不會執行,如果析構函數在異常點之後執行了某些必要的動作比如釋放某些資源,則這些動作不會執行,會造成諸如資源泄漏的問題。

2)通常異常發生時,c++的機制會調用已經構造對象的析構函數來釋放資源,此時若析構函數本身也拋出異常,則前一個異常尚未處理,又有新的異常,會造成程序崩潰的問題。
34、c++中類型轉換機制?各適用什麼環境?dynamic_cast轉換失敗時,會出現什麼情況?(對指針,返回NULL.對引用,拋出bad_cast異常)

對指針進行dynamic_cast,失敗返回null,成功返回正常cast後的對象指針;

對引用進行dynamic_cast,失敗拋出一個異常,成功返回正常cast後的對象引用。

35、拷貝構造函數作用及用途?什麼時候需要自定義拷貝構造函數?

  1. 一個對象以值傳遞的方式傳入函數體; 2) 一個對象以值傳遞的方式從函數返回; 3) 一個對象需要通過另外一個對象進行初始化;

36、c++裏面的虛函數的原理和實現

37、平時開發當中多態用的多麼?

38、 多態的開銷有多大?

39、菱形繼承的虛函數的開銷說一下

40、malloc是如何實現的?

malloc基本的實現原理就是維護一個內存空閒鏈表,當申請內存空間時,搜索內存空閒鏈表,找到適配的空閒內存空間,然後將空間分割成兩個內存塊,一個變成分配塊,一個變成新的空閒塊。如果沒有搜索到,那麼就會用sbrk()才推進brk指針來申請內存空間。

41、如果物理內存是2G 如果mallco 4G可以麼?會有什麼問題?

malloc的實現與物理內存自然是無關的,分配到的內存只是虛擬內存,而且只是虛擬內存的頁號,代表這塊空間進程可以用,實際上還沒有分配到實際的物理頁面。

42、能說一下STL幾種容器麼說一下這些容器的對比

43、vector和list的使用場景區別

1 如果你需要高效的隨即存取,而不在乎插入和刪除的效率,使用vector

2 如果你需要大量的插入和刪除,而不關心隨即存取,則應使用list

3 如果你需要隨即存取,而且關心兩端數據的插入和刪除,則應使用deque

44、C++析構和構造的順序,爲什麼析構函數最好是虛函數

1)構造函數順序:先基類、再數據成員中是類對象的構造函數、最後派生類構造函數的函數體

2)析構函數順序:與構造函數相反

3)析構函數最好是虛函數:若派生類有一個指向動態內存分配的數據成員,而又將基類的指針指向派生類對象,同時基類的析構函數又不是虛函數的話,編譯器就實施靜態綁定,釋放基類指針所指對象的空間時候只執行基類的析構函數,不執行派生類的析構函數,那派生類動態分配的數據成員所申請的空間就不能被釋放,這就造成了內存泄漏。

45、虛函數和虛函數表

1)虛函數:聲明成員函數爲虛函數以後,就可以實現動態綁定,也就是基類指針可以指向派生類對象,實現相同函數名的派生類的特定行爲

2)虛函數表:就是用來運行時查詢,幫助系統將某一函數名綁定到虛成員函數表中特定入口地址

46、智能指針瞭解麼,全部都講一下怎麼用,會出現什麼問題

47、知道c++11新特性嗎,知道智能指針嗎,知道智能指針實現嗎,來你給我馬上實現一個智能指針【要求寫出成員和構造函數析構函數】

48、C 簡單問了下static,然後就轉戰別的基礎了。

數據結構和算法:

  1. 向量和隊列有什麼區別
    

1)向量:能高效的進行隨機存取,時間複雜度爲o(1); 在進行插入和刪除操作時,會造成內存塊的拷貝,時間複雜度爲o(n)。

2)隊列:能高效的進行隨機存取,時間複雜度爲o(1);在內部方便的進行頭尾部的插入和刪除操作,時間複雜度爲o(n)。

  1. 向量和隊列的實現原理是什麼(當時我說可以用鏈表以及數組,面試官就問我鏈表如何實現向量的隨機訪問,然後我就意識到鏈表不能實現向量了,因爲不能實現隨機訪問,這個問題我答地比較糟糕)
    

1)向量: vector和數組類似,擁有一段連續的內存空間,並且起始地址不變。因此能高效的進行隨機存取,時間複雜度爲o(1);但因爲內存空間是連續的,所以在進行插入和刪除操作時,會造成內存塊的拷貝,時間複雜度爲o(n)。另外,當數組中內存空間不夠時,會重新申請一塊內存空間並進行內存拷貝。

2)隊列:deque的元素數據採用分塊的線性結構進行存儲,deque分成若干線性存儲塊,稱爲deque塊。塊的大小一般爲512個字節,元素的數據類型所佔用的字節數,決定了每個deque塊可容納的元素個數。

所有的deque塊使用一個Map塊進行管理,每個Map數據項記錄各個deque塊的首地址。Map是deque的中心部件,將先於deque塊,依照deque元素的個數計算出deque塊數,作爲Map塊的數據項數,創建出Map塊。以後,每創建一個deque塊,都將deque塊的首地址存入Map的相應數據項中。

  1. 什麼是優先隊列(當時我回答說優先隊列可以用堆實現,面試官就問了第4個問題)

優先隊列隊首元素一定是當前隊列中優先級最高的一個

優先隊列可以用堆實現

  1. 堆是用最大堆還是最小堆實現優先隊列,爲什麼?

默認是用最大堆實現優先隊列(priority_queue<int, vector, less >默認衍生自vector,數字大的優先級越高)

  1. 如果返回堆中最大的元素,要怎麼做?

取堆頂元素,如果要取完後要刪除,則把最後一個元素移至第一個元素,並將size自減1,然後從堆頂元素自上向下進行整堆

  1. 如果堆中某元素的序號是5,那他兩個自孩子的序號分別是多少?

10、11

  1. 說一下快排的時間複雜度

平均複雜度O(nlogn),最壞情況下O(n^2),最好情況下O(nlogn)

  1. 什麼樣的情況是快排的最壞情況,舉個例子

元素均有序是快排的最壞情況

  1. 如何解決快排的的最壞情況(我說的隨機打亂)

選擇劃分元的時候隨機進行選擇

  1. 說說隨機打亂的具體實現

  2. 給一系列整形數,其中除了一個數只有一個之外,其他數都有兩個,請設計算法找到只有一個的那個數(我開始回答計數排序,後來他又問我有沒有別的,我就說全放到set裏,可以排除所有那些添加進set裏讓set元素個數不增加的所有元素,剩下的就是要找到那個數,不知道對不對,感覺應該還有更好的方法)

從頭到尾將這n個數異或一遍,得到的數即爲要找的那個數。

一個整型數組裏除了兩個數字之外,其他的數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。

思路:1)如果把數組中的所有數字都依次異或一遍,則可以消掉成對出現的數字,那麼還有兩個數字是單一的,肯定也不同,那麼最終異或的結果肯定不是0。表示在二進制中肯定有一位是1,那麼兩個不同的數字,一定有一個在該位爲1,另一個在該位爲0。如果將整個數組按照該位是否爲1分爲兩部分,那麼這兩部分各自包含一個單一數字。

12、一串數只出現一次,給定一個輸入,讓返回所有數對,數對中的兩個數之和等於輸入。我考慮了1min回答兩種思路,第一種快排後兩邊向中間遍歷,大了右邊-1,小了左邊+1, O(nlogn)的複雜度,然後就是說不排序就和冒泡一樣遍歷,O(n2)的複雜度。

13、問了遊戲排行榜的數據結構應該怎麼設計

我先說只取前幾名的話堆排序,如果不是前幾名的話要看查詢多還是改動多,然後說目前只能想到BST。然後又聊會說可以先分組,再排序,就和Query 的select一樣,最後join就可以了。後來上網上看了下,網上答案是桶排序和紅黑樹,雖然自己回答的不精確,不過看來思路還是差不多的。

14、要求我寫快排,基本上就是說下思路就可以了,沒什麼難度。

15、數據庫B+樹(不瞭解B+樹,扯了扯紅黑樹)

16、給你一個表(數據很大),有用戶名和數據,如何快速檢索某條數據

對索引排序+二分查找,對索引建表,在新表裏可以用hash、分區等操作(B+樹)

17、對數據壓縮熟悉不(不熟悉,實習的時候直接調用導師接口,面試官就沒接着問)

18、只有大小寫英文字母的文本文檔,數據量很大,如何壓縮表示(因爲我說了不懂數據壓縮,就出了一個相關問題)

哈夫曼編碼+詳細實現過程,還可以對重複出現的字母加下標,對重複出現的子串編碼(後面兩種方法針對可能的具體問題,主要還是哈夫曼編碼

19、遊戲中,地圖很大,英雄的技能釋放半徑和英雄的座標已知,如何知道每個英雄的技能範圍內的對手(維護一個以技能半徑爲閾值的大根堆,範圍內的入堆,範圍外的出堆,面試官不太滿意,說不夠全面,說只給出了一個環節的方法)

20、一個數組,把所有奇數排在偶數前面,且保證奇數和奇數相對位置不變,偶數和偶數相對位置不變

再用一個數組

21、檢測npc之間的距離

22、Hash的知識。(hash的構建與衝突處理。)

1)構建:a直接定址法b除留餘數法c數字分析法d平方取中法e摺疊法

2)衝突處理:a.開放定址法(線性探測、平方探測、再散列)b拉鍊法

23、hash衝突,怎麼解決(散鏈表,雙重hash,等等)

24、圖的搜索有哪幾種方式?廣搜要怎麼做?需要什麼額外空間嗎

DFS和BFS,其中BFS需要開闢隊列內存,DFS需要棧。

25、給定一個迷宮,部分座標是無法通過的,求某兩點間最短路徑?(廣搜+並查集)

從起點到終點的最短路徑其實就是一個建立隊列的過程:

1)從起點開始,先將其加入隊列,設置距離爲0;

2)從隊列首端取出位置,將從這個位置能夠到達的位置加入隊列,並且讓這些位置的距離爲上一個位置的距離加上1;

3)循環2直到將終點添加到隊列中,這說明我們已經找到了路徑;

注意到在這個過程中,每次處理的位置所對應的距離是嚴格遞增的,因此一旦找到終點,當時的距離就是最短距離;

26、簡述Dijkstra算法的過程,描述一下A Star算法

A*算法:

1)把起點加入 open list 。

2)重複如下過程:

a.遍歷 open list ,查找 F 值最小的節點,把它作爲當前要處理的節點。

b.把這個節點移到 close list 。

c.對當前方格的 8 個相鄰方格的每一個方格?

◆ 如果它是不可抵達的或者它在 close list 中,忽略它。否則,做如下操作。

◆ 如果它不在 open list 中,把它加入 open list ,並且把當前方格設置爲它的父親,記錄該方格的 F , G 和 H 值。

◆ 如果它已經在 open list 中,檢查這條路徑 ( 即經由當前方格到達它那裏 ) 是否更好,用 G 值作參考。更小的 G 值表示這是更好的路徑。如果是這樣,把它的父親設置爲當前方格,並重新計算它的 G 和 F 值。如果你的 open list 是按 F 值排序的話,改變後你可能需要重新排序。

d.停止,當你

◆ 把終點加入到了 open list 中,此時路徑已經找到了,或者

◆ 查找終點失敗,並且 open list 是空的,此時沒有路徑。

3)保存路徑。從終點開始,每個方格沿着父節點移動直至起點,這就是你的路徑。opened_table:採用優先隊列實現的小頂堆,用於存放待擴展結點,同時利用F值作爲排序指標;

closed_table:採用set(紅黑樹)實現,便於快速查找格是否存在於closed_table中;

27、找出一個無序數組中大小後K個數據?

類似於快速排序的思想,隨機選取一個元素,把所有小於等於這個元素的數據移到左邊,所有大於這個元素的數據移動到右邊。

如果這個元素成了第K個數,直接返回這個數。如果左邊的個數大於K,不管右邊的數了,在左邊重複上面的過程。如果左邊的個數等於T<K,不管左邊的數了,重複上面的過程,只是K=K-T-1。平均情況下,第一次劃分的時間複雜度是O(N),第二次就是O(N/2),總共是O(n+n/2+n/4+…)=O(n)

28、Set的底層實現是什麼?紅黑樹是做什麼用的?額外開銷是多少?

set的底層實現是紅黑樹。紅黑樹是一種平衡二叉查找樹。a結點是紅色或黑色。b根節點是黑色,每個葉子結點都是黑色,c每個紅色結點的兩個孩子結點都是黑色。d從每個葉子到根的所有路徑上不能有兩個連續的紅色結點。e從任一結點到其每個葉子的所有路徑都包含相同數目的黑色結點。

紅黑樹和AVL樹一樣都對插入時間和刪除時間以及查找時間提供了最好可能的最壞保障。時間複雜度是O(logn),需要額外的空間也是O(logn)

29、給定1000億個數據,要找出其中最大的一個值,內存只有1G?

大文件變小文件,然後每個文件裏hash_map統計最大的值,然後再歸併排序。

30、給定1000億個數據,裏邊有的數據有重複,要求設計一個算法刪除重複數據?要求儘量快。

先取模分成小文件,然後每個文件使用hash_map或者trie樹。

31、洗牌算法,如何證明算法是隨機的

需要隨機置亂的n個元素的數組array:
for( i =n-1;i>=1;i–)
(0 =< j <= i)
交換array[i]和array[j]
end
32、100萬個32位整數,如何最快找到中位數。能保證每個數是唯一的,如何實現O(N)算法?
這道題是編程之美或編程珠璣上的。
這道題使用位圖,需要空間複雜度是512M。

33、hashmap和treemap

34、linux rwxrwxrwx文件拷貝

35、版本控制,github指令

38、n個球要分成m堆每個堆不能爲空,有多少種分發

39、紅黑樹實現

每次先按BST方法插入數據,每次都將插入節點變紅,然後再自下而上調整節點顏色,使其滿足紅黑樹性質

40、堆了解麼?怎麼刪除元素?怎麼插入元素?

刪除:刪除頂元素,將最後一個數據移到第一個,size自減1,然後從第一個節點自上而下進行整堆

41、圖的遍歷介紹一下

DFS、BFS

42、你看,現在我百度一個ip地址可以查到那個IP的實際省市顯地址,現在我給你每個縣/村的ip段,要求你實現一下輸入查找功能。【大概是問數據庫,我完全不會,瞎答了】

43.你看看我們陰陽師手遊裏面,可以搖繩子【手機給我展示了一下,真的是像繩子不是單擺】,現在我要你來實現一個繩子,要可以搖那種。

44.你看我打開一個谷歌網頁,輸入一些單詞,他下面給我提示了一些可能我需要的選項,比如我輸入一個tail出現了balabala。你現在給我實現一下這個功能,會用什麼數據結構什麼算法呢。

1)Trie是一顆存儲多個字符串的樹。相鄰節點間的邊代表一個字符,這樣樹的每條分支代表一則子串,而樹的葉節點則代表完整的字符串。和普通樹不同的地方是,相同的字符串前綴共享同一條分支。

2)hashmap統計: 先對這批海量數據預處理。具體方法是:維護一個Key爲Query字串,Value爲該Query出現次數的HashTable,即hash_map(Query,Value),每次讀取一個Query,如果該字串不在Table中,那麼加入該字串,並且將Value值設爲1;如果該字串在Table中,那麼將該字串的計數加一即可,最終在O(N)的時間複雜度內用Hash表完成了統計。

3)堆排序:藉助堆這個數據結構,找出Top K,時間複雜度爲N‘logK。即藉助堆結構,我們可以在log量級的時間內查找和調整/移動。因此,維護一個K(該題目中是10)大小的小根堆,然後遍歷300萬的Query,分別和根元素進行對比。所以,我們最終的時間複雜度是:O(N) + N’ * O(logK),(N爲1000萬,N’爲300萬)。

45、.你說你打過acm比賽,acm比賽主要是用算法數據結構吧,你說說你覺得你遇到過的最巧妙的一個數據結構或者算法題是什麼。【我回答了rope,正好面試官沒聽過,然後給他瞎bb了一番】

46、給你11位電話號碼,讓你通過電話找名字
47、 給你多個ipv4的區間,每個區間屬於一個城市,如0.0.0.1-1.1.1.1屬於北京,給你一個ipv4地址,你要輸出所屬的城市,如1.1.1.0輸出北京。區間有可能會重疊,如果詢問的ipv4屬於多個城市,則輸出所有所屬城市。
48、平面最近點對。

計算機網絡:

1、TCP和UDP的區別

2、tcp的可靠性怎麼保證(三次握手、四次揮手、確認序列號)

1)檢驗和

典型的有CRC校驗法。在要傳輸的k比特數據D後添加(n-k)比特冗餘位。

2)序列號

通過序列號,可以去重、超時重傳、數據有序到達。

3)確認應答

可以確認ACK之前的數據肯定到達了,保證了可靠性。

4)超時重傳

發送的數據有可能因爲網絡擁堵,沒有及時到達,發送端沒有收到確認,超過計時器,就會進行重發。

發送端有可能收到許多重複確認,累計到一定次數,TCP認爲網絡或者接收端出現異常,重新發送丟失的數據包。

5)連接管理

通過三次握手四次揮手,也可提高可靠性。

6)流量控制

接收端處理數據的速度是有限的,如果發送端發的太快,接收端緩衝區容易滿,會造成丟包以及引起丟包重傳。

7)擁塞控制

發送數據的時候,不能剛開始就發送大量的數據,所以在不清楚網絡狀況的情況下,不能貿然發送大量的數據,有可能加重網絡負擔,TCP會使用慢啓動機制,探探路,所以剛開始的時候,將擁塞窗口設爲1,以後是指數增長,當達到閾值的時候,按照線性增長,到達擁塞窗口的最大值後,擁塞窗口重回1。

3、TCP和UDP的區別?分別舉例它們的上層協議?

TCP是基於連接的,可靠的,偏向於傳輸大量數據,速度慢,http,ftp,smtp,telnet使用了tcp;UDP是無連接的,不可靠的,偏向於傳輸少量數據,速度快,dns,tftp,rip,snmp,rtp,nfs等使用了udp。

4、TCP4層網絡層次、3次握手

5、TCP和UDP的知識點。TCP講三次握手和四次揮手。

(1)三次握手

第一次握手:主機A發送同步報文段(SYN)請求建立連接。

第二次握手:主機B聽到連接請求,就將該連接放入內核等待隊列當中,並向主機A發送針對SYN的確認ACK,同時主機B也發送自己的請求建立連接(SYN)。

第三次握手:主機A針對主機BSYN的確認應答ACK。

(2)四次揮手

第一次揮手:當主機A發送數據完畢後,發送FIN結束報文段。

第二次揮手:主機B收到FIN報文段後,向主機A發送一個確認序號ACK(爲了防止在這段時間內,對方重傳FIN報文段)。

第三次揮手:主機B準備關閉連接,向主機A發送一個FIN結束報文段。

第四次揮手:主機A收到FIN結束報文段後,進入TIME_WAIT狀態。並向主機B發送一個ACK表示連接徹底釋放。(如果客戶端的確認應答丟失,算上這個丟失報文的時間,再加上服務端重傳FIN的時間(重傳後客戶端重新啓動2MSL計時器),2MSL的時間足夠使客戶端收到重傳的FIN報文段。所以客戶端不能立即進入CLOSED狀態。)

6、網絡,dns、https和http,非對稱加密和加密比較一下

操作系統:

  1. 進程和線程的區別
    

1)進程是系統分配資源(CPU以外)的基本單位

2)線程是被系統獨立調度的基本單位(CPU的分配單位)

  1. 線程間怎麼共享資源

線程直接讀/寫進程數據段(如全局變量)來通信

3、線程使用共享資源會出現什麼問題?需要怎麼做?

操作系統具有異步性,如果對共享資源的訪問不加以約制,一些具有相互制約、相互合作的線程得到的最終結果可能是錯的

設置臨界區,對臨界區資源設置信號量,進行同步和互斥

4、多線程同步方式

1)軟件實現方法(皮特森算法(雙標誌和單標誌):

Pi: flag[i]=true; turn=j; while(flag[j]&&turn==j); )

2)硬件實現方法(中斷屏蔽方法(屏蔽中斷/關中斷)、硬件指令方法(原子操作))

3)信號量(PV操作)

4)管程(解決臨界區分散所帶來的管理和控制問題,包括共享結構數據說明、一組操作、設置共享數據初始值語句)

5、如何進行線程同步?在Windows下舉例?分用戶模式下同步和內核模式下同步 討論?

用戶模式下的方法有:原子操作(例如一個單一的全局變量),臨界區。

內核模式下的方法有:事件,信號量,互斥量。

6、同步機制應遵循的準則

1)空閒讓進

2)忙則等待

3)有限等待

4)讓權等待

  1. 進程間通信有哪些算法or多進程通信方式

1)共享存儲(PV操作對共享空間讀寫進行同步互斥)

2)消息傳遞(1直接通信方式,掛在接收進程的消息緩衝隊列上2間接通信方式,即信箱)

3)管道通信(管道是連接讀進程和寫進程通信的共享文件,限制管道大小,緩衝區允許一邊寫入另一邊讀出,管道通信是半雙工通信)

4)客戶機-服務器系統(包括:套接字(socket),遠程過程調用和遠程方法調用)

8、進程間通信有哪幾種方式?在特定環境(比如兩個程序需要共享一個文本)下哪種效率最高?Windows下如何進行內存共享?

無名管道,有名管道,信號量,信號,高級管道,消息隊列,共享內存,sokect等,共享內存的效率最高,因爲它可以直接讀寫內存,而不需要任何的數據拷貝。windows下主要通過映射機制實現的。共享內存的方式原理就是將一份物理內存映射到不同進程各自的虛擬地址空間中,這樣每個進程都可以讀取同一份數據,從而實現進程通信。因爲是通過內存操作實現通信,因此是一種最高效的數據交換方法。

9、進程和線程的區別、進程如何調度(扯了進程維護線程池,臨界區、事務、信號量、信號)

1)先來先服務調度算法

2)短作業優先調度算法

3)優先級調度算法

4)高響應比優先調度算法(響應比=(等待時間+要求服務時間)/要求服務時間)

5)時間片輪轉調度算法

6)多級反饋隊列調度算法

10、 簡單說一下進程間切換髮生的事情

1)保存處理機上下文,包括程序計數器和其他寄存器

2)更新PCB信息

3)把PCB移入相應的隊列,如就緒、阻塞隊列

4)選擇另一個進程執行,並更新其PCB

5)更新內存管理的數據結構

6)恢復處理機上下文

7、進程安全如何保證(扯了進程的數據同步和鎖的實現

安全狀態:能找到一個分配資源的序列讓所有進程都順利完成

11、進程在什麼情況下會互鎖

多個進程同時佔有對方需要的資源而同時請求對方的資源,而它們在得到請求之前不會釋放所佔有的資源

(1) 系統資源的競爭

(2) 進程推進順序非法(信號量使用不當,A等B的消息,B等A的消息)

12、互鎖怎麼解決

1)預防死鎖(設置某些限制條件,破壞死鎖四個必要條件之一)

2)避免死鎖(動態分配資源過程中,用某種方法防止系統進入不安全狀態)

3)死鎖檢測及解除(剝奪資源、撤銷進程、進程回退)

13、線程死鎖的幾個條件是什麼?

(1)互斥條件:指線程對所分配的資源進行排他性使用,即在一段時間內某資源只由一個線程佔用。

(2)請求和保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。

(3)不可剝奪條件:進程已獲得的資源,在未使用之前,不能強行剝奪。

(4)循環等待條件:指在發生死鎖時,必然存在一個進程資源循環等待鏈,鏈中每一個進程已獲得的資源同時被鏈中下一個進程所請求

14、給定兩個線程,A,B兩個鎖,舉個造成死鎖的例子?

程序中使用多個互斥量時,如果允許一個線程一直佔有第一個互斥量,並且在試圖鎖住第二個互斥量時處於阻塞狀態,但是擁有第二個互斥量的線程也在試圖鎖住第一個互斥量,這時就會發生死鎖。因爲兩個線程都在相互請求另一個線程擁有的資源,所以這兩個線程都無法向前運行,於是就產生死鎖。如果所有線程總是在對互斥量B加鎖之前鎖住互斥量A,那麼使用這兩個互斥量不會產生死鎖

15、可能面試官瞭解到我是系統方向,所以讓我解釋一個Hello World程序從C到最終運行起來的過程。

基本上就是先編譯,得到Symbol, 鏈接器Resolve Symbol, Printf屬於動態鏈接庫裏面的內容 所以涉及到GOT和PLT表,然後操作系統開新的進程,Load二進制文件,將控制流跳到程序入口Main函數執行等等。面試官聽到動態鏈接庫,又問了我一些動態鏈接庫的內容和進程的地址空間和 內存的映射(基本上就是回答新進程的地址空間只是映射了Kernel代碼,不用創建新的,動態鏈接庫也是映射過來可以執行balabala

16、程序有哪幾種鏈接方式?分別說明區別?哪種效率高?如果一個動態庫沒有.lib和頭文件,要怎麼使用裏面的函數?

1)靜態鏈接:在程序運行之前,先將各個目標模塊及它們所需的庫函數,鏈接成一個完整的裝配模塊,以後不再拆開。我們把這種事先進行鏈接的方式稱爲靜態鏈接方式。

2)裝入時動態鏈接:將用戶源程序編譯後所得到的一組目標模塊,在裝入內存時,採用邊裝入邊鏈接的鏈接方式。

3)運行時動態鏈接:這是指對某些目標模塊的鏈接,是在程序執行中需要該目標模塊時,纔對它進行的鏈接。(便於修改和更新,便於實現對目標模塊的共享)

第三種方式效率較高。還可以節省大量的內存空間。

17、cache的作用和實現機制,講了LRU、FIFO和LEU,詳細介紹了LRU的三種實現

Cache作用:調節CPU與主存讀取速度不一致的矛盾

Cache實現機制:將Cache和主存都分成若干大小相等的塊,Cache中存儲主存中最活躍的若干塊副本

頁面調度算法:

最佳置換算法OPT:選擇以後不用的頁面

最近最久未使用LRU:選擇最近最久未使用的頁面(堆棧類算法,需要寄存器和棧的硬件支持)

最不經常使用LFU:將一段時間內訪問次數最少的頁面換出

先進先出FIFO:選擇最先裝入內存的頁面(基於隊列)

時鐘置換算法CLOCK(NRU):選擇最近未用的頁面

改進的時鐘算法:考慮頁面修改問題

18、問了操作系統的調度,頁表之類的問題,然後講了講緩存算法。(LRU要求講一下怎麼實現)

LRU的實現:

1)用一個數組來存儲數據,給每一個數據項標記一個訪問時間戳,每次插入新數據項的時候,先把數組中存在的數據項的時間戳自增,並將新數據項的時間戳置爲0並插入到數組中。每次訪問數組中的數據項的時候,將被訪問的數據項的時間戳置爲0。當數組空間已滿時,將時間戳最大的數據項淘汰。

2)利用一個鏈表來實現,每次新插入數據的時候將新數據插到鏈表的頭部;每次緩存命中(即數據被訪問),則將數據移到鏈表頭部;那麼當鏈表滿的時候,就將鏈表尾部的數據丟棄。

3)利用鏈表和hashmap。當需要插入新的數據項的時候,如果新數據項在鏈表中存在(一般稱爲命中),則把該節點移到鏈表頭部,如果不存在,則新建一個節點,放到鏈表頭部,若緩存滿了,則把鏈表最後一個節點刪除即可。在訪問數據的時候,如果數據項在鏈表中存在,則把該節點移到鏈表頭部,否則返回-1。這樣一來在鏈表尾部的節點就是最近最久未訪問的數據項。

對於第一種方法,需要不停地維護數據項的訪問時間戳,另外,在插入數據、刪除數據以及訪問數據時,時間複雜度都是O(n)。對於第二種方法,鏈表在定位數據的時候時間複雜度爲O(n)。所以在一般使用第三種方式來是實現LRU算法。

19、基址變址尋址

1)相對尋址:PC的內容加上指令格式中的形式地址A而形成操作數的有效地址

2)基址尋址:將CPU中基址寄存器(BR)的內容加上指令格式中的形式地址A,而形成操作數的有效地址(基址寄存器內容由操作系統確定,內容不變,形式地址可變,用於分配存儲空間)

3)變址尋址:變址寄存器(IX)的內容加上指令格式中的形式地址A,而形成操作數的有效地址(變址寄存器內容可由用戶改變,形式地址A不變,用於數組)

20、堆和棧的區別?

(1)管理方式不同。棧由操作系統自動分配釋放,無需我們手動控制;堆的申請和釋放工作由程序員控制,容易產生內存泄漏;

(2)空間大小不同。每個進程擁有的棧的大小要遠遠小於堆的大小。理論上,程序員可申請的堆大小爲虛擬內存的大小,進程棧的大小64bits的Windows默認1MB

(3)生長方向不同。堆的生長方向向上,內存地址由低到高;棧的生長方向向下,內存地址由高到低。

(4)分配方式不同。堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是由操作系統完成的,比如局部變量的分配。動態分配由alloca函數進行分配,但是棧的動態分配和堆是不同的,他的動態分配是由操作系統進行釋放,無需我們手工實現。

(5)分配效率不同。棧由操作系統自動分配,會在硬件層級對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高。堆則是由C/C++提供的庫函數或運算符來完成申請與管理,實現機制較爲複雜,頻繁的內存申請容易產生內存碎片。顯然,堆的效率比棧要低得多。

(6)存放內容不同。棧存放的內容,函數返回地址、相關參數、局部變量和寄存器內容等。堆,一般情況堆頂使用一個字節的空間來存放堆的大小,而堆中具體存放內容是由程序員來填充的。

21、靜態變量是存放在數據段或者BSS段,是不入棧的

22、mmu虛擬內存映射(MMU負責虛擬地址映射爲物理地址)

虛擬內存是一些系統頁文件,存放在磁盤上,每個系統頁文件大小爲4K,物理內存也被分頁框,每個頁框大小也爲4K,這樣虛擬頁文件和物理內存頁就可以對應,實際上虛擬內存就是用於物理內存的臨時存放的磁盤空間。頁文件就是內存頁, 物理內存中每頁叫物理頁,磁盤上的頁文件叫虛擬頁,物理頁+虛擬頁就是系統所有使用的頁文件的總和。

23、Windows提供了3種方法來進行內存管理:

1)虛擬內存,最適合用來管理大型對象或者結構數組;

2)內存映射文件,最適合用來管理大型數據流(通常來自文件)以及在單個計算機上運行多個進程之間共享數據;

3)內存堆棧,最適合用來管理大量的小對象。

Windows操縱內存可以分兩個層面:物理內存和虛擬內存。,

24、虛擬內存組成部分:

1) 頁表機制

2) 中斷機構

3) 地址變換機構

4) 內存和外存

25、 epoll的文件描述符是否有拷貝過程?

26、 讓你設計一個方式實現內核和用戶態都沒有拷貝 如何實現?

項目相關

1、 後來面試官問了我研究生階段做的事情,問我有沒有接觸過網絡編程,因爲瞭解到我最近用python,就問我有沒有用過socket或者select這兩個庫

2、 聽完自我介紹後面試官就開始問我各種簡歷上的項目經歷,首先是研究生期間做的虛擬化部分,我做了簡單介紹。之後是大四的軟件測試和嵌入式開發,大三的LeapMotion和Android App開發, 之前接觸過的Kinect開發等等。瞭解完後就開始問我熟悉的編程語言(我說的Java & Pure C)。

瞭解到LeapMotion是做的有關一個遊戲的內容後就開始問我詳細內容,由於裏面用到了交互設備和圖形學的簡單知識,問我了不瞭解OpenGL。(我說的不瞭解,但是說了一些概念)。 之後聊到 遊戲開發引擎,我只說了我聽說過Unity和Unreal… 然後說的是我不瞭解,專業是系統方向。

3、 如果以前做過3D建模的項目,還會着重考察3D建模的知識。比如openGL、DirectX等。如果有Unity3D的經驗就更好了。

4、 Redis底層的數據結構、跳錶怎麼實現的?hash表怎麼實現的?怎麼rehash?是不是rehash一定要全部複製?什麼時候會觸發rehash?redis持久化介紹一下,epoll和select介紹一下

5、 系統設計題1:網絡分區的時候,a的朋友圈被b點讚了,怎麼同步這個消息

6、 系統設計題2:實現一個高精度的定時器(最後提示了linux內核實現了一個,有空可以看看)

7、 簡歷上的項目是python寫的web,然後問了一些python的知識,例如Django裏面的AbstractUser是怎麼對密碼進行加密的(項目裏用了這個),然後還有mysql一些注入的問題。

Misc(雜)

1、 問了我關於骨骼動畫的原理,因爲這個我簡歷有寫我在項目中提供做美術素材,這個部分應該跟每個小夥伴簡歷上具體內容相關。

2、 其中還問過我一些圖形學的知識,不過我不是很瞭解,所以只是介紹了一下景深、3D裏面攝像頭balabala,把當時大三LeapMotion項目的內容說了一點點…

3、 你用過哪些設計模式?

工廠方法和抽象工廠有哪些區別?

工廠方法模式屬於對象創建型模式,它定義一個用戶創建對象的接口,讓子類決定實例化哪一個類。工廠方法模式使一個類的實例化延遲到其子類。具體來說就是一個一個抽象產品類,派生出很多個具體產品類;同時,一個抽象工廠類,派生出多個具體工廠類。而每個具體工廠類只能創建一個具體產品類的實例。

抽象工廠模式也屬於對象創建型模式,它提供了一個創建一系列相關或相互依賴對象的接口,而無須制定它們具體的類。具體來說就是在多個抽象產品類中,每個抽象產品類可以派生出多個具體產品類。一個抽象工廠類,可以派生出多個具體工廠類。每個具體工廠類可以創建多個具體產品類的實例。

區別:工廠方法模式只有一個抽象產品類,而抽象工廠模式有多個。工廠方法模式的具體工廠類只能創建一個具體產品類的實例,而抽象工廠模式可以創建多個。

工廠方法:說白了就是一個方法,這個方法是創建具體的產品的,它要求所有的工廠都具有同一個簽名的方法,必要時重寫該方法;

抽象工廠:不能直接創建產品,只能創建工廠,即抽象工廠創建的產品是工廠。

提問

最後,面試官讓我問問題,我大概問了兩個:

  1. 遊戲行業有沒有程序與美術的雙棲人才?

  2. 實習做什麼樣的工作?(要看分到哪兩個組裏)

3、網易研發和策劃之間的協作和不同,研發是有自己的考覈機制,也有與策劃的反饋機制,所以可以影響遊戲的設計,不過影響不大。

4、問我這種方向差異比較大的要怎麼跟進,面試官也是介紹了那邊的培訓制度,不用擔心

騰訊:
C++方面的問題:

1、 然後問了struct和class的區別

(當時說了默認public和private的區別)但是面試官不是很滿意,可能應該有更深層次的說法,原諒我底層懂的真的不是很多。

C#中:class 是引用類型,structs是值類型,當你實例化一個class,它將創建在堆上。而你實例化一個struct,它將創建在棧上

2、 const的作用(只記得定義常量,定義常量指針,常量成員函數)

const定義時賦初值,以後只能使用,不能修改

3、指針和引用的區別

4、函數參數傳遞

1)值傳遞(passl-by-value)過程中,被調函數的形式參數作爲被調函數的局部變量處理,即在堆棧中開闢了內存空間以存放由主調函數放進來的實參的值,從而成爲了實參的一個副本。值傳遞的特點是被調函數對形式參數的任何操作都是作爲局部變量進行,不會影響主調函數的實參變量的值。

2)引用傳遞(pass-by-reference)過程中,被調函數的形式參數雖然也作爲局部變量在堆棧中開闢了內存空間,但是這時存放的是由主調函數放進來的實參變量的地址。被調函數對形參的任何操作都被處理成間接尋址,即通過堆棧中存放的地址訪問主調函數中的實參變量。正因爲如此,被調函數對形參做的任何操作都影響了主調函數中的實參變量

5、c++的多態性。

6、問了對inline的理解。(C++)

解決一些頻繁調用的小函數對棧內存重複開闢所帶來的消耗

7、能否將構造函數定義爲虛函數?

8、問了STL中map是利用何種結構和方法實現的。

紅黑樹

8、 C++的三大特性?

封閉性、繼承、多態

10、多態是什麼實現的?

11、指針和引用的區別?

12、const有什麼用法?

13、內存泄漏、數組越界問題、指針非法訪問問題?智能指針

14、C++初始化列表使用

15、stl容器可以放入智能指針嗎?auto_ptr不可以,其餘的可以

16、c++的多態,多態的實現,還有STL庫的一些實現

17、C++的構造函數和析構函數的執行順序?

18、結構體和類有什麼區別?

19、堆、棧一般放哪些數據?

數據結構和算法:

1、有100盞燈,從1~100編號,開始燈的狀態是亮的,然後按照1的倍數,2的倍數,3的倍數。。。一直到100的倍數翻轉,問你最後熄滅的是哪幾盞燈。當時應該仔細想好再寫代碼的,一開始思路略微麻煩了一些,其實類似素數篩那樣走一遍就可以了,大概nlogn。

2、然後還問了一個第k大的問題

3、問了在2D的環境下,有極大數量的物體(假設爲小球),如何優化使得系統開銷和性能最好?

4、假如有100個小球有碰撞的檢測(其中有一個爲用戶),如果需要統計小球的碰撞的次數總和,如果減少性能要求的前提下進行實現?

5、假如有100個小球有碰撞的檢測(每一個都是用戶),如果需要統計小球的碰撞的次數總和,如果減少性能要求的前提下進行實現?

6、問了一下我簡歷上的項目,看到我用了快排,於是問我這樣的算法快排是穩定的嗎?本來準備了快排的概念、實現方法,結果對方說:我不想問這種東西,因爲太基礎了,每個程序員都應該熟練掌握的。

7、你知道哪些設計模式?常用到的模式有哪些?單例模式+適配器模式

8、像紅黑樹、二叉樹啥的。我是連STL庫一塊問的,因爲STL庫的實現本身就用了很多數據結構;

9、談談對於數據結構的認識(因爲樓主都快把數據結構背下來了,洋洋灑灑說了半個小時)10、b樹b+樹b樹的區別(b+樹對於葉子結點有一個雙向鏈表,b樹+非葉子結點)

11、動態規劃(最短路徑算法)

12、談談排序和堆排序的應用

13、AVL樹和紅黑樹區別(一個通過旋轉實現完全平衡,一個通過定義根結點的顏色)

計算機網絡:

操作系統:

1、 問了線程和進程的區別

2、 問了對sleep的理解。(進程中的概念)

3、 進程和線程的區別?

4、 進程間通訊有幾中方式?

5、 死鎖是什麼?

6、 進程間通訊方式

7、 進程線程協程的區別

8、進程和線程的區別?

項目相關

1、,問了實現工程的代碼量是越多越好還是越少越好?

2、 開發遇到的問題:那些比較簡單,哪些比較需要花時間解決?

3、聊了聊做的項目的,談了談怎麼設計的。

Misc(雜)

1、 問了shader。

2、 MVC架構有什麼瞭解?

3、 前端有什麼瞭解?

4、 有木有做過GUI的開發?

5、 對於遊戲開發有什麼興趣?

6、 對於後臺開發的認識?

7、 對於AI這一塊有什麼興趣?

8、 對於信息安全計算有什麼瞭解?

9、 關於遊戲和Unity的內容

10、爲什麼ngui在VR裏不能使用

11、NGUI Altas原理

12、用c#實現一個商城的業務邏輯(因爲剛做過,還有印象)

13、在計算機圖形學中如何進行平移、旋轉、縮放?

14、有哪些光照模型?

15、如何判斷空間中的一點在一個球體內?

16、Unity3D腳本的生命週期?

17、Unity3D的內存管理?

18、Unity3D的UI如何渲染?

19、面向組件的好處?

20、用Opengl如何畫一個點,一條直線?

21、有哪些設計模式?

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