1. 程式人生 > >綜合面試題(下)

綜合面試題(下)

10.C++中包含哪幾種強制型別轉換?他們有什麼區別和聯絡
reinterpret_cast: 轉換一個指標為其它型別的指標。它也允許從一個指標轉換為整數型別,反之亦 然. 這個操作符能夠在非相關的型別之間轉換. 操作結果只是簡單的從一個指標到別的指標的值的 二進位制拷貝. 在型別之間指向的內容不做任何型別的檢查和轉換? 
class A{}; 
class B{}; 
A* a = new A;

B* b = reinterpret_cast(a); 
• static_cast: 允許執行任意的隱式轉換和相反轉換動作(即使它是不允許隱式的),例如:應用到類 的指標上, 意思是說它允許子類型別的指標轉換為父類型別的指標(這是一個有效的隱式轉換), 同 時, 也能夠執行相敏感詞作: 轉換父類為它的子類 
class
Base {}; class Derive:public Base{}; Base* a = new Base; Derive *b = static_cast(a); • dynamic_cast: 只用於物件的指標和引用. 當用於多型型別時,它允許任意的隱式型別轉換以及相 反過程. 不過,與static_cast不同,在後一種情況裡(注:即隱式轉換的相反過程),dynamic_cast 會檢查操作是否有效. 也就是說, 它會檢查轉換是否會返回一個被請求的有效的完整物件。檢測在 執行時進行. 如果被轉換的指標不是一個被請求的有效完整的物件指標,返回值為NULL. 對於引用 型別,會丟擲bad_cast異常 • const_cast
: 這個轉換型別操縱傳遞物件的const屬性,或者是設定或者是移除,例如: class C{}; const C* a = new C; C *b = const_cast(a); (1static_cast:在功能上基本上與C風格的型別轉換一樣強大,含義也一樣。它有功能上的限制。例如,你不能用static_cast像用C風格轉換一樣把struct轉換成int型別或者把double型別轉換成指標型別。另外,static_cast不能從表示式中去除const屬性,因為另一個新的型別轉換符const_cast有這樣的功能。 可以靜態決議出型別的轉換可能性,即使是在繼承體系中,即使包括了多重繼承和虛繼承,只要可以進行靜態決議就可以轉換成功 (2
const_cast:用於型別轉換掉表示式的constvolatile屬性。通過使用const_cast,你向人們和編譯器強調你通過型別轉換想做的只是改變一些東西的constness或者volatieness屬性。這個含義被編譯器所約束。如果你試圖使用const_cast來完成修改constness或者volatileness屬性之外的事情,你的型別轉換將被拒絕。 (3dynamic_cast:它被用於安全地沿著類的繼承關係向下進行型別轉換。這就是說,你能用dynamic_cast把指向基類的指標或引用轉換成指向其派生類或其兄弟類的指標或引用,而且你能知道轉換是否成功。失敗的轉換將返回空指標(當對指標進行型別轉換時)或者丟擲異常(當對引用進行型別轉換時)。 (4reinterpret_cast:使用這個操作符的型別轉換,其轉換結果幾乎都是執行期定義。因此,使用reinterpret_cast的程式碼很難移植。reinterpret_casts的最普通的用途就是在函式指標型別之間進行轉換。
11.簡述C++虛擬函式作用及底層實現原理!
要點是虛擬函式表和虛擬函式表指標的作用。C++中虛擬函式使用虛擬函式表和 虛擬函式表指標實現,虛擬函式表是一個類的虛擬函式的地址表,用於索引類本身以及父類的虛擬函式的地 址,假如子類的虛擬函式重寫了父類的虛擬函式,則對應在虛擬函式表中會把對應的虛擬函式替換為子類的 虛擬函式的地址;虛擬函式表指標存在於每個物件中(通常出於效率考慮,會放在物件的開始地址處), 它指向物件所在類的虛擬函式表的地址;在多繼承環境下,會存在多個虛擬函式表指標,分別指向對應 不同基類的虛擬函式表。
12.一個物件訪問普通成員函式和虛擬函式哪個更快!?
首先看是否構成虛擬函式重寫,如果不構成 則一樣快 
如果構成了虛擬函式重寫,那就訪問普通成員函式更快, 
因為構成了虛擬函式重寫在編譯階段就不能確定函式地址,需要在執行的時候在虛表裡查詢; 
呼叫函式的真實地址。
13.在什麼情況下,解構函式需要是虛擬函式?!
首先要明確:

1.每個解構函式(不加 virtual) 只負責清除自己的成員。
2.可能有基類指標,指向的確是派生類成員的情況。(這是很正常的),
   那麼當析構一個指向派生類成員的基類指標時,程式就不知道怎麼辦了。 
   所以要保證執行適當的解構函式,基類中的解構函式必須為虛析構。

        基類指標可以指向派生類的物件(多型性),如果刪除該指標delete []p;就會呼叫該指標指向的派生類解構函式,而派生類的解構函式又自動呼叫基類的解構函式,這樣整個派生類的物件完全被釋放。如果解構函式不被宣告成虛擬函式,則編譯器實施靜態繫結,在刪除基類指標時,只會呼叫基類的解構函式而不呼叫派生類解構函式,這樣就會造成派生類物件析構不完全。所以,將解構函式宣告為虛擬函式是十分必要的。
14.行內函數、建構函式、靜態成員函式可以是虛擬函式嗎?!
行內函數是在編譯階段就展開的,而虛擬函式是在執行階段動態繫結的,因此不可以

建構函式本身就是為了明確初始化物件才產生的,而虛擬函式主要是為了不再完全理解細節 也能正常處理物件,另外虛擬函式是在不同的型別產生不同的動作,現在沒有物件何談動,所以不可以。

靜態函式不屬於某一個物件,而是屬於類的本身,而虛擬函式是屬於物件的所以不支援;

因為C++不支援友元函式的繼承,對於沒有繼承特性的函式何來虛擬函式的說法?
15.建構函式中可以呼叫虛擬函式嗎?!
不可以
因為在建構函式和解構函式中,物件是不完整的,可能會發生未定義的行為。
16.簡述C++中虛繼承的作用及底層實現原理!
作用:為了解決從不同途徑繼承來的同名的資料成員在記憶體中有不同的拷貝造成資料不一致問題,將共同基類設定為虛基類。
這時從不同的路徑繼承過來的同名數據成員在記憶體中就只有一個拷貝,同一個函式名也只有一個對映。這樣不僅就解決了二義性問題,也節省了記憶體,避免了資料不一致的問題。
底層實現原理:底層實現原理與編譯器相關,一般通過虛基類指標實現,即各物件中只儲存一份父類的物件,多繼承時通過虛基類指標引用該公共物件,從而避免菱形繼承中的二義性問題。