面試的職位是 C++開發工程師,主要聊的仍是C++。在過程當中自我感受面得還行,至少沒上次那麼蠢。前端
聊的內容主要集中在STL和線程安全、資源管理的層面。面試
慣例的,填完面試信息表並簡歷一塊兒上交,而後等面試官來客套完,就開始聊技術了。算法
注意,面試官的提問並不是原話,有修飾和腦補。編程
客套話什麼的就略了。後端
面試官:...行,那咱們就聊聊C++吧。你經常使用哪一個版本的C++?數組
我:我比較經常使用的是C++11。安全
C++版本這個問題面試裏應該很少見,不過做爲引入的話題還行,標準之神會瞑目的。數據結構
對於C++版本這個詞,很大機率上你們說的應該就是C++標準委員會WG21制定的C++標準了,最新版本的標準文檔是C++17定稿N4659,制定中的C++20標準文檔能夠訪問WG21/docs/papers/2018查閱。多線程
須要注意的是,若是答成了我用VC6之類的騷話,很大機率會留下很差的映像——或者對方也是忠實的VC6神教教徒的話,達成共識也說不定。架構
閒話少敘。
std::shared_ptr
面試官:說說
std::shared_ptr
是怎麼實現的?通常怎麼去使用它?
答:
shared_ptr
是經過引用計數實現的,它能夠做爲容器元素,在程序裏傳遞blabal.....並且shared_ptr
不是線程安全的,它不能跨線程傳遞,要額外作一層包裝blabla......
正巧最近有想寫一篇智能指針相關的博客,面試官的第一問就提到了。
說到智能指針,就必須提一下RAII了。
std::shared_ptr
和其餘智能指針類型都在<memory>
頭文件裏定義,主要的做用是實現自動化的資源管理,基於RAII的理念設計和實現。
RAII指的是獲取資源即初始化,英文全寫是Resource Acquisition Is Initialization,屬於一種面向對象編程語言中常見的慣用法。
它的思路是這樣子的:初始化即獲取資源,離開做用域就自動銷燬。
RAII解決的問題是,當異常發生時,如何確保資源釋放。這是個異常安全的問題。
常見的非RAII風格代碼裏,若是要確保資源被正確釋放,就要用try {} catch() {} finally {}
塊捕獲異常,而後執行資源釋放的代碼,再將異常從新拋出。
而RAII的理念是,讓資源的生命週期和一個棧上的對象嚴格綁定,確保棧上對象被析構的時候,資源也就被一同釋放了。
在C++中,有大量的代碼都是以RAII風格進行設計的,其中智能指針也是。
std::shared_ptr
的實現引用計數,大概瞭解過智能指針的人都能回答得出來。
雖說實現方式並無規定只能是引用計數,但實際上你們都是這麼寫的,萬一哪天有個GC實現的std::shared_ptr
也別太震驚。
實現思路也挺簡單。
全部指向同一實例的std::shared_ptr
應當持有同一個引用計數,來保持全部std::shared_ptr
計數同步,因此它們共同擁有一個計數器指針long *p
。
在複製時,shared_ptr
管理的對象指針和引用計數器指針被同時複製,而後引用計數器指針保存的引用計數+1——銷燬同理,減小引用,直到刪除。
std::shared_ptr
和CopyAssignable
std::shared_ptr
知足CopyContructiable
、CopyAssignable
和LessThanComparable
這些標準庫的具名要求,所以能夠做爲STL容器的元素。
順便一提
Concept
有很大可能出如今 C++20 標準裏。
std::shared_ptr
不是線程安全的,否則不知足C++對Zero Cost Abstraction
的要(吹)求(逼)。
依據官方說法,多線程訪問不一樣的std::shared_ptr
實例是沒問題的(大多容器也是);多線程訪問同一個std::shared_ptr
實例,可是隻調用const
方法,那麼也是沒問題的(多線程讀);多線程訪問同一個std::shared_ptr
實例,調用非const
方法,那麼會產生數據競爭(多線程讀寫)。
若是但願在線程間傳遞 std::shared_ptr
得靠 STL 提供的原子操做庫std::atomic
。
std::atomic
能夠快速幫助包裝一個線程安全的對象或者指針,不過這東西對std::shared_ptr
的特化是目前還在制定的C++20
標準的一部分,因此能不用則不用,直到標準制定完成穩定,而且各編譯器支持完善後再行考慮。
除此以外,若是確實有這方面的考慮,引入boost
是一個不錯的選擇。
不管如何,跨線程使用std::shared_ptr
我不怎麼支持。
跨線程傳遞std::shared_ptr
自己就是個很是危險的行爲。std::shared_ptr
做爲標準庫的一員,揹負了C++的歷史包袱,它隨時可能被取出裸指針使用,或者意外複製了一次或幾回,而這些對線程安全幾乎就是意味着做死的行爲卻沒有任何管束。
std::auto_ptr
std::weak_ptr
std::unique_ptr
其中std::auto_ptr
已經被掃進歷史的垃圾堆了,做爲替代者,std::unique_ptr
有更明確的語義和更高的可定製性。
std::weak_ptr
是對於std::shared_ptr
的補充,對於但願使用std::shared_ptr
做爲使用了指針的數據結構之間的鏈接方式,又不但願產生循環引用惡劣狀況的一個解決方案。弱指針的存在不影響引用計數工做。
最後是std::unique_ptr
,它的語義是明確惟一持有某一資源,依照約定,被std::unique_ptr
持有的資源不該該再有第二人持有,std::unique_ptr
是惟一訪問該資源的入口。
這些智能指針都有一個共同點:爲了兼容C代碼,因此它們隨時能夠被取出裸指針而不影響自身的工做,但這種使用方式形成的一切後果自負。
std::vector
面試官:...知道
std::vector
吧?講講它是怎麼實現的。
我:vector保存了一個必定長度的buffer,當插入時能夠避免插入一次就分配一次空間blabla...當插入長度超過了buffer長度,buffer會依照內部算法來從新分配一次內存,擴張長度。
回答不全對。其實面試官以後又強調了一次,但面試時沒有聽出來。
面試官:那以前分配的buffer呢?
我:以前分配的buffer先複製到新的buffer裏,而後舊buffer會被釋放。
這裏對於釋放舊buffer的說法實際上是有問題的,能夠具體看看下面。
std::vector
的內存佈局是連續的,這一點除了幾乎每一個人都有所瞭解以外(...),標準給出的要求也能夠看出點端倪。
26.3.11.1 Class template vector overview
A vector is a sequence container that supports (amortized) constant time insert and erase operations at the end; insert and erase in the middle take linear time. Storage management is handled automatically, though hints can be given to improve efficiency.
關鍵點集中在這裏:
... constant time insert and erase operations at the end;
末端插入和刪除是常數時間
... insert and erase in the middle take linear time.
中間插入和刪除須要線性時間(就是 O(n)
)。
典型的數組插入和刪除的特徵,不一樣的是std::vector
能夠變長,因此真正插入大量數據的時候會有屢次從新分配內存和複製的操做。
CopyAssignable
的約定std::vector
要求儲存的對象知足DefautConstructible
、CopyContructiable
和CopyAssignable
的具名要求,文檔參考26.3.11.1
第2節。
26.3.11.1
A vector satisfies all of the requirements of a container and of a reversible container (given in two tables in 26.2), of a sequence container, including most of the optional sequence container requirements (26.2.3), of an allocator-aware container (Table 86), and, for an element type other than bool, of a contiguous container (26.2.1).
其中提到的Table 86
中列出了DefaultConstructible
、CopyAssignable
和CopyConstructiable
。
發揮一下腦洞,這些要求完美符合了以前對於從新分配內存的猜想對不對?
對象要能夠被默認構造,由於vector
的實現多是new
了一個新的對象數組(更多是字節數組,到時候再placement new
);對象要能夠被複制構造,由於對象可能被從舊數組移動到新數組;對象要能夠被複制構造.....
固然更可能的緣由是vector
自己是可複製的,上面的就當我吹逼吧。
除此以外還有CopyInsertable
和MoveInsertable
的具名需求,就像其字面意義那樣,很少作解釋。
對C稍有經驗的人應該知道C語言有一個API叫作realloc
,它作的事情是這樣的:
C++天然不會少。
面試時沒有想起來,原本認爲是一種優化方案,但STL自己就算是優化方案了吧(...)。正確的解答應該是
用realloc的方式嘗試擴展buffer長度,若是沒法擴展長度,則拷貝舊buffer到新buffer,再釋放舊buffer。
還行,失誤就是失誤,認錯複習一遍。
vector
,map
,list
面試官:說說看
vector
、list
、map
有什麼不一樣,分別在什麼樣的上下文環境裏去使用它們吧。我:vector能夠被隨機訪問,支持隨機訪問迭代器,迭代器算法有些不適用在
list
和map
上blabla...list
一般是鏈表實現,在插入刪除的性能上有優點blabla......
順便一提還沒說到map
,面試官就換話題了。
這一題我大概又沒有 get 到面試官的 point,單談論容器的話可說的東西很多,我以爲面試官可能更想了解下我對這些容器的性能和內存方面的認知,惋惜我答的有些太淺白了。
先從迭代器的角度比較三個容器。
vector
是個典型的隨機訪問容器,顯然支持forward iterator
、reversible iterator
和random access iterator
。典型的實現是dynamic array
。
list
是個線性結構容器,支持forward iterator
、reversible iterator
。典型的實現是鏈表。
map
是個樹形容器,支持forward iterator
和reversible iterator
。典型的實現是紅黑樹。
討論常見實現。
vector
是連續分配,訪問成本低,插入和刪除的成本高,會重分配內存。
list
是不連續分配,訪問成本高,任意位置插入刪除成本相對低,插入刪除不會致使從新分配整塊內存。
map
是不連續分配,插入刪除訪問成本不該和線性容器比較,畢竟它是關聯容器。插入刪除的成本都比較高,由於須要從新平衡樹。訪問時間在標準中的要求是對數時間複雜度,插入時間懶得繼續翻標準文檔了。
顯而易見vector
適合高頻讀,而list
適合大量插入刪除,map
和前面兩個迭代器都搭不上調,在須要複雜索引的地方再合適不過了。
這些容器都不是線程安全的。
依照標準,多線程訪問不一樣的容器實例一切都安好,訪問同一個實例的const
方法也ok,可是非const
方法就會引發數據競爭。
尤爲注意迭代器的選擇,這玩意兒有時候不比指針好多少。
面試官:你在項目裏通常是怎麼管理內存的呢?
我:一個是儘量用智能指針,而後是須要頻繁構造對象的場合下能夠用placement new blabla...
內存管理是一個很是廣闊的話題,個人回答太過於淺顯了。常見的內存管理策略有不少,智能指針只能算是RAII這種常見的範式,placement new 算是內存池/對象池的一種寫法大概,還有其餘不少策略我並不瞭解也未能涉及。
RAII的範式能夠確保異常安全,避免手賤忘記回收內存以及底層設計變動拋出的異常沒法處理時致使意外的資源泄露。
諸如此類等等。
有一些約定能夠關注一下。
首先RAII的全寫是獲取資源即初始化,連資源都沒能獲取的話,構造理應失敗,而不是靜默給出一個無效的對象。
很好理解,若是析構又拋個異常出來的話,這個對象還析構不析構?父類還析構不析構?
在STL裏除了智能指針以RAII設計之外,還有加鎖解鎖相關的內容也是:std::lock_guard
。
諸如此類的guard
模式也在其餘語言中有出現:好比說C#的using (var file = File.Open(...)) {}
。
內存池和對象池算是常見的設計範式,基本考慮到大量對象的構造刪除的狀況都會考慮到使用這兩個模式,由於真的很好用(
內存池的模式主要是預先分配內存,而後在這片內存上構造對象,主要的適用場景是大量頻繁構造小對象,構形成本低,生命週期短,內存分配成本居高不下的狀況。固然,不只是這裏提到的場景,根據具體業務邏輯可能還會有不一樣的理由去選擇內存池模式。
對象池區別於內存池的地方在於,對象池的對象構形成本要更高,頻繁構造和析構是沒法接受的,這種時候就須要一個候選備用的對象池,對象池實現須要對象自己容許被複用在不一樣的地方,通常來講性能會比較好。內存池則沒這個顧慮:反正你須要就構造一個唄。
這兩個池均可以用factory
模式來提供構造對象的服務,而工廠的消費者不須要了解對象是怎麼構造出來的。結合RAII的話,內存池、對象池裏的對象還能夠用一層RAII設計的「智能指針」封裝,使其完成使命後能自動返還資源,等待下一個工廠訪客。
面試官:喜歡玩遊戲嗎?都玩過哪些遊戲?
我:個人話...主要玩的是音遊,和貴公司業務可能並無太多關聯。
面試官:除了音樂遊戲,有玩過RPG、ARPG類型的遊戲嗎?
我:像是輻射啊,老滾啊這些...開放世界類型的遊戲遊戲性沒那麼好,比起來我更喜歡電影式的遊戲,好比說最近比較火的《底特律:變人》。
面試官:......(你丫來搗亂的是吧)
面試官:說說你對遊戲行業的見解吧。
我:遊戲行業前景好啊blablabla...娛樂崛起blabla...經濟增加blabla....
面試官:......(????)
面試官:你上一家公司也是製做遊戲的吧?就是說,大家遊戲製做啊,都有哪方面的人在負責作什麼東西,大概是怎麼個分工合做的樣子。(提醒+強調) 我:哦!哦哦,大概就是一我的負責策劃整個遊戲的玩法和系統,設計每一個細節,而後程序負責去實現,自動測試blabla...內部試玩blabla...
還行,這波操做其實我也是挺佩服本身的。
我注意到一件事:在屢次面試遊戲行業的職位時,都提到這這個問題:
你玩過哪些遊戲?
也許形式上有所區別:
你玩過的遊戲裏,有哪些特別喜歡的?
換位思考,若是我是面試官,我爲何要問這個問題?我想知道什麼?
熟悉遊戲嗎?
知道遊戲有哪些元素嗎?
能理解(咱們招你進來要作的遊戲)要你作什麼嗎?
沒必要太過刻意地表達出對遊戲行業的崇拜或者擡高之類的,這一關主要的目的仍是引出下文,聊聊對遊戲製做流程的理解。若是對面試的公司出的產品有所瞭解的話可能算是加分項。
可是,從一個遊戲玩家的角度出發,表現出很差的情緒容易留下壞映像——特別是,絕對不要明顯地表達出對國產網遊、手遊、頁遊的鄙視!!
從一個玩家的角度出發,我也不喜歡大部分國產的頁遊手遊,可是當着遊戲行業公司的面試官的面,表現出我看不起你的態度,知道什麼叫做死嗎?
更況且並非全部國產遊戲都是屎,舉例來講我如今超喜歡 MUSE DASH 這款國產音遊的,手感比蘭空 voze、節奏大師之類的好得多,界面也沒有像節奏大師那樣糊成屎,要不是個人 Unity3D 水平太差我真想給這家 pero pero game 工做室(公司?)投個簡歷看看。
除此以外還有就是抱着拯救國產遊戲的想法或者態度,又或者勞資教大傢什麼纔是真正的遊戲這樣的想法或者態度,做死無極限啊。
比較穩妥的回答方案應該是常見的幾個網遊,好比說LOL,DNF,王者榮耀,諸如此類。實際上玩過沒玩過.....咳,不被戳穿就無所謂了。
加班是屢見不鮮,好像全部遊戲行業的公司都會這麼說。
大概瞭解下幾個術語,算是加班界的黑話吧。
一個是996。什麼意思呢?上午9點上班,晚上9點下班,一週上6天,加班費不用考慮了,不存在的,最多給調休。
再有一個是大小周。一週上6天,一週上5天,如此循環。一樣,大周加班不算加班費,給調休。
另外就是調休。若是加班一天,未來某天就能夠不扣工資休息一天,直白吧。攢下半年的調休而後一口氣給本身放6個月假這種事情仍是作夢比較好,調休基本上就等於無償加班了,忙起來的時候勸你別休,否則人手就不夠了;那閒下來的時候還能讓你一週休6天?你敢休公司也不敢讓你隨便休啊,其餘員工怎麼看。
發薪日。網上有人總結,發薪日越接近月中的,或者超過月中的,大多都是怕員工流失的公司,而這些公司每每都不是什麼好公司。聽起來仍是挺有道理的(
固然,最後仍是要靠本身的眼睛去確認這一點。
以前待得確實是一家小公司,甚至算得上工做室級別的超小初創公司,遊戲製做方面的知識儲備不算充足,寫這篇博客的時候又去補習了一下。
主要的工種分爲策劃、美術、程序。
細分的話,策劃可能有數值方面的,世界背景人物背景方面的,對話文本方面的,甚至可能有長篇幅的資料啊故事啊這方面的需求。
美術有UI方面的,人物、場景的原畫師,3d模型製做,動畫製做,骨骼製做,特效製做,等等方面的。程序常常須要和美術方面的溝通交流。
程序的話主要分先後端和測試,再加上運維和DBA之類的角色。
細分的話前端根據開發平臺不一樣也有不一樣的技術棧,圖像特效上可能會有更專業的大牛負責,team leader帶隊設計架構,分配工做,諸如此類。後端也同樣,根據不一樣的技術抉擇,可能總體的人員配置也有所區別,但你們都是程序嘛。
測試算是比較獨立的,編寫測試代碼是一件很痛苦的事情(
因此這份疼痛有專人負責承受了:)
持續集成啊什麼的也被承包了,測試或者運維會去負責的。
DBA通常公司也用不到,運維多少會兩手SQL,規模更大的公司可能會設置這個專門職位。
流程上來講,策劃給出遊戲方案,美術可能會配合作個初稿效果圖之類的(更多是策劃本身作個簡單的效果圖之類的方便說明),程序瘋狂實現(崩潰-爆發-認命 循環),測試則配合給出反饋,讓程序的脫髮情況持續惡化,最後發佈,項目黃了。
哦不是,我是說項目火了,程序們一躍成爲CTO,迎娶白富美,走上人生巔峯。
(並無)
其實此次面試的自我感受仍是不錯的,沒有犯下太蠢的錯誤,可是能夠改進的地方依然不少,語言組織能力須要進一步提升。
這篇博客的目的是自我檢討,可是此次自我檢討的效果並不算好,由於面試官的問題基本上都戳在我懂,但又沒真正去深刻挖掘的領域。平常使用天然沒有問題,但理解卻談不上了。
若是面試官在細節上稍做追究:好比說placement new和user-defined new 之類的話題上深刻,異常安全,或者問個map用紅黑樹實現,紅黑樹什麼原理,那麼此次我基本又要掛了。
關於給出的待遇的問題......我其實很好奇......
由於我真的才工做一年,不懂啊...
一年工做年限,C++我也不知道算什麼水平,不知道怎麼去橫向對比,要8k是要多了麼...
初級職位的意思是待遇初級仍是能力初級啊...
還有主程通常指的是 team leader 對嗎,遊戲行業程序是否是幹到 team leader 就算到頭了...只能轉管理崗了...