天天看點

小談類機制相關小談類機制相關

小談類機制相關

本文主要涉及類相關的一些常見面試問題,以及相關特性。

包括 this 指針、拷貝構造函數相關以及類機制。

一、this指針

編譯器在編譯普通成員函數時,會隐式的配置設定一個形參指針,即this指針。并且當執行個體化對象調用該成員函數時,會把目前對象的位址指派給this指針

1、this指針是常量指針

**this指針是常量指針,指向不可改變。**當使用const 關鍵字修飾成員函數時,該指針即是常量指針,又是指針常量,指向和指向的值都不可改變。

2、this 指針隻存在于普通成員函數中

this指針是編譯器為普通成員函數配置設定的隐式形參,對于靜态成員函數和友元函數來說,都是沒有 this指針的,是以一般雙目運算符的重載都使用全局函數做友元的形式。

另外我們知道,傳回局部變量的位址和引用會造成不可估計後果。但是在拷貝構造函數等成員函數中都會傳回this指針的解引用,因為其是傳入的有效形參,是有明确位址的。

3、成員函數中delete this 會發生什麼?

1)禁止在析構函數中使用 delete this。因為delete this 會不斷的調用本對象的析構函數,而析構函數中又會調用delete this,形成無線遞歸,進而造成堆棧溢出。

2)在普通成員函數中調用 delete this, 需要區分是棧對象和堆對象。因為delete this會執行析構函數,并釋放記憶體。那麼對于堆對象來說會執行析構,并且釋放記憶體,那麼當再次通路時就會出現記憶體問題;對于棧對象來說會執行析構,但是不會釋放記憶體,那麼再次通路也會出現記憶體錯誤。

二、拷貝構造函數

定義一個類實際就是聲明一個組合類型,而構造函數就是初始化(成員變量指派)的過程。拷貝構造函數就是根據一個現有的對象作為基礎去建立一個新的對象。

1、為什麼拷貝構造函數中形參一般使用const + &?

1)使用 & 是為了避免遞歸調用拷貝構造函數,進而造成堆棧溢出

2)使用const 是因為形成 常量左值引用,可以支援傳入左值、右值、常量左值和常量右值,相當于擴大了可傳入參數的類型範圍

2、深淺拷貝與移動語義

1)淺拷貝。

兩個指針指向同一塊記憶體,容易造成 記憶體的重複釋放問題。

2)深拷貝。

重新申請一塊記憶體,然後再進行指派操作。不會造成記憶體的重複釋放,但是存在記憶體開銷問題。

3)移動構造函數

主要是應用于臨時變量的拷貝,減少不必要的記憶體開銷問題。

3、拷貝構造函數和指派操作符差別

1)拷貝構造函數是構造函數,而指派操作符重載是成員函數

2)另外就是拷貝構造函數是去完成對未初始化的存儲區的初始化,而指派操作符是處理一個已經存在的對象。

三、類機制

1、class 和 struct 的差別

在C中struct中是不能存放函數的,如果想要實作封裝、繼承等特性需要使用函數指針。

在C++中class 和 struct 都是可以實作繼承和多态的。差別是預設的通路權限不一樣。

2、類在什麼情況下不能被繼承?

1)使用 final 關鍵字

2)構造函數私有化

3)虛繼承 + 友元

3、當發生多種繼承時類的構造順序(虛繼承、抽象類繼承、普通繼承)

靜态成員> 虛基類> 抽象類> 基類 > 成員變量(按照聲明順序)> 派生類

4、類的大小

1)位元組對齊

2)虛函數(虛函數表指針占用 8個位元組)

3)虛繼承(虛基類表指針占用8個位元組)

4)空類的大小為1個位元組。主要是用于區分不同的對象。

5、如何限制隻能在棧上建立對象?

C++隻有使用new 關鍵字,對象才會建立在堆上。并且C++允許重載operator new 運算符,那麼隻要講operator new 運算符進行重載并私有化即可。

6、如何限制隻能在堆上建立對象?

類對象隻能建立在堆上,就是不能靜态建立類對象,即不能直接調用類的構造函數。 容易想到将構造函數設為私有。在構造函數私有之後,無法在類外部調用構造函數來構造類對象,隻能使用new運算符來建立對象。然而,前面已經說過,new運算符的執行過程分為兩步,調用new 時仍然需要調用構造函數。

當對象建立在棧上面時,是由編譯器配置設定記憶體空間的,調用構造函數來構造棧對象。當對象使用完後,編譯器會調用析構函數來釋放棧對象所占的空間。編譯器管理了對象的整個生命周期。如果編譯器無法調用類的析構函數,情況會是怎樣的呢?比如,類的析構函數是私有的,編譯器無法調用析構函數來釋放記憶體。是以,編譯器在為類對象配置設定棧空間時,會先檢查類的析構函數的通路性,其實不光是析構函數,隻要是非靜态的函數,編譯器都會進行檢查。如果類的析構函數是私有的,則編譯器不會在棧空間上為類對象配置設定記憶體。

7、如何判斷類/結構體對象是否相等? 能否使用memcmp函數進行逐字元判斷

1)判斷類/結構體對象是否相等需要重載比較運算符(==)。

2)不可以使用memcmp函數進行比較。因為memcmp是進行逐位元組比較的,而結構體在儲存時進行了位元組對齊,而在位元組對齊的過程中的填充是随機的,是以無法直接進行比較。

四、成員初始化清單

采用初始化清單,成員初始化的順序就是根據變量聲明的順序。

1、為什麼使用成員初始化清單要比在構造函數中初始化要快一點?

嚴格來說,在構造函數中執行的是指派操作,并不是初始化操作;因為在對象的構造順序中,成員的初始化要比構造函數的初始化早;

那麼如果使用構造函數對自定義成員對象進行指派會執行兩步(預設構造函數 + 拷貝指派), 而如果使用成員初始化清單,則不會執行預設構造函數,而是直接根據初始化清單中傳入的參數進行相應的成員構造。減少了不必要的開銷。

2、三種情況必須使用成員初始化清單

1)引用類型成員(初始化時必須要進行指派)

2)常量類型成員(初始化後不能被指派,隻能在初始化時指派)

3)沒有預設構造函數的成員類型(比如隻重載了預設構造函數,那麼該成員就沒有預設的無參構造函數可以調用,就會出錯)