2018網易遊戲開發面試題(經驗)錦集

一、C++部分

1.1 malloc和new之前的區別以及底層實現

 

 相關文章:

https://blog.csdn.net/nie19940803/article/details/76358673

https://blog.csdn.net/seamanj/article/details/81019920

https://www.cnblogs.com/sinaxyz/archive/2012/08/20/2647631.html

 

1.2 什麼是多態

C++多態方式:

(1)靜態多態(重載,模板)

是在編譯的時候,就確定調用函數的類型。

(2)動態多態(覆蓋,虛函數實現)

運行的時候,才確定調用的是哪個函數,動態綁定。運行基類指針指向派生類的對象,並調用派生類的函數。

虛函數實現原理:虛函數表和虛函數指針。

純虛函數: virtual int fun() = 0;

相關文章:

https://www.cnblogs.com/Allen-rg/p/6927129.html

https://www.cnblogs.com/dormant/p/5223215.html

http://blog.jobbole.com/107432/

https://blog.csdn.net/haoel/article/details/1948051/

 

1.3 虛析構函數的作用

基類定義了虛函數,子類可以重寫該函數,當子類重新定義了父類的虛函數後,父類指針根據賦給它的不同的子類指針,動態地調用屬於子類的該函數,且這樣的函數調用是無法在編譯器期間確認的,而是在運行期確認,也叫做遲綁定。

定義一個函數爲虛函數,不代表函數爲不被實現的函數。

定義他爲虛函數是爲了允許用基類的指針來調用子類的這個函數。

定義一個函數爲純虛函數,才代表函數沒有被實現。

定義純虛函數是爲了實現一個接口,起到一個規範的作用,規範繼承這個類的程序員必須實現這個函數。

(1)當派生類的對象從內存中撤銷時,一般先調用派生類的析構函數釋放該對象中的派生類部分,再調用基類的析構函數釋放該對象中的基類部分,從而能夠完整的釋放該對象內存。

(2)但是,當用基類指針指向了一個派生類對象,即 base *bptr = new child;此時用delete bptr;來撤銷bptr 指向的動態存儲空間時,只會執行基類的析構函數來釋放該堆內存中的基類部分,但是並不會執行派生類的析構函數來釋放該堆內存中的派生類部分。此時,就會造成內存泄漏現象。

(3)爲了避免此類現象發生,我們將基類的析構函數聲明爲虛析構函數,這樣就解決了上述問題(即先調用派生類的析構函數釋放該動態空間中的派生類部分,再調用基類的析構函數釋放該動態空間中的基類部分,從而能夠完整的釋放該堆內存)。

(4)如果將基類的析構函數聲明爲虛析構函數,那麼該基類的所有派生類的析構函數都自動成爲虛析構函數。

相關文章:

https://www.cnblogs.com/milanleon/p/6479697.html

https://www.zhihu.com/question/23971699

 

1.4 虛函數的底層的實現

實現原理:虛函數表+虛表指針

關鍵字:虛函數底層實現機制;虛函數表;虛表指針

編譯器處理虛函數的方法是:爲每個類對象添加一個隱藏成員,隱藏成員中保存了一個指向函數地址數組的指針,稱爲虛表指針(vptr),這種數組成爲虛函數表(virtual function table, vtbl),即,每個類使用一個虛函數表,每個類對象用一個虛表指針

舉個例子:基類對象包含一個虛表指針,指向基類中所有虛函數的地址表。派生類對象也將包含一個虛表指針,指向派生類虛函數表。看下面兩種情況:

  • 如果派生類重寫了基類的虛方法,該派生類虛函數表將保存重寫的虛函數的地址,而不是基類的虛函數地址。
  • 如果基類中的虛方法沒有在派生類中重寫,那麼派生類將繼承基類中的虛方法,而且派生類中虛函數表將保存基類中未被重寫的虛函數的地址。注意,如果派生類中定義了新的虛方法,則該虛函數的地址也將被添加到派生類虛函數表中。

相關文章:

