1. 程式人生 > 實用技巧 >軟體開發面試題目

軟體開發面試題目

目錄

軟體開發面試題目

1.類的繼承

2.類的大小

3.多型性

4.什麼是memory leak,也就是記憶體洩漏

5.new和malloc的區別

6.併發(concurrency)和並行(parallelism)

7.多執行緒的同步,鎖的機制

8.請你來說一下堆和棧的區別

9.new與malloc的10點區別

10.排序演算法時間複雜度

11.記憶體碎片

12.類和結構體的區別

13.野指標

14.虛擬函式和純虛擬函式

15.值傳遞,指標傳遞,引用傳遞


軟體開發面試題目

1.類的繼承

Private: 只能被本類成員函式或友元函式訪問。

Public: 可以被本類和其他類以及程式中的其他函式訪問。

Protected: 可以由本類成員函式訪問,也可以由派生類的成員函式訪問,但不允許程式中其它函式訪問。

2.類的大小

  1. 僅包含一般成員函式(即沒有虛擬函式),不含成員變數時,執行結果和(一)是一樣的,系統也只是為物件建立了1個位元組的佔位符。因此,我們可以得出結論是,一般成員函式不會對類的大小造成影響。
  2. 依次繼承的三個類中含有相同數量,相同型別的一般成員變數(不含靜態成員變數)。此種情況下,類物件大小=基類物件大小+自身成員大小。A當中三個字元變數3個位元組,一個整形變數4個位元組,考慮記憶體對齊因素(預設為4),A類物件大小為8。B類物件大小為A類物件大小基礎上再加8,C類物件大小在B類物件大小基礎上再加8。
  3. 可以看到,類物件大小沒有因為增加了靜態成員而變化。因為靜態成員是屬於類成員共有的,不單獨屬於任何一個物件,對靜態成員的儲存不會選擇在某個物件空間,而是存在於堆當中,因此不會對物件的大小造成影響。

3.多型性

  1. 靜態多型性(編譯時多型):指呼叫相同名稱的函式時,編譯器在編譯階段就能夠根據函式引數型別的不同和個數的不同來確定要呼叫的函式,(函式過載和運算子過載)。
  2. 動態多型性(執行時多型):指在函式名,函式引數和返回型別都相同的情況下,在編譯階段不能確定要呼叫的函式,只能在程式執行時才能確定要呼叫的函式。動態多型性通過繼承和虛擬函式實現。(只有通過基類的指標呼叫虛擬函式時,對該虛擬函式的呼叫才能實現動態多型性。通過基類的物件呼叫虛擬函式時,系統採用的是靜態聯編。

4.什麼是memory leak,也就是記憶體洩漏

參考回答:

記憶體洩漏(memory leak)是指由於疏忽或錯誤造成了程式未能釋放掉不再使用的記憶體的情況。記憶體洩漏並非指記憶體在物理上的消失,而是應用程式分配某段記憶體後,由於設計錯誤,失去了對該段記憶體的控制,因而造成了記憶體的浪費。

記憶體洩漏的分類:

1. 堆記憶體洩漏 (Heap leak)。對記憶體指的是程式執行中根據需要分配通過malloc,realloc new等從堆中分配的一塊記憶體,再是完成後必須通過呼叫對應的 free或者delete 刪掉。如果程式的設計的錯誤導致這部分記憶體沒有被釋放,那麼此後這塊記憶體將不會被使用,就會產生Heap Leak.

2. 系統資源洩露(Resource Leak)。主要指程式使用系統分配的資源比如 Bitmap,handle ,SOCKET等沒有使用相應的函式釋放掉,導致系統資源的浪費,嚴重可導致系統效能降低,系統執行不穩定。

3. 沒有將基類的解構函式定義為虛擬函式。當基類指標指向子類物件時,如果基類的解構函式不是virtual,那麼子類的解構函式將不會被呼叫,子類的資源沒有正確是釋放,因此造成記憶體洩露

5.new和malloc的區別

參考回答:

1、new分配記憶體按照資料型別進行分配,malloc分配記憶體按照指定的大小分配;

2、new返回的是指定物件的指標,而malloc返回的是void*,因此malloc的返回值一般都需要進行型別轉化。

3、new不僅分配一段記憶體,而且會呼叫建構函式,malloc不會。

4、new分配的記憶體要用delete銷燬,malloc要用free來銷燬;delete銷燬的時候會呼叫物件的解構函式,而free則不會。

5、new是一個操作符可以過載,malloc是一個庫函式。

6、malloc分配的記憶體不夠的時候,可以用realloc擴容。擴容的原理?new沒用這樣操作。

7、new如果分配失敗了會丟擲bad_malloc的異常,而malloc失敗了會返回NULL。

8、申請陣列時: new[]一次分配所有記憶體,多次呼叫建構函式,搭配使用delete[],delete[]多次呼叫解構函式,銷燬陣列中的每個物件。而malloc則只能sizeof(int) * n。

6.併發(concurrency)和並行(parallelism)

參考回答:

併發(concurrency):指巨集觀上看起來兩個程式在同時執行,比如說在單核cpu上的多工。但是從微觀上看兩個程式的指令是交織著執行的,你的指令之間穿插著我的指令,我的指令之間穿插著你的,在單個週期內只運行了一個指令。這種併發並不能提高計算機的效能,只能提高效率。

並行(parallelism):指嚴格物理意義上的同時執行,比如多核cpu,兩個程式分別執行在兩個核上,兩者之間互不影響,單個週期內每個程式都運行了自己的指令,也就是運行了兩條指令。這樣說來並行的確提高了計算機的效率。所以現在的cpu都是往多核方面發展。

7.多執行緒的同步,鎖的機制

同步的時候用一個互斥量,在訪問共享資源前對互斥量進行加鎖,在訪問完成後釋放互斥量上的鎖。對互斥量進行加鎖以後,任何其他試圖再次對互斥量加鎖的執行緒將會被阻塞直到當前執行緒釋放該互斥鎖。如果釋放互斥鎖時有多個執行緒阻塞,所有在該互斥鎖上的阻塞執行緒都會變成可執行狀態,第一個變為執行狀態的執行緒可以對互斥量加鎖,其他執行緒將會看到互斥鎖依然被鎖住,只能回去再次等待它重新變為可用。在這種方式下,每次只有一個執行緒可以向前執行。

8.請你來說一下堆和棧的區別

參考回答:

1)申請方式:

棧由系統自動分配和管理,堆由程式設計師手動分配和管理。

2)效率:

棧由系統分配,速度快,不會有記憶體碎片。

堆由程式設計師分配,速度較慢,可能由於操作不當產生記憶體碎片。

3)擴充套件方向

棧從高地址向低地址進行擴充套件,堆由低地址向高地址進行擴充套件。

4)程式區域性變數是使用的棧空間,new(delete)/malloc(free)動態申請的記憶體是堆空間,函式呼叫時會進行形參和返回值的壓棧出棧,也是用的棧空間。

9.new與malloc的10點區別

1. 申請的記憶體所在位置

new操作符從自由儲存區(free store)上為物件動態分配記憶體空間,而malloc函式從堆上動態分配記憶體。自由儲存區是C++基於new操作符的一個抽象概念,凡是通過new操作符進行記憶體申請,該記憶體即為自由儲存區。而堆是作業系統中的術語,是作業系統所維護的一塊特殊記憶體,用於程式的記憶體動態分配,C語言使用malloc從堆上分配記憶體,使用free釋放已分配的對應記憶體。

2.返回型別安全性

new操作符記憶體分配成功時,返回的是物件型別的指標,型別嚴格與物件匹配,無須進行型別轉換,故new是符合型別安全性的操作符。而malloc記憶體分配成功則是返回void * ,需要通過強制型別轉換將void*指標轉換成我們需要的型別。

型別安全很大程度上可以等價於記憶體安全,型別安全的程式碼不會試圖方法自己沒被授權的記憶體區域。關於C++的型別安全性可說的又有很多了。

3.記憶體分配失敗時的返回值

new記憶體分配失敗時,會丟擲bac_alloc異常,它不會返回NULL;malloc分配記憶體失敗時返回NULL。

int*a = (int*)malloc( sizeof(int));

int* a = newint();

是否需要指定記憶體大小

使用new操作符申請記憶體分配時無須指定記憶體塊的大小,編譯器會根據型別資訊自行計算,而malloc則需要顯式地指出所需記憶體的尺寸。

class A{...}

A * ptr = new A;

A * ptr = (A *)malloc(sizeof(A)); //需要顯式指定所需記憶體大小sizeof(A);

是否呼叫建構函式/解構函式

使用new操作符來分配物件記憶體時會經歷三個步驟:

第一步:呼叫operator new 函式(對於陣列是operator new[])分配一塊足夠大的,原始的,未命名的記憶體空間以便儲存特定型別的物件。

第二步:編譯器執行相應的建構函式以構造物件,併為其傳入初值。

第三部:物件構造完成後,返回一個指向該物件的指標。

使用delete操作符來釋放物件記憶體時會經歷兩個步驟:

第一步:呼叫物件的解構函式。

第二步:編譯器呼叫operator delete(或operator delete[])函式釋放記憶體空間。

總之來說,new/delete會呼叫物件的建構函式/解構函式以完成物件的構造/析構。而malloc則不會。可以看下例子:

class A

{

public:

A() :a(1), b(1.11){}

private:

int a;

double b;

};

int main()

{

A * ptr = (A*)malloc(sizeof(A));

return 0;}

6.對陣列的處理

C++提供了new[]與delete[]來專門處理陣列型別:

A * ptr = new A[10];//分配10個A物件

使用new[]分配的記憶體必須使用delete[]進行釋放:

delete [] ptr;

new對陣列的支援體現在它會分別呼叫建構函式函式初始化每一個數組元素,釋放物件時為每個物件呼叫解構函式。注意delete[]要與new[]配套使用,不然會找出陣列物件部分釋放的現象,造成記憶體洩漏。

