By SmartPtr(http://www.cppblog.com/SmartPtr/)
近一年以來,AUTODESK的面試題在網上是鬧的沸沸揚揚,作為一個名企,這是可以理解的,況且其面試題質量也是不錯的。抽一些閒暇時間,把網上傳的比較多的70道題簡單的解答了一遍,不為別的,只為再熟悉一下在大學學過的一些基礎知識。希望對大家有用。當然,這只是我的個人解答,有什麼不對的或者需要補充的,大家儘管提上來,好的話我加上去的。。。
1. 在類的普通成員函式中呼叫虛擬函式,情況是怎麼樣的?(物件、引用、指標)
多型, 事實上,這是 Template Method模式的關鍵
2. 關於成員變數初始化順序,幾個有依賴關係的成員變數要初始化,讓寫出建構函式。
在初始化列表中,成員變數的初始化順序是其在類中宣告順序,而非列表中的順序。
3. 寫一個雙連結串列。
Struct ListNode
{
int nData;
ListNode* pPreviousNode;
ListNode* pNextNode;
}
一般連結串列都會有一個表頭節點與指向表頭節點的頭指標, 應該會提供列表介面, 按此資料結構實現即可。
4. 寫個is-a和has-a。
這個比較簡單
Class Pet{};
Class Dog: public Pet{};
Class Boy{Pet* m_pPet;};
5. struct vs. class.
1)預設訪問屬性, struct為public, class為private
2) 預設繼承屬性,struct為public, class為private
3)class可以用來宣告模板引數,而struct不能
6. 稱8個小球的問題
沒題
7. stl 裡面vector的實現(內部空間的申請與分配)
Vector中文名字是動態陣列, 其內部資料結構就是一個陣列, 但是在陣列元素不夠用的時候,就要動態的重新分配, 一般是現在大小的兩倍, 然後把原陣列的內容拷貝過去。所以, 在一般情況下, 其訪問速度同一般陣列, 只有在重新分配發生時, 其效能才會下降
8. struct /class的區別
重複了
9. 為什麼要用struct
成員的預設屬性不同,用struct的話,主要是作為資料的集合。
10. 怎樣使一個class不能被例項化
1,建構函式私有化,2,抽象類
11. 私有繼承和public繼承的區別。
私有繼承: 只繼承實現,不繼承實現 has-a
公有繼承:繼承介面與實現 is-a
12. void *p的問題
不能++
13. 引用和指標的區別與聯絡。引用是否可以更改
聯絡: 支援多型,可以用來引用同一物件
區別:指標可以為NULL, 引用不可以; 指標可以重賦值, 引用不可以;
14. windows程式設計基礎,執行緒與程式的區別
程式是一系列靜態的指令序列
程式是程式的一次動態執行,程式其實是一個資源的容器,包括一個私有的虛擬地址空間,一些初始的程式碼與資料, 一些系統資源的控制程式碼等
執行緒是一個程式中的執行體, 一般包括CPU暫存器狀態,兩個棧(核心模式,使用者模式)以及一個TLS(Thread-Local Storage)等
15. com+是否熟悉
COM+是COM技術的延伸與發展, 它包括了所有COM的基本功能(基於介面的程式設計模型,基本元件服務),並組合了DCOM(使元件技術延伸到了分散式領域)和MTS-Microsoft Transaction Server(提供了伺服器端的元件管理與配置管理),並新增了一些服務:負載平衡,記憶體資料庫,事件模型,佇列服務等,主要用於Windows DNA(Distributed interNet Application Architecture)三層結構的中間層。
16. 簡述一下hash演算法
雜湊表的目的是表查詢插入修改能夠達到O(1)的演算法複雜度, 通過對key編碼來確定其儲存地址來實現, 當不同的key得到相同的編碼時,便需要進行衝突檢測與處理,一般方法有除留餘數法, 線性探測法,平方探測法, 這使其無法真正達到O(1)
17. 一個32位的資料,怎樣找到最左邊的一個1?
如果是在最左位,這個數是負數,否則的話,左移一位,看是否變成負數,這是O(n)的演算法, 也可以用一個模板去與,並不斷改變這個模板
O(n/2)的演算法:二分方式查詢 ???
18. 一個4*4的格子,填入1~15 然後給個目標狀態,怎樣去搜尋。
比如:
1 2 3 6
0 4 5 7
8 9 10 11
12 13 14 14
再給出個最終的狀態 (隨便都可以)
0 表示一個空格,可以移動,有點像拼圖;
人工智慧的教材上用的應該就是這個例子,用A*演算法,它既不是廣度搜尋,也不是深度搜尋,而是一種啟發式搜尋,在進行下一步搜尋之前,會用一個估價函式來對後面的節點評分, 取評分最優的進行下一步搜尋,如果找不到結果,回溯。對於本題,用曼哈頓距離作為評分標準是個不錯的選擇。
19. 給你100萬個資料,資料的值在0~65535之間 用最快的速度排序
多關鍵字基數排序MSD(MOST SIGNIFICANT DIGIT FIRST)
20. 如果我們的一個軟體產品,使用者回覆說:執行速度很慢,你怎麼處理?
詢問其Workflow, 使用者的硬體環境
21. 八皇后問題,詳述解法 (八皇后問題說的是在8*8國際象棋棋盤上,要求在每一行放置一個皇后,且能做到在豎方向,斜方向都沒有衝突)
回溯法
22. kmp快速匹配演算法 ---不算輕鬆的搞定
普通的模式匹配演算法,一旦不匹配,模式串右移一位;但是其實根據一直條件,我們可以算出應該向右移幾位以避免不必要的比較;演算法實現比較曲折
23. 無向圖中兩點間最短路問題 ---偉大的迪傑克斯拉演算法
假設一共有N個節點, 需要一個一維陣列Previous[N]來記錄前一個節點序號;一個一維陣列TotalLength[N]來記錄從原點到當前節點最短路徑;一個二維陣列Weights[N][N]來記錄各點之間邊的權重(如果存在), 然後從源點到終點進行深度搜尋或廣度搜尋, 按以下規則:搜尋到某個節點b時,假設其前一個節點為a, 把TotalLength[a] + Weights[a][b]與TotalLength[b]相比較,如果小於TotalLength[b], 則TotalLength[b] = TotalLength[a] + Weights[a][b], Previous[b] = a; 反之則不做任何操作。這樣到搜尋結束後, 從Previous[N]陣列中就能得到整條最短路徑了
24. 空間中任意給兩個向量,求角平分線
先單位化, 假設單位化後結果為nv1, nv2, 則角平分線為(nv1+nv2) / 2
25. 什麼是平衡樹
左右子樹都是平衡樹,且高度相差不超過1的有序二叉樹
26. 哈夫曼編碼問題
理論基礎:霍夫曼樹是帶權路徑長度(WPL:Weighted Path Length)最小的二叉樹,它不一定是完全二叉樹,應該是權值大的外結點離根節點最近的擴充二叉樹。霍夫曼編碼是為了實現資料的最小冗餘編碼,是資料壓縮學的基礎。 它根據字元在電文中出現的頻率為權值,構造霍夫曼樹,左為0, 右為1. 其有兩個效果,一是保證電文有最短的編碼,二是字元間不需要分隔符,因為不同的字元必定有不同的開頭(成為字首編碼)。
27. 有向圖求環
以該節點為源點與終點嗎進行深度優先或廣度優先搜尋
28. .給n個點,求凸包問題
凸包(convex hull)是指一個最小凸多邊形,滿足這N個點都在多邊形上,或其內。演算法描述:
求出最右的那個點作為凸多邊形的一個頂點(P0),遍歷其他所有點(Pi), 如果其他點都在向量P0Pi的同一側,則Pi也為凸多邊形的頂點。
29. 四則運算(給一個字首表示式(波蘭式)或字尾表示式(逆波蘭式),然後求解;給一箇中綴表示式)
+*-CDBA -/EF---------------------> A+B*(C-D)-E/F 字首-中綴
操作符進棧,一個變數tmp放上一個中間運算元(運算結果),遇到運算元檢查tmp是否為空, 空的話取兩個運算元,不空的話取一個運算元,另一個就是tmp了,操作符出棧運算,結果放入tmp中,如果是操作符,tmp清空
ABCD-*+EF/- ---------------------> A+B*(C-D)-E/F 字尾-中綴
運算元進棧,遇到操作符,兩個運算元出棧,計算結果入棧
30. STL中container有哪些?
序列容器: vector, list, deque, bitset
關聯容器: set, multiset, map, multimap
適配容器:stack, queue, priority_queue
類容器: string, valarray, bitset
擴充套件容器:hash_set, hash_multiset, hash_map, hash_multimap
31. map中的資料儲存方式是什麼?
紅黑樹, 是一種平衡二叉搜尋樹, 具有良好的最壞情況執行時間(統計效能好與AVL樹)
32. map和hashmap有什麼區別?
內部資料結構不同, map是紅黑樹,hashmap是雜湊表
33. hashmap是標準庫中的嗎?
不是的,但在SGI stl與vc2005中都提供了。
34. vector中的erase方法跟algorithm的remove有什麼區別?
vector中erase是真正刪除了元素, 迭代器訪問不到了。 algorithm中的remove只是簡單的把要remove的元素移到了容器最後面,迭代器還是可以訪問到的。因為algorithm通過迭代器操作,不知道容器的內部結構,所以無法做到真正刪除。
35. object是什麼?
具有內部狀態,以及操作的 軟體構造,用來表示真實存在(物理上或概念上)的物件
36. C++中如何阻止一個類被例項化?
純虛擬函式;建構函式私有化(友元)
37. 一般在什麼時候建構函式被宣告成private呢?
singleton模式; 阻止某些操作(如阻止拷貝構造)
38. 什麼時候編譯器會生成預設的copy constructor呢?
使用者沒有自定義copy constructor;在程式碼中使用到了copy constructor;
39. 如果你已經寫了一個建構函式,編譯器還會生成copy constructor嗎?
如果我寫的是copy constructor, 不會
如果我寫的不是copy constructor, 同38
40. 為什麼說如果一個類作為基類,則它的解構函式要宣告成virtual的?
因為,如果delete一個基類的指標時, 如果它指向的是一個子類的物件,那麼解構函式不為虛就會導致無法呼叫子類解構函式,從而導致資源洩露。 當然,另一種做法是將基類解構函式設為protected.
41. inline的函式和#define有什麼區別?什麼時候會真的被inline,什麼時候不會呢?
1) 巨集是在預編譯階段簡單文字替代, inline在編譯階段實現展開
2)巨集肯定會被替代,而複雜的inline函式不會被展開
3)巨集容易出錯(運算順序),且難以被除錯,inline不會
4)巨集不是型別安全,而inline是型別安全的,會提供引數與返回值的型別檢查
當出現以下情況時inline失敗
函式size太大
inline虛擬函式
函式中存在迴圈或遞迴
函式呼叫其他inline函式
42. 如果把一個類的成員函式寫在類的宣告中是什麼意思?
inline此函式 (inline與template類似, 必須在.h中實現)
43. public繼承和private繼承有什麼架構上的區別?
public是is-a的關係,繼承介面與實現
private是has-a的關係 ,只繼承實現
44. 在多繼承的時候,如果一個類繼承同時繼承自class A和class B,而class A和B中都有一個函式叫foo(),如何明確的在子類中指出override哪個父類的foo()?
首先,foo在A,B總應該都是虛擬函式,否則就直接覆蓋了,就沒有這個問題了;其次,這個問題從語法角度來看似乎是無法解決。因為我們不能改原有設計(不然也沒這個問題了:)),所有隻好從extend來考慮:
class EA: public class A
{
public:
virtual void foo(){fooA();}
private:
virtual void fooA() = 0;
}
class EB: public class B
{
public:
virtual void foo(){fooB();}
private:
virtual void fooB() = 0;
}
這樣, 我就可以override不同的函式來達到這個目的了
class AB: public EA, pubic EB
{
private:
virtual void fooA(){}
virtual void fooB(){}
}
45. 虛擬繼承的語法是什麼?
A
/ \
B C
\ /
D
class A{};
class B: virtual public A{};
class C: virtual public A{};
class D: public B, public C{};
46. 部分模版特例化和全部模版特例化有什麼區別?
偏特化只使用於類别範本,而全特化適用與函式模板,類别範本。
偏特化的結果還是一個模板,而全特化的結果是一個具體的型別。
47. 編一個函式,使一個單項鍊錶轉置。
應該是逆序吧
這個小演算法竟然花了我不少時間,沒有測試過的:
{
int data;
ListNode* next;
};
void ReverseList(ListNode* p)
{
ListNode* p0 = NULL;
ListNode* p1 = p->next;
ListNode* p2 = p1 ? p1->next : NULL;
// 三個指標,分別表示當前處理節點,前一節點與後一節點
// 複用頭節點的next來儲存節點
while (NULL != p2)
{
p->next = p2->next; //暫存
p1->next = p0; //逆轉
p2->next = p1;
p0 = p1; //往下一個節點
p1 = p2;
p2 = p->next;
}
p->next = p1; //p1末元素變為首元素,鏈到頭節點上
}
48. 拆解一個整數,比如4,可以拆解成4=3+1;4=2+2;4=2+1+1;4=1+1+1+1
首先,對一個數進行拆分後,可能又要對最後一個因子進行拆分,所以要用遞迴;其次,第n+1個因子是小於等於第n個因子的;再者,對最後一個因子,我可以直接輸出,也可以繼續拆分。
演算法如下:
{
for (int i = 0; i < num; ++i)
{
printf("%d ", res[i]);
}
printf("\n");
}
// n表示總數,m表示最大因子
void split(int n, int m)
{
static int res[100]; //儲存結果
static int num = -1; //當前因子下標
num++;
//遞迴終止條件,為0不可再分,直接輸出
if(0 == n)
{
print(res, num+1);
num--;
return;
}
else
{
if(n == m)
{
// 不拆,直接輸出
res[num] = m;
print(res,num+1);
num--;
}
else
{
// 拆分出第一個
res[num] = m;
n = n-m;
//最大因子不可能大於總數
if(m>n) m = n;
// 迴圈,第二個因子可以繼續拆分,而且按照最大因子不同可以拆分成多個
for (int i = m; i>=1; --i)
{
split(n, i);
}
num--;
}
}
}
void Split(int n)
{
for (int i = n-1; i>=1; i--)
{
split(n, i);
}
}
唉,老了,這個小東西搞了我N久的。。。。
49. 不用庫函式,實現strcpy或者memcpy等函式
一個位元組一個位元組的拷過去吧,但是要考慮源記憶體與目標記憶體的重疊。
50. 行內函數的作用和缺點
把程式碼直接插入到呼叫的地方,可以減少函式呼叫的次數,但是會增加程式碼的size,還有,如果內聯失敗,在每個呼叫的obj裡,都會產生一份該函式的拷貝,這樣既沒有怎麼減少程式碼的size,又沒有減少函式的呼叫,賠了夫人又折兵。。。
51. 指標和引用的區別
指標可以不初始化,引用必須初始化
指標可以是NULL,而引用必須引用一個實在的物件
指標可以重指向其他物件,引用一旦初始化,便不再改變
52. 友元的意義
使被宣告為友元的函式或類可以訪問某個類的非共有成員。
53. 虛擬函式的意義
實現多型
54. Overload, Overwrite, Override 各自的特點和意義
Overload: 函式過載(名字相同,引數不同)
Overwrite:覆蓋
Override: 虛擬函式過載
55. 標頭檔案中的ifndef/define/endif 幹什麼用?
防止該標頭檔案被重複引用。
56. #i nclude <filename.h> 和#i nclude “filename.h” 有什麼區別?
#i nclude <filename.h>: 從標準庫路徑去尋找該檔案,對於VC來說,應該還包括VC環境設定選項中的包含目錄以及工程屬性中指定的目錄
#i nclude “filename.h”:先在當前目錄查詢,如果找不到,按上面那種方式尋找
57. 在C++ 程式中呼叫被C 編譯器編譯後的函式,為什麼要加extern “C”?
C++語言支援函式過載,C 語言不支援函式過載。函式被C++編譯後在庫中的名字與C 語言的不同。C++提供了C 連線交換指定符號extern“C”來解決名字匹配問題
58. 一個類有基類、內部有一個其他類的成員物件,建構函式的執行順序是怎樣的?
先執行基類的(如果基類當中有虛基類,要先執行虛基類的,其他基類則按照宣告派生類時的順序依次執行),再執行成員物件的,最後執行自己的。
59. 請描述一個你熟悉的設計模式
這個看你熟悉什麼了。singleton最簡單了,template method用的最多了,bridge挺炫的,command吹吹undo,redo也不錯。。。。。
60. 在UML 中,聚合(aggregation)和組合(composition)有什麼區別?
其實從名字就能分別出來了。
聚合表示只是簡單的聚聚,沒什麼本質的聯絡,所以這些物件的生存時間也就沒什麼關係了;
組合表示了更加緊密的一種關係,這些物件有著共同的生存期。
一個典型的例子是孫悟空,手臂,金箍棒的關係。。。。
61. C#和C++除了語法上的差別以外,有什麼不同的地方?
C++是直接生成可執行程式碼,而C#是先生成中間程式碼,等到第一次執行時,才由JIT(Just In Time)生成可執行的機器碼。
還有就是(1) c#有垃圾自動回收機制,程式設計師不用擔心物件的回收。(2)c#嚴禁使用指標,只能處理物件。如果希望使用指標,則僅可在unsafe 程式塊中能使用指標。(3)c#只能單繼承。(4)必須通過類名訪問靜態成員。不能像C++中那樣,通過物件訪問靜態成員。(5)在子類中重寫父類的虛擬函式時必須用關鍵字override,覆蓋父類的方法要用關鍵字new
62. New delete與malloc free 的區別
對於類,New 和delete會呼叫構造,解構函式
new,delete都是能感知到型別的。new返回一個制定的型別,delete刪除一個指定的型別,從而不用給定size。而malloc與free都是處理void型別的。用時時必須經過強制型別轉換。
63. #define DOUBLE(x) x+x,i = 5*DOUBLE(10);i是多少?正確的宣告是什麼?
I = 5*10+10 = 60 60
正確的宣告是:
#define DOUBLE(x) ((x)+(x))
64. 有哪幾種情況只能用intialization list 而不能用assignment?
當類中含有const、reference 成員變數;基類的建構函式都需要引數;類中含有其他類的成員物件,而該類的建構函式都需要引數。
65. C++是不是型別安全的?
不是。兩個不同型別的指標之間可以強制轉換。C#是型別安全的。
66. main 函式執行以前,還會執行什麼程式碼?
全域性物件的建構函式會在main 函式之前執行。
67. 描述記憶體分配方式以及它們的區別。
(1)從靜態儲存區域分配。記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個執行期間都存在。例如全域性變數,static 變數。
(2) 在棧上建立。在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時這些儲存單元自動被釋放。棧記憶體分配運算內建於處理器的指令集。用的是cache,速度較快但容量較小。
(3) 從堆上分配,亦稱動態記憶體分配。程式在執行的時候用malloc 或new 申請任意多少的記憶體,程式設計師自己負責在何時用free 或delete 釋放記憶體。動態記憶體的生存期由我們決定,使用非常靈活,但問題也最多。
(4)文字常量區, 如char* p = "hello, world"就是一個例子,其記憶體也在程式編譯的時候就已經分配好?
一個程式除了上面這些,還有一個(5)程式程式碼區了。
68. 比較一下C++中static_cast 和 dynamic_cast 的區別。
Static_cast可以顯式的做一些自動轉換,如一些int, char一些基礎型別的轉換,以及指標之間的轉換。但是其不保證安全性。Dynamic_cast主要作用其實在於把一個基類指標轉化為子類指標,因為這個基類指標真正指向的不一定是我們想轉換的型別的物件,所以轉換可能失敗,dynamic_cast能夠知道失敗而返回NULL,而static_cast就沒那麼聰明瞭,原因是dynamic_cast會利用rtti去查詢該轉換是否可行.(耗費時間多點。)
69. 當一個類A 中沒有生命任何成員變數與成員函式,這時sizeof(A)的值是多少,如果不是零,請解釋一下編譯器為什麼沒有讓它為零。
不為零,不同的物件應該有不同的地址,假設我宣告一個A的陣列A a[2],如果為零,那麼a[0]和a[1]的地址豈不相同了
70. 已知兩個連結串列head1 和head2各自有序,請把它們合併成一個連結串列依然有序,要求用遞迴方法進行。
歸併排序,應該比較簡單。要注意的是如果一個連結串列為空,那麼可以簡單的把另一個直接鏈過去了。
注:有一部分題的答案是在網上看到的題中就包含了的。