https://blog.csdn.net/wanghaobo920/article/details/7674631

https://blog.csdn.net/chenchong_219/article/details/41967321

https://www.cnblogs.com/Allen-rg/p/6927319.html

https://blog.csdn.net/m0_37316917/article/details/70143151

https://blog.csdn.net/f1033774377/article/details/80488867

 

1.5 數組和鏈表的區別

  • 數組靜態分配內存,鏈表動態分配內存;
  • 數組在內存中連續,鏈表不連續;
  • 數組元素在棧區,鏈表元素在堆區;
  • 數組利用下標定位,時間複雜度爲O(1),鏈表定位元素時間複雜度O(n);
  • 數組插入或刪除元素的時間複雜度O(n),鏈表的時間複雜度O(1);

相關文章:

https://www.cnblogs.com/janneystory/p/5758958.html

https://blog.csdn.net/snow_wu/article/details/53172721

https://blog.csdn.net/hao10119/article/details/80412472

 

1.6 C++容器迭代器失效

vector迭代器的失效情況: 

1.當插入(push_back)一個元素後,end操作返回的迭代器肯定失效。

2.當插入(push_back)一個元素後,capacity返回值與沒有插入元素之前相比有改變,則需要重新加載整個容器,此時begin和end操作返回的迭代器都會失效。 
3.當進行刪除操作(erase,pop_back)後,指向刪除點的迭代器全部失效;指向刪除點後面的元素的迭代器也將全部失效。 
deque迭代器的失效情況: 
1.在deque容器首部或者尾部插入元素不會使得任何迭代器失效。 
2.在其首部或尾部刪除元素則只會使指向被刪除元素的迭代器失效。 
3.在deque容器的任何其他位置的插入和刪除操作將使指向該容器元素的所有迭代器失效。 

List/set/map迭代器的失效情況: 

刪除時,指向該刪除節點的迭代器失效

list<int> intList; 
list<int>::iterator it = intList.begin(); 
while(it != intList.end()) 

    it = intList.erase(it); 
    …… 

相關文章:

https://www.cnblogs.com/blueoverflow/p/4923523.html

https://blog.csdn.net/lujiandong1/article/details/49872763

https://www.cnblogs.com/Commence/p/7526421.html

https://blog.csdn.net/mr_chenping/article/details/30548507

 

1.7 指針和引用的區別

爲了進一步加深大家對指針和引用的區別,下面我從編譯的角度來闡述它們之間的區別:

程序在編譯時分別將指針和引用添加到符號表上,符號表上記錄的是變量名及變量所對應地址。指針變量在符號表上對應的地址值爲指針變量的地址值,而引用在符號表上對應的地址值爲引用對象的地址值。符號表生成後就不會再改,因此指針可以改變其指向的對象(指針變量中的值可以改),而引用對象則不能修改。

引用與指針的區別

1、指針是一個實體,需要分配內存空間。引用只是變量的別名,不需要分配內存空間。

2、引用在定義的時候必須進行初始化,並且不能夠改變。指針在定義的時候不一定要初始化,並且指向的空間可變。(注:不能有引用的值不能爲NULL)

3、有多級指針,但是沒有多級引用,只能有一級引用。

4、指針和引用的自增運算結果不一樣。(指針是指向下一個空間,引用時引用的變量值加1)

5、sizeof 引用得到的是所指向的變量(對象)的大小,而sizeof 指針得到的是指針本身的大小。

6、引用訪問一個變量是直接訪問,而指針訪問一個變量是間接訪問。

參考答案:

(1)當引用被創建時,它必須被初始化。而指針則可以在任何時候被初始化。

(2)一旦一個引用被初始化爲指向一個對象,它就不能被改變爲對另一個對象的引用。而指針則可以在任何時候指向另一個對象。

(3)不可能有NULL引用。必須確保引用是和一塊合法的存儲單元關聯。

進一步解析:

指針和引用都是C++中的基本語法成份,它們既有聯繫,也有不同之處。