至於malloc,它並知道你在這塊記憶體上要放的陣列還是啥別的東西,反正它就給你一塊原始的記憶體,在給你個記憶體的地址就完事。所以如果要動態分配一個數組的記憶體,還需要我們手動自定陣列的大小:

int * ptr = (int *) malloc( sizeof(int) );//分配一個10個int元素的陣列

10.排序演算法時間複雜度

11.記憶體碎片

記憶體碎片通常分為內部碎片和外部碎片:

1. 內部碎片是由於採用固定大小的記憶體分割槽,當一個程序不能完全使用分給它的固定記憶體區域時就產生了內部碎片,通常內部碎片難以完全避免;

2. 外部碎片是由於某些未分配的連續記憶體區域太小,以至於不能滿足任意程序的記憶體分配請求,從而不能被程序利用的記憶體區域。

現在普遍採用的段頁式記憶體分配方式就是將程序的記憶體區域分為不同的段,然後將每一段由多個固定大小的頁組成。通過頁表機制,使段內的頁可以不必連續處於同一記憶體區域,從而減少了外部碎片,然而同一頁內仍然可能存在少量的內部碎片,只是一頁的記憶體空間本就較小,從而使可能存在的內部碎片也較少。

12.類和結構體的區別

C#

  1. 結構體(sturct)是一種值型別,而類(class)是引用型別。區別在於複製方式,值型別的資料是值賦值,引用型別的資料是引用複製。
  2. 結構體使用棧儲存(Stack Allocation),而類使用堆儲存(Heap Allocation)。棧的空間相對較小.但是儲存在棧中的資料訪問效率相對較高。堆的空間相對較大.但是儲存在堆中的資料的訪問效率相對較低。結構體使用完之後就自動解除記憶體分配,類例項有垃圾回收機制來保證記憶體的回收處理。

如何選擇結構體還是類

1. 堆疊的空間有限,對於大量的邏輯的物件,建立類要比建立結構好一些

2. 結構體表示如點、矩形和顏色這樣的輕量物件,例如,如果宣告一個含有 1000 個點物件的陣列,則將為引用每個物件分配附加的記憶體。在此情況下,結構體的成本較低。

3. 在表現抽象和多級別的物件層次時,類是最好的選擇,因為結構體不支援繼承

4. 大多數情況下該型別只是一些資料時,結構體時最佳的選擇

C++

  1. 最本質的一個區別就是:結構體的成員和成員函式在預設情況下都是公有的(public),類的成員和成員函式在預設情況下都是私有的(private)。

訪問許可權:

比如以下程式碼

struct A{int m_nNum;};struct B : A{ QString m_strFile;};

上面例子中,B是public繼承A的。如果寫成下面:

class A{int m_nNum;};class B : A{ QString m_strFile;};

這樣的話B就變成private繼承A了。這個就是預設訪問許可權的意思。

其實說到底繼承取決於子類而非基類!

混合繼承

比如下面簡單那的例子

struct A{int m_nNum;};class B : A{ QString m_strFile;};struct C:B{};

上面:B是private繼承A,C是public繼承B!!!

13.野指標

1. 指標變數的值未被初始化。

2.指標所指向的地址空間已經被free或delete,但是沒有賦值為null,此時指標指向隨機一片記憶體。

3.指標操作超越了作用域。

14.虛擬函式和純虛擬函式

定義一個函式為虛擬函式,不代表函式為不被實現的函式。

定義他為虛擬函式是為了允許用基類的指標來呼叫子類的這個函式。

定義一個函式為純虛擬函式,才代表函式沒有被實現。

定義純虛擬函式是為了實現一個介面,起到一個規範的作用,規範繼承這個類的程式設計師必須實現這個函式。

15.值傳遞,指標傳遞,引用傳遞

首先從概念上來說一下這幾種函式傳參方式及區別:

1、值傳遞:形參是實參的拷貝,改變函式形參的值並不會影響外部實參的值,這是最常用的一種傳參方法,也是最簡單的一種傳參方法,只需要傳遞引數,返回值那是return考慮的;

2、指標傳遞:指標傳遞引數從本質上來說也是值傳遞,它傳遞的是一個地址。【值傳遞過程中,被調函式的形參作為被調函式的區域性變數來處理,即在函式內的棧中開闢記憶體空間以存放由主調函式放進來的實參的值,從而成了實參的一個副本(記住這個,函式內參數的是實參的副本)】。由於指標傳遞的是外部實參的地址,當被調函式的形參值發生改變時,自然外部實參值也發生改變。

3、引用傳遞:被調函式的形參雖然也作為區域性變數在棧中開闢了記憶體空間,但是棧中存放的是由主調函式放進的實參變數的地址。被調函式對形參的任何操作都被處理成間接定址,即通過棧中存放的地址訪問主調函式中實參變數(實參和形參通過引用,合二為一,說白點就是:一個人,有兩個名字那種;後面想會詳細說)。因此,形參的任何改動都會直接影響到實參。