史上最全的C++/游戲開發(fā)面試問題總結(jié)(一)——C++基礎(chǔ)
筆者畢業(yè)兩年,最近通過獵頭拿到了騰訊IEG以及網(wǎng)易游戲的兩個客戶端研發(fā)offer(UE4/C++)。在面試前夕,筆者對C++進(jìn)行了較為全面的復(fù)習(xí)和總結(jié),樂觀
估計可以涵蓋80%左右的面試基礎(chǔ)問題。
這個系列的文章預(yù)計有《C++基礎(chǔ)》、《內(nèi)存、STL、虛函數(shù)相關(guān)》、《數(shù)據(jù)結(jié)構(gòu)與算法》、《操作系統(tǒng)與網(wǎng)絡(luò)》四篇(后續(xù)可能會調(diào)整),每篇都是以問答的形式分享并給出了參考資料的鏈接地址。大部分問題回答的比較簡潔,需要大家去仔細(xì)閱讀參考資料的具體內(nèi)容,當(dāng)然也可以直接問我(人多的話會考慮建一個群)~
個人覺得如果這些問題你全部搞懂的話,大部分面試官在C++上就拿你沒什么辦法或者說不會再進(jìn)一步為難你了。不過想徹底理解所有內(nèi)容也并不容易,這里面涉及到操作系統(tǒng)、數(shù)據(jù)結(jié)構(gòu)、計算機(jī)系統(tǒng)原理、匯編等基礎(chǔ)內(nèi)容,涉及到的書籍包括《C++ Primer》《Inside the C++ Object Model》《Effctive C++》《More Effctive C++》《C++ Template》《The Design and Evolution of C++》《STL源碼剖析》《深入理解計算機(jī)系統(tǒng)》等。在后續(xù)的文章里,我會把這些電子書分享給大家。
問:了解const么?哪些時候用到const?與宏定義有什么差異?(提問概率:★★★★)
簡單理解,const的目的就是定義一個“不會被修改的常量”,可以修飾變量、引用、指針,可以用于函數(shù)參數(shù)、成員函數(shù)修飾。成員變量。使用const可以減少代碼出錯的概率,我們通常要注意的是區(qū)分常量指針(指向常量的指針)和指針常量(地址是常量,指針指向的地址不變)以及合理的在函數(shù)參數(shù)里面使用。具體的情況可以參考下面的書籍與資料。
參考書籍與資料:《Effctive C++》
Const用法總結(jié)(快速區(qū)分指針常量與常量指針)https://blog.csdn.net/u012999985/article/details/49009531
問:reference和pointer的區(qū)別?哪些情況使用pointer?(提問概率:★★)
1.指針可以為空,而引用不可以指向空值。
2.指針可以不初始化,引用必須初始化。這意味著引用不需要檢測合法性
3.指針可以隨時更改指向的目標(biāo),而引用初始化后就不可以再指向任何其他對象
根據(jù)上面的情況我們知道大概知道哪些時候需要使用指針了。不過還有一種情況,在重載如[]符號的時候,建議返回引用,這樣便于我們書寫習(xí)慣也方便理解。因為平時我們都是這樣使用, a[10] = 10;而不是 *a[10] = 10;
參考書籍與資料:《More Effctive C++》
問:inline的優(yōu)劣(提問概率:★★)
優(yōu)點(diǎn):減少函數(shù)調(diào)用開銷
缺點(diǎn):增加函數(shù)體積,exe太大,占用CPU資源,可導(dǎo)致cache裝不下(減小了cache的命中) ,不方便調(diào)試debug下一般不內(nèi)聯(lián), 每次修改會重新編譯頭文件增加編譯時間
注意:inline只是一個請求,編譯器有權(quán)利拒絕。有7種情況下都會拒絕,虛調(diào)用,體積過大,有遞歸,可變數(shù)目參數(shù),通過函數(shù)指針調(diào)用,調(diào)用者異常類型不同,declspec宏等
forceinline字面意思上是強(qiáng)制內(nèi)聯(lián),一般可能只是對代碼體積不做限制了,但是對于上面的那些情況仍然不會內(nèi)聯(lián),如果沒有內(nèi)聯(lián)他會返回一個警告。 構(gòu)造函數(shù)析構(gòu)函數(shù)不建議內(nèi)聯(lián),里面可能會有編譯器優(yōu)化后添加的內(nèi)容,比如說初始化列表里面的東西。
參考書籍與資料:
WiKi(https://zh.wikipedia.org/wiki/%E5%86%85%E8%81%94%E5%87%BD%E6%95%B0)
MSDN(https://msdn.microsoft.com/zh-cn/magazine/z8y1yy88(v=vs.110).aspx)
問:final和override的作用,以及使用場合(提問概率:★★)
final:禁止繼承該類或者覆蓋該虛函數(shù)
override:必須覆蓋基類的匹配的虛函數(shù)
場合(final):不希望這個類被繼承,比如vector,編碼者可能不夠了解vector的實現(xiàn),或者說編寫者不希望別人去覆蓋某個虛函數(shù),顧名思義,final就是最終么
場合(override):第一種,在使用別人的函數(shù)庫,或者繼承了別人寫的類時,想寫一個新函數(shù),可能碰巧與原來基類的函數(shù)名稱一樣,被編譯器誤認(rèn)為要重寫基類的函數(shù)。第二種情況是想覆寫一個基類的函數(shù),但是不小心參數(shù)不匹配或者名字拼錯,結(jié)果導(dǎo)致寫了一個新的虛函數(shù)
參考書籍與資料:《C++ Primer》
問:The rule ofthree是什么?為什么這么做?(提問概率:★)
If you need to explicitly declare either the destructor,copy constructor or copy assignment operator yourself, you probably need toexplicitly declare all three of them.(析構(gòu)函數(shù),拷貝構(gòu)造函數(shù),賦值運(yùn)算符盡可能一起聲明。如果你只定義一個,編譯器會幫助你定義另外兩個,而編譯器定義的版本也許不是你想要的)
參考書籍與資料:WIKI Rule of three
(https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming))
問:C++03/98有什么你不習(xí)慣或不喜歡的用法?C++11有哪些你使用到的新特性?(提問概率:★★★★★)
這個問題最簡單的辦法就是看下一個版本的C++有哪些特性,新的特性肯定是有意義的。
如:
auto,有一些迭代器或者map嵌套類型,遍歷時比較麻煩,auto寫起來很方便。
vector以及其他容器的列表初始化,原來想要像數(shù)組一樣初始化的話,需要一個一個來,很麻煩。
類內(nèi)初始值問題,總是需要放到構(gòu)造函數(shù)里面初始化,初始化列表倒是不錯,但是初始化數(shù)據(jù)太多就不行了。
nullptr,C++11前的NULL一般是是這樣定義的 #define NULL 0,這可能會導(dǎo)致一些函數(shù)參數(shù)匹配問題。而nullptr可以避免這個問題。
thread,不需要再使用其他的庫來寫多線程了。
智能指針shareptr,一定程度上解決內(nèi)存泄露問題。
右值引用,減少拷貝開銷。
lambda function,簡化那些結(jié)構(gòu)簡單的函數(shù)代碼。
當(dāng)然,你要是能說出一些還沒有改正或者有待考慮的問題就更好了,比如內(nèi)存管理的困難(沒有GC),沒有反射以及一些C#,java里面有而C++沒有的特性等,要能深入一點(diǎn)說那就更好了
參考書籍與資料:《C++ Primer》 nullptr,0與NULL (https://www.cnblogs.com/porter/p/3611718.html)
問:Delete數(shù)組的一部分會發(fā)生什么?為什么出現(xiàn)異常?(提問概率:★★★★)
VC下是異常,實際刪除的時候整個數(shù)組的內(nèi)存不僅僅是數(shù)據(jù)大小還包括CRTHeader,數(shù)組長度等信息。如果刪除一部分會從數(shù)量的位置開始傳入,是有問題的。VC下數(shù)組的內(nèi)存布局參考下面公式,
公式1)_CrtMemBlockHeader + <Your Data>+gap[nNoMansLandSize];這類數(shù)據(jù)用delete和delete[]都一樣!
公式2)_CrtMemBlockHeader +數(shù)組元素個數(shù)+ <Your Data>+gap[nNoMansLandSize];
如果其他編譯器,有可能不會報錯。但是只釋放一個數(shù)組對象也是有問題的,其他的對象既沒有釋放也沒有析構(gòu)。
參考書籍與資料:為何new出的對象數(shù)組必須要用delete[]刪除,而普通數(shù)組delete和delete[]都一樣(https://www.cnblogs.com/sura/archive/2012/07/03/2575448.html)
問:系統(tǒng)是如何知道指針越界的?(提問概率:★★)
VC下有一個結(jié)構(gòu)體_CrtMemBlockHeader,里面有一個Gap屬性,這個Gap數(shù)組放在你的指針數(shù)據(jù)的后面,默認(rèn)為0xFD,當(dāng)檢測到你的數(shù)據(jù)后不是0xFD的時候就說明的你的數(shù)據(jù)越界了。
參考書籍與資料:為何new出的對象數(shù)組必須要用delete[]刪除,而普通數(shù)組delete和delete[]都一樣 (https://www.cnblogs.com/sura/archive/2012/07/03/2575448.html)
問:C++編譯器有哪些常見的優(yōu)化?聽說過RVO(NRVO)么?(提問概率:★★★)
1.常量替換如int a = 2; int b = a; return b;可能會優(yōu)化為 int b=2; return b; 進(jìn)一步會優(yōu)化為return 2;
2.無用代碼消除比如函數(shù)返回值以及參數(shù)與該表達(dá)式完全無關(guān),直接會優(yōu)化掉這段代碼
3.表達(dá)式預(yù)計算和子表達(dá)式提取常量的乘法會在編譯階段就計算完畢,相同的子表達(dá)式也會被合并成一個變量來進(jìn)行計算
4.某些返回值為了避免拷貝消耗,可能會被優(yōu)化成一個引用并放到函數(shù)參數(shù)里面,如RVO,NRVO。
RVO:函數(shù)返回的對象如果是新構(gòu)造的值類型就直接通過一個引用作為參數(shù)來構(gòu)造,進(jìn)而避免創(chuàng)建一個臨時的“temp”對象。
NRVO:相比RVO進(jìn)一步優(yōu)化。對于RVO,如果函數(shù)在返回前創(chuàng)建了一個臨時變量,這個臨時變量還是會被構(gòu)造的,參考下面代碼
Point3d Factory()
{
Point3d po(1,2, 3);
return po;
}
//RVO優(yōu)化后
void Factory(Point3d &_result)
{
Point3d po(1,2,3);
_result.Point3d::Point3d(po);
return;
}
//NRVO優(yōu)化后
void Factory(Point3d &_result)
{
_result.Point3d::Point3d(1, 2, 3);
return;
}
NRVO則直接跳過臨時對象的構(gòu)造。
(補(bǔ)充:上面的優(yōu)化有的時候不同編譯器可能有差別,想一探究竟建議查看反匯編代碼。一般來說函數(shù)返回的臨時值類型對象是右值,通過寄存器存儲,所以獲取不到地址)
當(dāng)然,優(yōu)化還有很多,這里不一一列舉。由于這些優(yōu)化,你在調(diào)試過程中可能無法設(shè)置斷點(diǎn),所以需要關(guān)閉優(yōu)化。還有一個小的技巧,static變量不會被優(yōu)化。
參考書籍與資料:
《Inside the C++ Object Model》(深度探索C++對象模型)
RVO和NRVO的區(qū)別是什么?
(https://www.zhihu.com/question/32237405/answer/55440484)
Copy elision
(https://en.wikipedia.org/wiki/Copy_elision#Return_value_optimization)
RVO V.S. std::move
(https://www.ibm.com/developerworks/community/blogs/5894415f-be62-4bc0-81c5-3956e82276f3/entry/RVO_V_S_std_move?lang=en)
C++中的RVO和NRVO
(https://blog.csdn.net/yao_zou/article/details/50759301)
詳解RVO與NRVO
(https://blog.csdn.net/virtual_func/article/details/48709617)
問:聽說過mangling么?(提問概率:★★)
mangling 指編譯器給函數(shù)變量等添加很多的描述信息到名稱上用于傳遞更多信息。常用函數(shù)重載,編譯時可以把返回值類型等與原函數(shù)名稱進(jìn)行組合達(dá)到區(qū)分的效果,具體規(guī)則看編譯器。
參考書籍與資料:《Inside the C++ Object Model》(深度探索C++對象模型)
Name mangling
(https://en.wikipedia.org/wiki/Name_mangling)
Why can't C functions be name-mangled?
(https://stackoverflow.com/questions/36621845/why-cant-c-functions-be-name-mangled)
問:成員函數(shù)指針了解么?可以轉(zhuǎn)換為Void*么?為什么?(提問概率:★★★)
不可以轉(zhuǎn)換成Void*,因為成員函數(shù)指針大小并不是4個字節(jié)(32位機(jī)器上),除了地址還需要this的delta,索引等信息。成員函數(shù)指針比較復(fù)雜,建議好好讀一下下面給出的文章。
寫法:函數(shù)指針 float (*my_func_ptr)(int, char *);
成員函數(shù)指針 float (SomeClass::*my_memfunc_ptr)(int,char *);
參考書籍與資料:
成員函數(shù)指針與高性能的C++委托(中文)(https://www.cnblogs.com/jans2002/archive/2006/10/13/528160.html)
Member Function Pointers and the Fastest Possible C++Delegates(英文)
(https://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible)
問:描述一下C/C++代碼的編譯過程?(提問概率:★★★★)
預(yù)處理——編譯——匯編——鏈接。預(yù)處理器先處理各種宏定義,然后交給編譯器;編譯器編譯成.s為后綴的匯編代碼;匯編代碼再通過匯編器形成.o為后綴的機(jī)器碼(二進(jìn)制);最后通過鏈接器將一個個目標(biāo)文件(庫文件)鏈接成一個完整的可執(zhí)行程序(或者靜態(tài)庫、動態(tài)庫)。
參考書籍與資料:《深入理解計算機(jī)系統(tǒng)》
c++編譯過程簡介
(http://www.cnblogs.com/dongdongweiwu/p/4743709.html)
問:了解靜態(tài)庫與動態(tài)庫么?說說靜態(tài)鏈接與動態(tài)鏈接的實現(xiàn)思路(提問概率:★★★)
靜態(tài)庫:任意個.o文件的集合,程序link時,被復(fù)制到output文件。這個靜態(tài)庫文件是靜態(tài)編譯出來的,索引和實現(xiàn)都在其中,可以直接加到內(nèi)存里面執(zhí)行。
對于Windows上的靜態(tài)庫.lib有兩種,一種和上面描述的一樣,是任意個.o文件的集合。程序link時,隨程序直接加載到內(nèi)存里面。另一種是輔助動態(tài)鏈接的實現(xiàn),包含函數(shù)的描述和在DLL中的位置。也就是說,它為存放函數(shù)實現(xiàn)的dll提供索引功能,為了找到dll中的函數(shù)實現(xiàn)的入口點(diǎn),程序link時,根據(jù)函數(shù)的位置生成函數(shù)調(diào)用的jump指令。(Linux下.a為后綴)
動態(tài)庫:包含一個或多個已被編譯、鏈接并與使用它們的進(jìn)程分開存儲的函數(shù)。在程序編譯時并不會被連接到目標(biāo)代碼中,而是在程序運(yùn)行是才被載入。不同的應(yīng)用程序如果調(diào)用相同的庫,那么在內(nèi)存里只需要有一份該共享庫的實例,規(guī)避了空間浪費(fèi)問題。(Linux下.so為后綴)
參考書籍與資料:《深入理解計算機(jī)系統(tǒng)》
Static library
(https://en.wikipedia.org/wiki/Static_library)
Dynamic-link library
(https://en.wikipedia.org/wiki/Dynamic-link_library)
lib與dll的關(guān)系
(https://blog.csdn.net/u012999985/article/details/50429715)
程序的靜態(tài)鏈接,動態(tài)鏈接和裝載 (https://www.cnblogs.com/acSzz/p/5743789.html)
程序運(yùn)行流程——鏈接、裝載及執(zhí)行 (https://www.xuebuyuan.com/1730287.html)
問:知道內(nèi)部鏈接與外部鏈接么?(提問概率:★★)
內(nèi)部鏈接:如果一個名稱對于他的編譯單元是局部的,并且在鏈接時不會與其他的編譯單元中同樣的名字沖突,那么這個名稱就擁有內(nèi)部鏈接。
外部鏈接:一個多文件的程序中,一個實體可以在鏈接時與其他編譯單元交互,那么這個實體就擁有外部鏈接。換個說法,那些編譯單元(.cpp)中能想其他編譯單元(.cpp)提供其定義,讓其他編譯單元(.cpp)使用的函數(shù)、變量就擁有外部鏈接
參考書籍與資料:What is external linkage and internallinkage?
(https://stackoverflow.com/questions/1358400/what-is-external-linkage-and-internal-linkage)
C++編譯與鏈接(2)-淺談內(nèi)部鏈接與外部鏈接 (https://www.cnblogs.com/magicsoar/p/3840682.html)
理解C++的鏈接:C++內(nèi)鏈接與外鏈接的意義 (https://blog.csdn.net/u012999985/article/details/50429769)
問:extern與static(提問概率:★★★)
extern 聲明一個變量定義在其他文件,這樣當(dāng)前文件就可以使用這個變量,否則會編譯失敗,如果兩個全局變量名稱一樣會出現(xiàn)鏈接失敗。extern c的作用更重要,因為c++的編譯方式與c是不同的,比如函數(shù)重載利用mangling的優(yōu)化。static變量,就是在全局聲明一個變量判斷是否初始化,是的話之后就不做操作了。static成員函數(shù)其實在編譯后與class完全沒有關(guān)系。static成員其實也沒關(guān)系,但是private的需要通過類去調(diào)用。static全局只能在本文件使用(內(nèi)鏈接),與其他無關(guān)。全局函數(shù)變量是外鏈接,可以跨單元調(diào)用。
參考書籍與資料:《C++ primer》
extern "C"
(https://baike.baidu.com/item/extern%20%22C%22)
問:delegate是什么?實現(xiàn)思路?與event的區(qū)別?(提問概率:★★★)
代理簡單來說就是讓對象B去代理A執(zhí)行A本身的操作,本質(zhì)上就是通過指向其他成員函數(shù)或者全局函數(shù)的函數(shù)指針去代理執(zhí)行。而函數(shù)指針有兩種,成員函數(shù)指針與普通的函數(shù)指針,我們一般就是通過對這兩種指針的封裝來實現(xiàn)代理的效果。常見的實現(xiàn)方式有兩種,一種是通過多態(tài)接口,另一種是通過宏。代理也分為單播代理與多播代理,單播就是一個調(diào)用只代理執(zhí)行一個函數(shù)功能,多播代理就是一個調(diào)用可以綁定多個代理函數(shù),可以觸發(fā)多個代理的函數(shù)操作。
Event是一種特殊的多播delegate,只有聲明事件的類可以調(diào)用事件的觸發(fā)操作。最常見的也容易理解的就是MFC里面的按鈕的鼠標(biāo)點(diǎn)擊事件了,他的調(diào)用只能在Button里面去執(zhí)行。
參考書籍與資料:[C++]實現(xiàn)委托模型(https://www.cnblogs.com/zplutor/archive/2011/09/17/2179756.html)
問:使用過模板么?了解哪些特性?(提問概率:★★★★)
模板分為函數(shù)模板與類模板,其根本目的是將類型“參數(shù)化”,實現(xiàn)編譯時的“動態(tài)化”,避免重復(fù)代碼的書寫。另一種運(yùn)行時的“動態(tài)化”就是多態(tài)。
模板使用常見的特性有“特化”,“偏特化”,“非類型模板參數(shù)”,“設(shè)置模板參數(shù)默認(rèn)類型”,“模板中的typename的使用”,“雙重模板參數(shù)Template Template Parameters”,“成員模板Member Template”,理解這些內(nèi)容我們就基本上可以看STL標(biāo)準(zhǔn)庫了。
另外,模板的實例化過程也是需要理解的。
參考書籍與資料:“STL源碼”,《C++ Template》,《C++ Primer》
問:聽說過轉(zhuǎn)發(fā)構(gòu)造么?(提問概率:★★)
通過foward關(guān)鍵字可以同時考慮到參數(shù)為左值以及右值的情況,然后把函數(shù)的參數(shù)完美的轉(zhuǎn)發(fā)到其他函數(shù)的參數(shù)里面。這個里面涉及到左值、右值、move、forward、引用折疊等技術(shù)點(diǎn)。
參考書籍與資料:《C++ Primer》《Effective Modern C++》
The Forwarding Problem: Arguments
(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm)
A Brief Introduction to Rvalue References (https://www.artima.com/cppsource/rvalue.html)
C++11 forward完美轉(zhuǎn)發(fā)
(https://blog.csdn.net/rankun1/article/details/78354153)
Effective Modern C++ 條款28 理解引用折疊 (https://blog.csdn.net/big_yellow_duck/article/details/52433305)
移動語義(move semantic)和完美轉(zhuǎn)發(fā)(perfect forward)
(https://codinfox.github.io/dev/2014/06/03/move-semantic-perfect-forward/)
問:描述一下函數(shù)調(diào)用過程中棧的變化(提問概率:★★★★)
回答這個問題需要對棧的使用過程,函數(shù)調(diào)用,匯編都有一定的理解才行。首先,要清楚一個概念“棧幀”。
棧幀(stack frame):機(jī)器用棧來傳遞過程參數(shù),存儲返回信息,保存寄存器用于以后恢復(fù),以及本地存儲。為單個過程(函數(shù)調(diào)用)分配的那部分棧稱為棧幀。棧幀其實是兩個指針寄存器,寄存器ebp為幀指針(指向該棧幀的最底部),而寄存器esp為棧指針(指向該棧幀的最頂部)。
然后我們再簡單描述一下函數(shù)調(diào)用的機(jī)制,每個函數(shù)有自己的函數(shù)調(diào)用地址,里面會有各種指令操作(這端內(nèi)存位于“代碼段”部分),函數(shù)的參數(shù)與局部變量會被創(chuàng)建并壓縮到“棧”的里面,并由兩個指針分別指向當(dāng)前幀棧頂和幀棧尾。當(dāng)進(jìn)入另一個子函數(shù)時候,當(dāng)前函數(shù)的相關(guān)數(shù)據(jù)會被保存到棧里面,并壓入當(dāng)前的返回地址。子函數(shù)執(zhí)行時也會有自己的“棧幀”,這個過程中會調(diào)用CPU的寄存機(jī)進(jìn)行計算,計算后再彈出“棧幀”相關(guān)數(shù)據(jù),通過“棧”里面之前保存的返回地址再回到原來的位置執(zhí)行前面的函數(shù)。參考下圖(改編自https://www.cnblogs.com/zlcxbb/p/5759776.html的圖片):

參考書籍與資料:《深入理解計算機(jī)系統(tǒng)》
函數(shù)調(diào)用棧幀過程帶圖詳解
(https://blog.csdn.net/IT_10/article/details/52986350)
函數(shù)調(diào)用棧淺析
(https://www.cnblogs.com/coderland/p/5902719.html)
函數(shù)調(diào)用過程棧幀變化詳解
(https://www.cnblogs.com/zlcxbb/p/5759776.html)
問:__cdecl/__stdcall是什么意思(提問概率:★★★)
常見的函數(shù)調(diào)用有如下
__cdecl/__stdcall/__thiscall/__fastcall。
cdecl按照c語言標(biāo)準(zhǔn),從右到左,可以實現(xiàn)可變參數(shù),調(diào)用者彈出參數(shù)。
stdcall(pascal調(diào)用約定)按照c++標(biāo)準(zhǔn),函數(shù)參數(shù)從右到左,不支持可變參數(shù),函數(shù)返回自動清空。但是有的時候編譯器會識別并優(yōu)化成cdecl。
Pascal語言中參數(shù)就是從左到右入棧的不支持可變長度參數(shù)
(注:__stdcall標(biāo)記的函數(shù)結(jié)束后,ret 8表示清理8個字節(jié)的堆棧,函數(shù)自己恢復(fù)了堆棧)
參考書籍與資料:“建議查看反匯編代碼”
x86 calling conventions
(https://en.wikipedia.org/wiki/X86_calling_conventions)
What is __stdcall?
(https://stackoverflow.com/questions/297654/what-is-stdcall)
__stdcall
( https://msdn.microsoft.com/zh-cn/library/zxk0tw93.aspx)
問:C++中四種Cast的使用場景是什么?(提問概率:★★★★★)
constcast,去掉常量屬性以及volatile,但是如果原來他就是常量去掉之后千萬不要修改;比如你手里有一個常量指針引用,但是函數(shù)接口是非常量指針,可能需要轉(zhuǎn)換一下;成員函數(shù)聲明為const,你想用this去執(zhí)行一個函數(shù),也需要用constcast
staticcast,基本類型轉(zhuǎn)換到void,轉(zhuǎn)換父類指針到子類不安全
dynamiccast,判斷基類指針或引用是不是我要的子類類型,不是強(qiáng)轉(zhuǎn)結(jié)果就返回null,用于多態(tài)中的類型轉(zhuǎn)換
reintercast,可以完成一些跨類型的轉(zhuǎn)換,如int到void*,用于序列化網(wǎng)絡(luò)包數(shù)據(jù)
參考書籍與資料:《C++ Primer》《The Design and Evolution of C++》(C++語言的設(shè)計與演化)
問:用過或很熟悉的設(shè)計模式有哪些?(提問概率:★★★★)
這個問題看好書寫寫代碼就可以自由發(fā)揮了,下面給幾個例子。
工廠模式,通過簡單工廠生成NPC對象,簡單處理的話可通過“字符串匹配”動態(tài)創(chuàng)建對象。如果有“反射機(jī)制”就可以直接傳class來實現(xiàn)。當(dāng)然可以進(jìn)一步使用抽象工廠,處理不同的生產(chǎn)對象。
單例,實現(xiàn)全局唯一的一個對象。構(gòu)造函數(shù)、靜態(tài)指針都是私有的,使用前提前初始化或者加鎖來保證線程安全。
Adaptor適配器,代碼適配原來的相機(jī)移動最后調(diào)用的是原來的移動,現(xiàn)在加了適配器繼承里面放了當(dāng)前引擎的攝像機(jī),然后覆蓋原來攝像機(jī)的移動邏輯。
Observer,一個對象綁定多個觀察者,然后這個對象一旦有消息就立刻公布給所有的觀察者,觀察者可以動態(tài)添加或刪除。在UE4里面,行為樹任務(wù)節(jié)點(diǎn)請求任務(wù)后進(jìn)入執(zhí)行狀態(tài),然后會立刻注冊一個觀察者observer到行為樹(行為樹本身就相當(dāng)于前面提到的那個對象)的observer數(shù)組里面同時綁定一個代理函數(shù)。行為樹tick檢測消息發(fā)送給所有觀察者,觀察者收到消息執(zhí)行代理函數(shù)。
參考書籍與資料:《Head First設(shè)計模式》《設(shè)計模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》
常見設(shè)計模式的解析和實現(xiàn)C++ (https://wenku.baidu.com/view/7488c59f0508763231121295.html)
Design Patterns
(https://en.wikipedia.org/wiki/Design_Patterns)