它們都是地址的概念,其中指針指向一塊內存,它的內容是所指內存的地址;而引用是某塊內存的別名,具體來說,指針是一個變量的地址,引用是一個變量的別名。

但它們的不同之處也很明顯,體現在以下方面:

  • 指針是一個實體,而引用僅是個別名;
  • 引用必須被初始化,指針不必;
  • 引用只能在定義時被初始化一次,之後不可變;指針可以改變所指的對象;
  • 可以有const指針,但是沒有const引用;
  • 不存在指向空值的引用,但是存在指向空值的指針,即引用不能爲空,指針可以爲空;
  • “sizeof 引用”得到的是所指向的變量(對象)的大小,而“sizeof 指針”得到的是指針本身(所指向的變量或對象的地址)的大小;
  • 指針和引用的自增(++)運算意義不一樣;
  • 程序爲指針變量分配內存區域,而引用不需要分配內存區域;
  • 指針可以有多級,但是引用只能是一級,例如int **p是合法的,而 int &&a;是不合法的;
  • 指針和引用作爲函數參數進行傳遞時也不同。用指針傳遞參數,可以實現對實參進行改變的目的;在將引用作爲函數參數進行傳遞時,實質上傳遞的是實參本身,而不是實參的一個拷貝,因此對形參的修改其實是對實參的修改。

相關文章:

https://www.cnblogs.com/x_wukong/p/5712345.html

https://blog.csdn.net/zhengqijun_/article/details/54980769

http://chuansong.me/n/2850726

 

1.8 C++中std::function模板和std::bind函數的使用

std::function

它是函數、函數對象、函數指針、和成員函數的包裝器,可以容納任何類型的函數對象,函數指針,引用函數,成員函數的指針。
以統一的方式處理函數、函數對象、函數指針、和成員函數。
允許保存和延遲執行函數。

std::bind綁定器

  • 將函數、成員函數和閉包轉成function函數對象
  • 將多元(n>1)函數轉成一元函數或者(n-1)元函數。

 

1、定義

bind(F f, T1 t1, T2 t2, ..., TN tN);

具體爲:

bind(&要調用的函數,&對象, 要調用函數的參數1,要調用函數的參數2...,_1(bind函數的參數1),_2(bind函數的參數2)...)

注:如果bind的是一個非靜態成員函數,第二個參數一定是一個該成員的一個指針,後面纔是正常的參數。

2、bind使用形式

(1)bind(&f)()  假設f是一個全局函數,綁定全局函數並調用;

(2)bind (&A::f, A())()  假設A是一個構造函數爲空的類,這個形式綁定了類的成員函數,故第二個參數需要傳入一個成員(成員靜態函數除外);

(3)bind (&A::f, _1)(new A()) 同上,效果是一樣的,但是使用了佔位符,使得沒有固定的的對象,推薦。

注:使用的時候一定要注意指向的是沒有this指針的函數(全局函數或靜態成員函數),還是有this指針的函數。後面一種必須要用bind()函數,而且要多一個參數,因爲靜態成員函數與非靜態成員函數的參 數表不一樣,原型相同的非靜態函數比靜態成員函數多一個參數,即第一個參數this指針,指向所屬的對象,任何非靜態成員函數的第一個參數都是this指針。

核心理解綁定類的成員函數。

相關文章:

https://blog.csdn.net/cr2066/article/details/52145719

https://www.cnblogs.com/bencai/p/9124654.html

https://blog.csdn.net/u011602557/article/details/70162609

https://blog.csdn.net/hanzhenling/article/details/45483913

https://blog.csdn.net/yusiguyuan/article/details/37102283

 

1.9 Lambda表達式

聲明Lambda表達式

Lambda表達式完整的聲明格式如下:

[capture list] (params list) mutable exception-> return type { function body }

各項具體含義如下

  1. capture list:捕獲外部變量列表
  2. params list:形參列表
  3. mutable指示符:用來說用是否可以修改捕獲的變量
  4. exception:異常設定
  5. return type:返回類型
  6. function body:函數體

