1. 程式人生 > >C++面試題(四)

C++面試題(四)

31:記憶體對齊的方式和為什麼要記憶體對齊

記憶體對齊的規則:
1.結構(struct)(或聯合(union))的資料成員,第一個資料成員放在offset為0的地方,以後每個資料成員儲存的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說是陣列,結構體等)的整數倍開始(比如int在32位機為4位元組, 則要從4的整數倍地址開始儲存),基本型別不包括struct/class/uinon。
2).結構體作為成員:如果一個結構裡有某些結構體成員,則結構體成員要從其內部”最寬基本型別成員”的整數倍地址開始儲存.(struct a裡存有struct b,b裡有char,int ,double等元素,那b應該從8的整數倍開始儲存.)。
3).收尾工作:結構體的總大小,也就是sizeof的結果,.必須是其內部最大成員的”最寬基本型別成員”的整數倍.不足的要補齊.(基本型別不包括struct/class/uinon)。
4).sizeof(union),以結構裡面size最大元素為union的size,因為在某一時刻,union只有一個成員真正儲存於該地址。
原因:


1.所有的硬體平臺都能訪問任意地址上的任意資料
2. CPU對記憶體的讀取操作是對齊的,經過記憶體對齊後,CPU的記憶體訪問速度大大提升

32:記憶體洩露的定義、檢查、避免

記憶體洩漏:指由於疏忽或錯誤造成程式未能釋放已經不再使用的記憶體的情況,也就是申請的堆沒有釋放
檢測:
1.檢測工具
2.在工作管理員中觀察程式執行時的記憶體變化情況,逐步註釋,找到洩露點
避免:
1.養成良好習慣,保證malloc/new和free/delete匹配
2.不用的記憶體馬上釋放
3.分配的記憶體的指標以連結串列的形式自行管理,使用完畢之後從連結串列中刪除,程式結束時可檢查改連結串列

33:智慧指標類

c++中 的智慧指標引入一個計數器,每次有物件指向資源時,計數器就加一物件析構時,計數器減一,當計數器的值為0時,物件被釋放