相關文章:

https://www.cnblogs.com/DswCnblog/p/5629165.html

https://www.cnblogs.com/langzou/p/5962033.html

 

1.10 C++仿函數(functor)

1 仿函數的概念

仿函數,又名函數對象,是一個定義了operator ()的對象。仿函數的主要功能代碼在仿函數類的operator ()體內完成。仿函數的妙處:

(1) 仿函數比一般函數更靈巧,可以用有狀態,對於仿函數可以同時擁有兩個狀態的不同實體。

(2) 每個仿函數都有其型別,通過傳遞不同型別的仿函數當作template參數給容器,可以構造出型別不同的容器。

(3) 執行速度上,仿函數通常比函數指針快。

很多stl算法有一個函數參數,例如remove_if,for_each等,這個函數可以是普通的全局函數,仿函數,類的成員函數(非static,static可以作爲全局函數使用),類的成員函數比較特殊,需要使用適配器函數mem_fun/mem_fun_ref包裝纔可以。

相關文章:

https://blog.csdn.net/raito__/article/details/51612690

https://www.cnblogs.com/landy126/archive/2013/03/09/2951227.html

 

1.11 static_cast與dynamic_cast

用法:static_cast < type-id > ( exdivssion ) 
該運算符把exdivssion轉換爲type-id類型,但沒有運行時類型檢查來保證轉換的安全性。它主要有如下幾種用法:
①用於類層次結構中基類和子類之間指針或引用的轉換。
  進行上行轉換(把子類的指針或引用轉換成基類表示)是安全的;
  進行下行轉換(把基類指針或引用轉換成子類表示)時,由於沒有動態類型檢查,所以是不安全的。
②用於基本數據類型之間的轉換,如把int轉換成char,把int轉換成enum。這種轉換的安全性也要開發人員來保證。
③把空指針轉換成目標類型的空指針。
④把任何類型的表達式轉換成void類型。

注意:static_cast不能轉換掉exdivssion的const、volitale、或者__unaligned屬性。


3.2 dynamic_cast
用法:dynamic_cast < type-id > ( exdivssion )
該運算符把exdivssion轉換成type-id類型的對象。Type-id必須是類的指針、類的引用或者void *;
如果type-id是類指針類型,那麼exdivssion也必須是一個指針,如果type-id是一個引用,那麼exdivssion也必須是一個引用。

dynamic_cast主要用於類層次間的上行轉換和下行轉換,還可以用於類之間的交叉轉換。
在類層次間進行上行轉換時,dynamic_cast和static_cast的效果是一樣的;
在進行下行轉換時,dynamic_cast具有類型檢查的功能,比static_cast更安全。

一、static_cast關鍵字(編譯時類型檢查)

用法:static_cast < type-id > ( expression ),該運算符把expression轉換爲type-id類型,但沒有運行時類型檢查來保證轉換的安全性,它主要有如下幾種用法:

(1)用於基本數據類型之間的轉換,如把int轉換爲char,把int轉換成enum,但這種轉換的安全性需要開發者自己保證(這可以理解爲保證數據的精度,即程序員能不能保證自己想要的程序安全),如在把int轉換爲char時,如果char沒有足夠的比特位來存放int的值(int>127或int<-127時),那麼static_cast所做的只是簡單的截斷,及簡單地把int的低8位複製到char的8位中,並直接拋棄高位。

(2)把空指針轉換成目標類型的空指針

(3)把任何類型的表達式類型轉換成void類型

(4)用於類層次結構中父類和子類之間指針和引用的轉換。

對於以上第(4)點,存在兩種形式的轉換,即上行轉換(子類到父類)和下行轉換(父類到子類)。對於static_cast,上行轉換時安全的,而下行轉換時不安全的,爲什麼呢?因爲static_cast的轉換時粗暴的,它僅根據類型轉換語句中提供的信息(尖括號中的類型)來進行轉換,這種轉換方式對於上行轉換,由於子類總是包含父類的所有數據成員和函數成員,因此從子類轉換到父類的指針對象可以沒有任何顧慮的訪問其(指父類)的成員。而對於下行轉換爲什麼不安全,是因爲static_cast只是在編譯時進行類型堅持,沒有運行時的類型檢查,具體原理在dynamic_cast中說明。

二、dynamic_cast關鍵字(運行時類型檢查)

用法:同static_cast

dynamic_cast主要用於類層次結構中父類和子類之間指針和引用的轉換,由於具有運行時類型檢查,因此可以保證下行轉換的安全性,何爲安全性?即轉換成功就返回轉換後的正確類型指針,如果轉換失敗,則返回NULL,之所以說static_cast在下行轉換時不安全,是因爲即使轉換失敗,它也不返回NULL。

對於上行轉換,dynamic_cast和static_cast是一樣的。

對於下行轉換,說到下行轉換,有一點需要了解的是在C++中,一般是可以用父類指針指向一個子類對象,如parent* P1 = new Children(); 但這個指針只能訪問父類定義的數據成員和函數,這是C++中的靜態聯翩,但一般不定義指向父類對象的子類類型指針,如Children* P1 = new parent;這種定義方法不符合生活習慣,在程序設計上也很麻煩。這就解釋了也說明了,在上行轉換中,static_cast和dynamic_cast效果是一樣的,而且都比較安全,因爲向上轉換的對象一般是指向子類對象的子類類型指針;而在下行轉換中,由於可以定義就不同了指向子類對象的父類類型指針,同時static_cast只在編譯時進行類型檢查,而dynamic_cast是運行時類型檢查,則需要視情況而定。

相關文章:

https://blog.csdn.net/hahaha_val/article/details/79384723

https://blog.csdn.net/qq_26849233/article/details/62218385

https://www.cnblogs.com/rednodel/p/5800142.html

http://www.cnblogs.com/jerry19880126/archive/2012/08/14/2638192.html

 

1.12 STL標準庫六大組件

STL提供六大組件,彼此可以組合套用:

  1. 容器(Containers):各種數據結構,如:vector、list、deque、set、map。用來存放數據。從實現的角度來看,STL容器是一種class template。
    1. https://www.cnblogs.com/geekpaul/tag/STL/
    2. https://blog.csdn.net/caojunhao123/article/details/11907857
  2. 算法(algorithms):各種常用算法,如:sort、search、copy、erase。從實現的角度來看,STL算法是一種 function template。
  3. 迭代器(iterators):容器與算法之間的膠合劑,是所謂的“泛型指針”。共有五種類型,以及其他衍生變化。從實現的角度來看,迭代器是一種將 operator*、operator->、operator++、operator- - 等指針相關操作進行重載的class template。所有STL容器都有自己專屬的迭代器,只有容器本身才知道如何遍歷自己的元素。原生指針(native pointer)也是一種迭代器。
    1. https://www.cnblogs.com/wxquare/p/4699429.html
    2. https://www.cnblogs.com/maluning/p/8570717.html
    3. https://www.cnblogs.com/bhlsheji/p/4842539.html

4、 仿函數(functors):行爲類似函數,可作爲算法的某種策略(policy)。從實現的角度來看,仿函數是一種重載了operator()的class或class template。一般的函數指針也可視爲狹義的仿函數。

5、 配接器(adapters);一種用來修飾容器、仿函數、迭代器接口的東西。例如:STL提供的queue 和 stack,雖然看似容器,但其實只能算是一種容器配接器,因爲它們的底部完全藉助deque,所有操作都由底層的deque供應。改變 functors接口者,稱爲function adapter;改變 container 接口者,稱爲container adapter;改變iterator接口者,稱爲iterator adapter。

  1. https://blog.csdn.net/jakemiao/article/details/24815713
  2. https://www.cnblogs.com/yongpan/p/7966865.html

6、 配置器(allocators):負責空間配置與管理。從實現的角度來看,配置器是一個實現了動態空間配置、空間管理、空間釋放的class template。