template<typename T>
 5 class SmartPointer {
 6 private:
 7     T* _ptr;
 8     size_t* _count;
 9 public:
10     SmartPointer(T* ptr = nullptr) ://建構函式
11             _ptr(ptr) {
12         if (_ptr) {
13             _count = new size_t(1
); 14 } else { 15 _count = new size_t(0); 16 } 17 } SmartPointer(const SmartPointer& ptr) {//拷貝建構函式 20 if (this != &ptr) { 21 this->_ptr = ptr._ptr; 22 this->_count = ptr._count; 23 (*this->_count)++; 24 } 25 } SmartPointer& operator=(const SmartPointer& ptr) {//賦值操作符 28 if (this->_ptr == ptr._ptr) { 29 return *this; 30 } 31 32 if (this->_ptr) { 33 (*this->_count)--; 34 if (this->_count == 0) { 35 delete this->_ptr; 36 delete this->_count; 37 } 38 } 39 40 this->_ptr = ptr._ptr; 41 this->_count = ptr._count; 42 (*this->_count)++; 43 return *this; 44 } T& operator*() {//*操作符 47 assert(this->_ptr == nullptr); 48 return *(this->_ptr); 49 50 } T* operator->() {//->操作符 53 assert(this->_ptr == nullptr); 54 return this->_ptr; 55 } ~SmartPointer() {//解構函式 58 (*this->_count)--; 59 if (*this->_count == 0) { 60 delete this->_ptr; 61 delete this->_count; 62 } 63 } 64 65 size_t use_count(){ 66 return *this->_count; 67 } 68 };

34:程式除錯的一般方法

1.插入列印語句
2.利用輸出的記憶體資訊和除錯工具打斷點
3.單步執行
4.看變數賦值是否正確
5.用工具查詢記憶體洩露
6.看日誌

35:怎麼除錯coredump

1.ulimit -c unlimited命令設定coredump檔案
2. gdb a.out core命令執行程式(linux下)
3. 使用bt命令檢視堆疊

36:記憶體檢查工具

valgrind(在linux下使用)

37:模板的用法和適用場景

函式模板: Template < class/*typename */ T>
類模板:template <class/*typename */ T>
class 類名{
};

模板中也可以有非模板的引數,如template<typename T, int MAXSIZE>class Stack{ }
非模板引數可以使常整數,或指向外部連結物件的指標
注意:
1.在類定義體外定義成員函式時,若此成員函式中有模板引數存在,則除了須要和一般類的體外定義成員函式一樣的定義外,還需在函式體外進行模板宣告
2.在類定義體外初始化const成員和static成員變數的做法和普通類體外初始化const成員和static成員變數的做法基本上是一樣的,唯一的差別是需再對模板進行宣告
3. 類模板的使用 類模板的使用實際上是將類模板例項化成一個詳細的類。它的格式為:類名<實際的型別>。
4. 函式模板不能是虛擬函式
5. 有時通用的函式模板不能解決個別型別的問題,我們必須對此進行定製,這就是函式模板的特化。函式模板的特化必須把所有的模版引數全部指定。
場景:一個類中資料成員的資料型別不能確定。或者是某個成員函式的引數或返回值的型別不能確定。就必須將此類宣告為模板,它的存在不是代表一個詳細的、實際的類,而是代表著一類類。

38:成員初始化列表的概念與優勢

以一個冒號開始,接著是以逗號分隔的資料成員列表,每個資料成員後面跟一個放在括號中的初始化式,對成員進行初始化。
建構函式在計算之前的初始化階段可以對類的成員進行初始化,也就是說物件在程式碼執行之前被建立。
初始化列表是建立的時候初始化,而程式碼內部則實際上是賦值。
1.const成員或引用成員無法賦值,必須使用初始化列表進行初始化
2.對於使用者自定義的型別來說,該型別在初始化列表中直接呼叫拷貝建構函式或建構函式,而進入函式體後,則是先呼叫建構函式,又呼叫拷貝賦值操作符,所以初始化列表速度快
3. C++初始化類成員時,是按照宣告的順序初始化的
4. 沒有預設建構函式的類型別,因為使用初始化列表可以不必呼叫預設建構函式來初始化,而是直接呼叫拷貝建構函式初始化
5. 子類初始化父類的私有成員,需要在(並且也只能在)引數初始化列表中顯示呼叫父類的建構函式

39:c++11的新特性

  1. nullptr 專門代表空指標
  2. auto 自動進行型別推導
  3. 引入了基於範圍的迭代寫法for(auto &i : arr)
  4. 初始化列表
  5. 引入了外部模板,能夠顯式的告訴編譯器何時進行模板的例項化
  6. 可以指定模板的預設引數
  7. 引入了委託構造的概念,這使得建構函式可以在同一個類中一個建構函式呼叫另一個建構函式
  8. 提供了一個匿名函式的特性
    等等

40:c++呼叫慣例

_stdcall 是StandardCall的縮寫,是C++的標準呼叫方式:所有引數從右到左依次入棧,如果是呼叫類成員的話,最後一個入棧的是this指標。這些堆疊中的引數由被呼叫的函式在返回後清除,即由呼叫者負責把引數壓入棧,最後由被呼叫者負責清除棧的內容,使用的指令是 retnX,X表示引數佔用的位元組數,CPU在ret之後自動彈出X個位元組的堆疊空間。稱為自動清棧。函式在編譯的時候就必須確定引數個數,並且呼叫者必須嚴格的控制引數的生成,不能多,不能少,否則返回後會出錯

41:c++的四種強制轉換

static_cast:完成靜態基礎型別轉換、同一繼承體系下的轉換(上行安全,下行不安全)、任意型別與空指標void*(或void)之間的轉換,主要執行非多型的轉換操作,隱式轉換都建議使用static_cast進行標明和替換
dynamic_cast:只有在派生類之間轉換時才使用dynamic_cast,轉換引數必須是類指標,類引用或者void*。
使用時基類必須有虛擬函式,因為dynamic_cast是執行時型別檢查,這個資訊存在虛表當中,下行轉換是安全的,如果不能轉換,返回NULL
const_cast:用於去除指標或引用的常量屬性,或賦予常量屬性,它是唯一可以對常量操作的轉換符
reinterpret_cast:不到萬不得已,不要使用,從底層對資料進行重新解釋,依賴具體的平臺,可以把整形變成指標,也可以把指標變成陣列,也可以在指標和引用之間隨意轉換,以轉化任何內建的資料型別為其他任何的資料型別,實質是對二進位制位的操作。