這六大組件的交互關係:container(容器) 通過 allocator(配置器) 取得數據儲存空間,algorithm(算法)通過 iterator(迭代器)存取 container(容器) 內容,functor(仿函數) 可以協助 algorithm(算法) 完成不同的策略變化,adapter(配接器) 可以修飾或套接 functor(仿函數)。

 

二、cocos2dx部分

2.1 Cocos2d-x內存管理機制之release和autorelease

·  autorelease內部是成對的retain()和release();

·  retain對++m_uReference,release對--m_uReference,在retain和release中間++m_uAutoReleaseCount。

·  autorelease自動釋放對象資源(自動回收池通常在遊戲每一幀的結束時開始release清理),此時對應m_uReference爲1,m_uAutoReleaseCount爲1,release釋放資源。

·  release釋放資源,此時如果--m_uReference爲0,就釋放對象資源,且m_uAutoReleaseCount大於0,就再清自動回收池中佔位信息。

相關文章:

https://www.cnblogs.com/steven66/p/5235790.html

https://blog.csdn.net/liaohongwei/article/details/21552785

https://blog.csdn.net/asforasiare/article/details/48667079

https://www.cnblogs.com/gongjiangzhixin/p/5391225.html

https://www.cnblogs.com/sniperHW/p/3789837.html

https://www.cnblogs.com/sniperHW/p/3789837.html

https://www.cnblogs.com/gongjiangzhixin/p/5391225.html

 

三、Python部分

3.1 Python中__get__、__getattr__、__getitem__、__getattribute__之間的差異與聯繫

相關文章:

https://blog.csdn.net/qianguozheng/article/details/50396776

https://blog.csdn.net/yiifaa/article/details/78068962

 

3.2 Python中 dict.items() dict.iteritems()區別

Python 文檔解釋:

  • dict.items(): Return a copy of the dictionary’s list of (key, value) pairs.
  • dict.iteritems(): Return an iterator over the dictionary’s (key, value) pairs.

dict.items()返回的是一個完整的列表,而dict.iteritems()返回的是一個生成器(迭代器)。

dict.items()返回列表list的所有列表項,形如這樣的二元組list:[(key,value),(key,value),...],dict.iteritems()是generator, yield 2-tuple。相對來說,前者需要花費更多內存空間和時間,但訪問某一項的時間較快(KEY)。後者花費很少的空間,通過next()不斷取下一個值,但是將花費稍微多的時間來生成下一item。

相關文章:

https://blog.csdn.net/u012542422/article/details/52052877

 

3.3Python中range和xrange的區別

相關文章:

https://www.cnblogs.com/wswang/p/5501565.html

 

3.4 python中繼承的作用以及多重繼承的執行順序

相關文章:

https://blog.csdn.net/aydfzmb/article/details/53287780

https://www.jianshu.com/p/71c14e73c9d9

 

3.5  @classmethod 和 @staticmethod的區別

相關文章:

https://www.jb51.net/article/126598.htm

 

3.6 Python迭代對象、迭代器、生成器

  • 容器是一系列元素的集合,str、list、set、dict、file、sockets對象都可以看作是容器,容器都可以被迭代(用在for,while等語句中),因此他們被稱爲可迭代對象。
  • 可迭代對象實現了__iter__方法,該方法返回一個迭代器對象。
  • 迭代器持有一個內部狀態的字段,用於記錄下次迭代返回值,它實現了__next__和__iter__方法,迭代器不會一次性把所有元素加載到內存,而是需要的時候才生成返回結果。
  • 生成器是一種特殊的迭代器,它的返回值不是通過return而是用yield。

相關文章:

https://blog.csdn.net/u014745194/article/details/70176117

https://foofish.net/iterators-vs-generators.html

https://www.jb51.net/article/73939.htm

 

 

 

 

【注】:本文主要是將相關的比較有含金量的文章進行篩選並進行歸納以饗大家,版權歸各原作者所有。

 

 

 

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