C++面試常見問題彙總

 

1.      指針和引用的區別html

    1. 能夠有多級指針,可是沒有多級引用
    2. 指針:指針是一個變量,只不過這個變量存儲的是一個地址,指向內存的一個存儲單元;而引用跟原來的變量實質上是同一個東西,只不過是原變量的一個別名而已。
    3. 引用不能夠爲空,當被建立的時候,必須初始化,而指針能夠是空值,能夠在任什麼時候候被初始化。
    4. 能夠有const指針,可是沒有const引用;
    5. 指針的值在初始化後能夠改變,即指向其它的存儲單元,而引用在進行初始化後就不會再改變了
    6. 指針和引用的自增(++)運算意義不同;

2.      堆和棧的區別linux

    1. 堆空間的內存是動態分配的,通常存放對象,而且須要手動釋放內存。棧空間的內存是由系統自動分配,通常存放局部變量,好比對象的地址等值,不須要程序員對這塊內存進行管理,
    2. 棧是運行時的單位,而堆是存儲的單位。堆中的共享常量和緩存 。棧解決程序的運行問題,即程序如何執行,或者說如何處理數據;堆解決的是數據存儲的問題,即數據怎麼放、放在哪兒。
    3. 棧由於是運行單位,所以裏面存儲的信息都是跟當前線程(或程序)相關信息的。包括局部變量、程序運行狀態、方法返回值等等;而堆只負責存儲對象信息。

3.      malloc/free與new/delete異同點,new和delete是如何實現的,new 與 malloc的異同處c++

相同點:程序員

  1. malloc/free與new/delete均可以用於申請動態內存和釋放內存,他們申請的空間都在堆上分配。

不一樣點:正則表達式

  • 操做對象不一樣

malloc/free是C++/C語言的標準庫文件,new/delete是C++的運算符;算法

對非內部數據對象,malloc/free沒法知足動態對象要求。對象在建立時要自動執行構造函數,對象消亡以前要自動執行析構函數,而malloc/free是庫函數,不是運算符,故不在編譯器控制權限以內,不可以將執行構造函數和析構函數強加於malloc/free身上。而因爲new/delete是C++語言,可以完成動態內存分配和初始化工做,並可以完成清理與釋放內存工做,即可以自動執行構造函數和析構函數;編程

  • 用法不一樣

malloc分配內存空間前須要計算分配內存大小;而new可以自動分配內存空間;設計模式

malloc是底層函數,其函數返回值類型爲void *;而new運算符調用無參構造函數,故返回值爲對應對象的指針;數組

malloc函數類型不是安全的,編譯器不對其進行類型轉換、類型安全的相關檢查。malloc申請空間後,不會對其初始化,要單獨初始化;而new類型是安全的,由於它內置了sizeof、類型轉換和類型安全檢查功能,且在建立對象時,就完成了初始化工做,通常初始化調用無參構造函數;緩存

operator new對應於malloc,且operator new能夠重載,能夠自定義內存分配策略,甚至不作內存分配,甚至分配到非內存設備上;但malloc不能。

free只進行釋放空間;而delete則釋放空間的同時調用析構函數。此外delete使用是注意釋放數組的方法爲delete []數組名。

  • new先調用malloc,後調用構造函數,delete先調用析構後調用free;

4.      C和C++的區別?面向對象編程的特色?

"面向過程的模型",按這種模型編寫的程序以一系列的線性步驟(代碼)爲特徵,可被理解爲做用於數據的代碼.

"面向對象的模型",按這種模型編寫的程序圍繞着程序的數據(對象)和針對該對象而嚴格定義的接口來組織程序,它的特色是數據控制代碼的訪問.經過把控制權轉移到數據上,面向對象的模型在組織方式上有:抽象,封裝,繼承和多態的好處.

 (數據抽象)如一棵樹的高度、一本書的頁數等。不過,有些特徵並不能用這樣的辦法來表示,(過程抽象)如一隻狗會跑。用數據來表示這個跑的動做是作不到的,由於這是一個動做過程,而不是一種狀態。

多態指一樣的方法在不一樣的對象中有不一樣的表現方式。多態實現的方法,

靜態多態----函數重載  泛型編程

動態多態----虛函數

5.      Struct和class的區別

    1. 默認的繼承與訪問權限:struct默認是公有繼承(public),class默認是私有繼承(private)
    2. 在C++中對struct的功能進行了擴展,struct能夠被繼承,能夠包含成員函數,也能夠實現多態,當用大括號對其進行初始化須要注意:

當struct和class中都定義了構造函數,就不能使用大括號對其進行初始化

若沒有定義構造函數,struct可使用{ }進行初始化,而只有當class的全部數據成員及函數爲public時,可使用{ }進行初始化

因此struct更適合當作是一個數據結構的實現體,class更適合當作是一個對象的實現體。

6.      define 和const的區別(編譯階段、安全性、內存佔用等)

    1.  編譯器處理方式不一樣

define宏是在預處理階段展開。

const常量是編譯運行階段使用。

  1. 類型和安全檢查不一樣

  define宏沒有類型,不作任何類型檢查,僅僅是展開。

  const常量有具體的類型,在編譯階段會執行類型檢查。

  1. 存儲方式不一樣

  define宏僅僅是展開,有多少地方使用,就展開多少次,不會分配內存。

  const常量會在內存中分配(能夠是堆中也能夠是棧中)。

  1. const 能夠節省空間,避免沒必要要的內存分配。 例如:

#define PI 3.14159 //常量宏

const doulbe Pi=3.14159; //此時並未將Pi放入ROM中 ......

double i=Pi; //此時爲Pi分配內存,之後再也不分配!

double I=PI; //編譯期間進行宏替換,分配內存

double j=Pi; //沒有內存分配

double J=PI; //再進行宏替換,又一次分配內存!

const定義常量從彙編的角度來看,只是給出了對應的內存地址,而不是象#define同樣給出的是當即數,因此,const定義的常量在程序運行過程當中只有一份拷貝,而 #define定義的常量在內存中有若干個拷貝。

  1. 提升了效率。

編譯器一般不爲普通const常量分配存儲空間,而是將它們保存在符號表中,這使得它成爲一個編譯期間的常量,沒有了存儲與讀內存的操做,使得它的效率也很高

7.      C/C++中static和const關鍵字的做用總結

  • static 關鍵字至少有下列 n 個做用:

  (1)函數體內 static 變量的做用範圍爲該函數體,不一樣於auto 變量,該變量的內存只被分配一次,所以其值在下次調用時仍維持上次的值;

  (2)在模塊內的 static 全局變量能夠被模塊內所用函數訪問,但不能被模塊外其它函數訪問;

  (3)在模塊內的 static 函數只可被這一模塊內的其它函數調用,這個函數的使用範圍被限制在聲明它的模塊內;

  (4)在類中的 static 成員變量屬於整個類所擁有,對類的全部對象只有一份拷貝;

(5)在類中的 static 成員函數屬於整個類所擁有,這個函數不接收 this 指針,於是只能訪問類的 static 成員變量。

  • const 關鍵字至少有下列 n 個做用

(1)欲阻止一個變量被改變,可使用 const 關鍵字。在定義該 const 變量時,一般須要對它進行初始化,由於之後就沒有機會再去改變它了;

(2)對指針來講,能夠指定指針自己爲 const,也能夠指定指針所指的數據爲 const,或兩者同時指定爲 const;

(3)在一個函數聲明中,const 能夠修飾形參,代表它是一個輸入參數,在函數內部不能改變其值;

(4)對於類的成員函數,若指定其爲 const 類型,則代表其是一個常函數,不能修改類的成員變量;

8.      C++的頂層const和底層const

頂層const表示指針自己是個常量,底層const表示指針所指的對象是一個常量。

Int *const p1 = &i;  不能改變P1的值,這是一個頂層const

Const int *p2 = &i;  容許改變P2的值,這是一個底層const

用實參初始化形參時會忽略到頂層const。換句話說,形參的頂層const被忽略掉了

9.      final和override關鍵字

  • final限定某個類不能被繼承或某個虛函數不能被重寫。若是修飾函數只能修飾虛函數,且要寫到類或函數後面。

class Base

{

    virtual void foo();

};

class A : Base

{

    void foo() final; // foo 被override而且是最後一個override,在其子類中不能夠重寫

    void bar() final; // Error: 父類中沒有 bar虛函數能夠被重寫或final

};

class B final : A // 指明B是不能夠被繼承的

{

    void foo() override; // Error: 在A中已經被final了

};

class C : B // Error: B is final

{

};

  • override關鍵字保證了派生類中聲明重寫的函數與基類虛函數有相同的簽名,可避免一些拼寫錯誤,如加了此關鍵字但基類中並不存在相同的函數就會報錯,也能夠防止把原本想重寫的虛函數聲明成了重載。同時在閱讀代碼時若是看到函數聲明後加了此關鍵字就能立馬知道此函數是重寫了基類虛函數。保證重寫虛函數的正確性的同時也提升了代碼可讀性。

class A

{

    virtual void foo();

};

class B :A

{

    virtual void foo() override;    //OK

    virtual void f00() override;     //Error ,由於基類中沒有void f00()函數

};

10.  拷貝初始化和直接初始化,初始化和賦值的區別

  • 對象不存在,且沒用別的對象來初始化,調用構造函數
  • 對象不存在,且用別的對象來初始化,調用拷貝構造
  • 對象存在,用別的對象給他賦值,就是賦值函數

http://www.javashuo.com/article/p-ewwxvnpf-hd.html

https://blog.csdn.net/ljianhui/article/details/9245661

https://blog.csdn.net/splendid7/article/details/81537706

http://www.javashuo.com/article/p-wgwpsesw-ez.html(移動構造函數)

(1)什麼是拷貝初始化(也稱爲複製初始化):將一個已有的對象拷貝到正在建立的對象,若是須要的話還須要進行類型轉換。拷貝初始化發生在下列狀況:

1.使用賦值運算符定義變量

2.將對象做爲實參傳遞給一個非引用類型的形參

3.將一個返回類型爲非引用類型的函數返回一個對象

4.用花括號列表初始化一個數組中的元素或一個聚合類中的成員

(2)什麼是直接初始化:在對象初始化時,經過括號給對象提供必定的參數,而且要求編譯器使用普通的函數匹配來選擇與咱們提供的參數最匹配的構造函數

ClassTest ct2 = "ab";//複製初始化

ClassTest ct2("ab");//直接初始化
  • 直接初始化和拷貝初始化效率基本同樣,由於在底層的實現基本同樣,因此將拷貝初始化改成直接初始化效率提升不大
  • 拷貝初始化何時使用拷貝構造函數:???
  1. 賦值表達式右邊是一個對象
  2. 直接初始化時,括號內的參數是一個對象
  3. 用花括號列表初始化一個數組中的元素或一個聚合類中的成員
  4. 將一個返回類型爲引用類型的函數返回一個對象
  5. 形參爲非引用類型的函數,其中是將實參拷貝到臨時對象
  • 何時使用到拷貝賦值運算符:???
  1. 賦值表達式右邊是一個左值對象(若是須要,能夠調用構造函數類型轉換,生成一個臨時對象)
  2. 當賦值表達式右邊是一個右值對象,且沒有定義移動賦值運算符函數
  • 何時使用移動賦值運算符:???
  1. 當賦值表達式右邊是一個右值對象,且定義了移動賦值運算符函數

11.  extern "C"的用法

爲了可以正確的在C++代碼中調用C語言的代碼;在程序中加上extern "C"後,至關於告訴編譯器這部分代碼是C語言寫的,所以要按照C語言進行編譯,而不是C++;

12.  模板函數和模板類的特例化

 Typename關鍵字 告訴編譯把一個特殊的名字解釋成一個類型,類模板時能夠換成class

  template<typename T> void swap(T& t1, T& t2)

{

T tmpT; tmpT = t1;

t1 = t2; t2 = tmpT;

}

#include <stdio.h> #include "method.h"

int main()

{

//模板方法 int num1 = 1, num2 = 2;

swap<int>(num1, num2);

   printf("num1:%d, num2:%d\n", num1, num2);

return 0;

  }

13.  C++STL源碼

STL是一個c++裏面很是強大的庫,c11引進的,裏面封裝例如容器,泛型算法等

14.  STL源碼中的hashtable的實現

hash_table是STL中hash_map 和 hash_set 的內部數據結構,hash_table的插入/刪除/查找的時間複雜度都爲O(1),是查找速度最快的一種數據結構,可是hash_table中的數據是無序的,通常也只有在數據不須要排序,只須要知足快速查找/插入/刪除的時候使用hash_table。hash_table的擴展是將原hash_table中的數據摘下來插入到一個臨時的hash_table中,由於每一個桶都使用list來實現的,所以插入刪除都不存在內存copy,因此也是很高效的,最後再將臨時hash_table和原來的hash_table(此時已經爲空)交換。

15.  STL中unordered_map和map的區別和應用場景

  • map是一種映射,這種映射是有序的,底層是使用紅黑樹來完成的,數據經過鍵值才存儲,鍵是惟一的。
  • unordered_map,是一種無序的,底層是經過hash表來完成的。unordered庫使用「桶」來存儲元素,散列值相同的被存儲在一個桶裏。當散列容器中有大量數據時,同一個桶裏的數據也會增多,形成訪問衝突,下降性能。爲了提升散列容器的性能,unordered庫會在插入元素是自動增長桶的數量,不須要用戶指定。每一個桶都是用list來完成的。

map

優勢:

● 有序性,這是map結構最大的優勢,其元素的有序性在不少應用中都會簡化不少的操做

● 紅黑樹,內部實現一個紅黑書使得map的不少操做在lgn的時間複雜度下就能夠實現,所以效率很是的高

缺點:

● 空間佔用率高,由於map內部實現了紅黑樹,雖然提升了運行效率,可是由於每個節點都須要額外保存父節點,孩子節點以及紅/黑性質,使得每個節點都佔用大量的空間

● 適用處,對於那些有順序要求的問題,用map會更高效一些

unordered_map

優勢

● 由於內部實現了哈希表,所以其查找速度很是的快

缺點

● 哈希表的創建比較耗費時間

● 適用處,對於查找問題,unordered_map會更加高效一些,所以遇到查找問題,常會考慮一下用unordered_map

16.  STL中vector的實現

注意兩個點:

1.vector有備用空間,當備用空間不夠的時候,會從新開闢原空間兩倍的空間進行重寫分配。

2.vector支持隨機的存取,可是最好是選擇從末尾插入,由於從中間插入會致使元素的移動,帶來了性能的開銷。

17.  STL容器的幾種迭代器以及對應的容器(輸入迭代器,輸出迭代器,前向迭代器,雙向迭代器,隨機訪問迭代器)

  • 順序容器:vector,deque是隨機訪問迭代器;list是雙向迭代器
  • 容器適配器:stack,queue,priority_queue沒有迭代器
  • 關聯容器:set,map,multiset,multimap是雙向迭代器
  • unordered_set,unordered_map,unordered_multiset,unordered_multimap是前向迭代器
  1. STL中的traits技法???

19.  vector使用的注意點及其緣由,頻繁對vector調用push_back()對性能的影響和緣由。

    1. 在一個vector的尾部以外的任何位置添加元素,都須要從新移動元素。
    2. 並且,向一個vector添加元素可能引發整個對象存儲空間的從新分配。從新分配一個對象的存儲空間須要分配新的內存,並將元素從舊的空間移到新的空間(由於vector在內存中的地址是連續的,因此加入一個可能形成地址不夠)

20.  C++中的重載和重寫的區別

  • 重定義發生在繼承中,從新定義了一個函數;重寫(虛函數多態關鍵):與重定義差很少,只是重寫的是虛函數;
  • 重載是指發生在同一個類中,名稱相同,可是參數個數或者類型不一樣。

21.  C++內存管理,內存池技術(熱門問題),與csapp中幾種內存分配方式對比學習加深理解???

c++的內存管理延續c語言的內存管理,可是也增長了其餘的,例如智能指針,除了常見的堆棧的內存管理以外,c++支持智能指針,智能指針的對象進行賦值拷貝等操做的時候,每一個智能指針都有一個關聯的計數器,該計數器記錄共享該對象的指針個數,當最後一個指針被銷燬的時候,計數器爲0,會自動調用析構函數來銷燬函數。

22.  介紹面向對象的三大特性,而且舉例說明每個

繼承,多態,封裝。

  • 繼承:某些類似的特性,能夠從一個類繼承到另外一個類,相似生活中的繼承,例如有個全部的汽車都有4個輪子,那麼咱們在父類中定義4個輪子,經過繼承得到4個輪子的功能,不用再類裏面再去定義這4個輪子的功能。。
  • 多態:多態和虛函數。多態指的相同的功能,不一樣的狀態,多態在面向對象c++裏面是經過重載和覆蓋來完成的,覆蓋在c++裏面經過虛函數來完成的。例如鴨子的例子,全部的鴨子都有顏色,咱們能夠將這個顏色設置成爲一個虛函數,經過繼承子類對虛函數進行覆蓋,不一樣子類中有各自的顏色,也就是有各自不一樣的鴨子顏色,這就是多態的典型表現之一
  • 封裝:將不少有類似特性的內容封裝在一個類中,例如學生的成績學號、課程這些能夠封裝在同一個類中。

23.  C++多態的實現

多態經過覆蓋和重載來完成。

24.  C++虛函數相關(虛函數表,虛函數指針),虛函數的實現原理(包括單一繼承,多重繼承等)(拓展問題:爲何基類指針指向派生類對象時能夠調用派生類成員函數,基類的虛函數存放在內存的什麼區,虛函數表指針vptr的初始化時間)

虛函數分爲兩種,純虛函數和虛函數,純虛函數適用於抽象基類,不須要定義,相似一種接口,是多態的典型處理方式。

一個類若是定義了虛函數,那麼編譯器會自動爲它加上一個虛函數表,並提供一個指向虛函數表的指針,子類經過繼承,能夠覆蓋父類的虛函數,當用戶調用虛函數的時候,會調用指針,去虛函數表中找匹配的虛函數,若是當前對象有覆蓋的虛函數,則去執行覆蓋的虛函數,不然執行父類的虛函數。

 

 

26.  this指針

    1. 一個對象的this指針並非對象自己的一部分,不會影響sizeof(對象)的結果。
    2. 全局函數,靜態函數都不能使用this。靜態函數表示了整個類範圍意義上的信息,而this指針卻實實在在的對應一個對象
    3. this在成員函數的開始執行前構造的,在成員的執行結束後清除
    4. this指針只能在一個類的成員函數中調用,它表示當前對象的地址

27.  析構函數通常寫成虛函數的緣由

delete父類指針時,能夠調用對象真正的析構函數。不然可能會出錯,如子類中有指針類型,而且申請了內存,這時就會形成內存泄漏。

28.  構造函數、拷貝構造函數和賦值操做符的區別

    1. 構造函數:對象不存在,沒用別的對象初始化
    2. 拷貝構造函數:對象不存在,用別的對象初始化
    3. 賦值運算符:對象存在,用別的對象給它賦值

30.  構造函數爲何通常不定義爲虛函數

  • 對象都尚未實例化,怎麼去找虛表

三個緣由

1.虛函數的做用是什麼?是實現部分或默認的功能,並且該功能能夠被子類所修改。若是父類的構造函數設置成虛函數,那麼子類的構造函數會直接覆蓋掉父類的構造函數。而父類的構造函數就失去了一些初始化的功能。這與子類的構造須要先完成父類的構造的流程相違背了。而這個後果會至關嚴重。

2.虛函數的調用是須要經過「虛函數表」來進行的,而虛函數表也須要在對象實例化以後纔可以進行調用。在構造對象的過程當中,尚未爲「虛函數表」分配內存。因此,這個調用也是違背先實例化後調用的準則。

3.虛函數的調用是由父類指針進行完成的,而對象的構造則是由編譯器完成的,因爲在建立一個對象的過程當中,涉及到資源的建立,類型的肯定,而這些是沒法在運行過程當中肯定的,須要在編譯的過程當中就肯定下來。而多態是在運行過程當中體現出來的,因此是不可以經過虛函數來建立構造函數的,與實例化的次序不一樣也有關係。

31.  構造函數的幾種關鍵字(default delete 0)

    1. = default:將拷貝控制成員定義爲=default顯式要求編譯器生成合成的版本
    2. = delete:將拷貝構造函數和拷貝賦值運算符定義刪除的函數,阻止拷貝(析構函數不能是刪除的函數 C++Primer P450)
    3. = 0:將虛函數定義爲純虛函數(純虛函數無需定義,= 0只能出如今類內部虛函數的聲明語句處;固然,也能夠爲純虛函數提供定義,不過函數體必須定義在類的外部)

32.  構造函數或者析構函數中調用虛函數會怎樣

結果上來看和調用普通函數無異。即基類調用基類的虛函數,派生類調用派生類的虛函數。

不要在構造函數和析構函數內調用虛函數。假如你定義了一個對象,首先調用的是基類的構造函數,而此時基類構造函數內的虛函數不是調用的是此對象類的對應函數,是基類的對應函數,與咱們的預期不同。

 

 

34.  靜態類型和動態類型,靜態綁定和動態綁定的介紹

 一、對象的靜態類型(static type):就是它在程序中被聲明時所採用的類型(或理解爲類型指針或引用的字面類型),在編譯期肯定;

  二、對象的動態類型(dynamic type):是指「目前所指對象的類型」(或理解爲類型指針或引用的實際類型),在運行期肯定;

 這兩個概念通常發生在基類和派生類之間。

 

 

  三、靜態綁定(statically bound):又名前期綁定(eraly binding),綁定的是靜態類型,所對應的函數或屬性依賴於對象的靜態類型,發生在編譯期;

  四、動態綁定(dynamically bound):又名後期綁定(late binding),綁定的是動態類型,所對應的函數或屬性依賴於對象的動態類型,發生在運行期;

  好比常見的,virtual函數是動態綁定,non-virtual函數是靜態綁定,缺省參數值也是靜態綁定。當缺省參數和虛函數一塊兒出現的時候狀況有點複雜,極易出錯。咱們知道,虛函數是動態綁定的,可是爲了執行效率,缺省參數是靜態綁定的  

http://www.javashuo.com/article/p-fihsqwfh-kc.html

35.  引用是否能實現動態綁定,爲何引用能夠實現

能夠實現,由於動態綁定是發生在程序運行階段的,c++中動態綁定是經過對基類的引用或者指針調用虛函數時發生。

引用和指針的靜態類型和動態類型能夠不同。

  • 靜態類型:變量聲明時的類型或表達式生成的類型。編譯時已經知道。
  • 動態類型:變量或表達式表示的內存的對象的類型。

36.  深拷貝和淺拷貝的區別(舉例說明深拷貝的安全性)

  • 淺拷貝只是對指針的拷貝,拷貝後兩個指針指向同一個內存空間,深拷貝不但對指針進行拷貝,並且對指針指向的內容進行拷貝,經深拷貝後的指針是指向兩個不一樣地址的指針。

37.  對象複用的瞭解,零拷貝的瞭解

  • 對象複用指得是設計模式,對象能夠採用不一樣的設計模式達到複用的目的,最多見的就是繼承和組合模式了
  • 零拷貝:零拷貝主要的任務就是避免CPU將數據從一塊存儲拷貝到另一塊存儲,主要就是利用各類零拷貝技術,避免讓CPU作大量的數據拷貝任務,減小沒必要要的拷貝,或者讓別的組件來作這一類簡單的數據傳輸任務,讓CPU解脫出來專一於別的任務。這樣就可讓系統資源的利用更加有效。

38.  介紹C++全部的構造函數

默認,普通,拷貝,賦值:默認無參,普通有參數,其實還有this指針,拷貝就是const class &A,賦值構造函數是對賦值符號的重載,若是有動態生成的對象的話,那麼它作賦值,原先的動態空間必定要被釋放掉,而後再賦上新的值,因此必須得本身重載=,實現delete。

 

 

39.  什麼狀況下會調用拷貝構造函數(三種狀況)

一個對象去初始化,值傳遞類,返回值是類

40.  結構體內存對齊方式和爲何要進行內存對齊?

保證計算機能夠一次讀出。

結構體不像數組,結構體中能夠存放不一樣類型的數據,它的大小也不是簡單的各個數據成員大小之和,限於讀取內存的要求,而是每一個成員在內存中的存儲都要按照必定偏移量來存儲,根據類型的不一樣,每一個成員都要按照必定的對齊數進行對齊存儲,最後整個結構體的大小也要按照必定的對齊數進行對齊。

一樣的結構體,裏面的數據排放順序不同,最後佔用的內存也不同大

//平臺VS2013下(默認對齊數爲8)

//練習一

    struct S1

    {

        char c1;

        int i;

        short s2;

    };

    printf("%d\n", sizeof(struct S1));//12

//練習二

    struct S2

    {

        char c1;

        short s2;

        int i;

    };

printf("%d\n", sizeof(struct S2));//8

 

 

41.  內存泄露的定義,如何檢測與避免?

  • 內存泄漏(memory leak)是指因爲疏忽或錯誤形成了程序未能釋放掉再也不使用的內存的狀況。內存泄漏並不是指內存在物理上的消失,而是應用程序分配某段內存後,因爲設計錯誤,失去了對該段內存的控制,於是形成了內存的浪費。
  • 堆內存泄漏 (Heap leak)。對內存指的是程序運行中根據須要分配經過malloc,realloc new等從堆中分配的一塊內存,再是完成後必須經過調用對應的 free或者delete 刪掉。若是程序的設計的錯誤致使這部份內存沒有被釋放,那麼此後這塊內存將不會被使用,就會產生Heap Leak.
  • 系統資源泄露(Resource Leak).主要指程序使用系統分配的資源好比 Bitmap,handle ,SOCKET等沒有使用相應的函數釋放掉,致使系統資源的浪費,嚴重可致使系統效能下降,系統運行不穩定。
  • 解決內存泄漏最有效的辦法就是使用智能指針

42.  手寫智能指針的實現(shared_ptr和weak_ptr實現的區別)

  • shared_ptr共享的智能指針

shared_ptr使用引用計數,每個shared_ptr的拷貝都指向相同的內存。在最後一個shared_ptr析構的時候,內存纔會被釋放。

注意事項:

1.不要用一個原始指針初始化多個shared_ptr。

2.不要再函數實參中建立shared_ptr,在調用函數以前先定義以及初始化它。

3.不要將this指針做爲shared_ptr返回出來。

4.要避免循環引用。

  • unique_ptr獨佔的智能指針:

<1>Unique_ptr是一個獨佔的智能指針,他不容許其餘的智能指針共享其內部的指針,不容許經過賦值將一個unique_ptr賦值給另一個 unique_ptr。

<2>unique_ptr不容許複製,但能夠經過函數返回給其餘的unique_ptr,還能夠經過std::move來轉移到其餘的unique_ptr,這樣它自己就再也不 擁有原來指針的全部權了。

<3>若是但願只有一個智能指針管理資源或管理數組就用unique_ptr,若是但願多個智能指針管理同一個資源就用shared_ptr。

  • weak_ptr弱引用的智能指針:

弱引用的智能指針weak_ptr是用來監視shared_ptr的,不會使引用計數加一,它無論理shared_ptr內部的指針,主要是爲了監視shared_ptr的生命週期,更像是shared_ptr的一個助手。 weak_ptr沒有重載運算符*和->,由於它不共享指針,不能操做資源,主要是爲了經過shared_ptr得到資源的監測權,它的構造不會增長引用計數,它的析構不會減小引用計數,純粹只是做爲一個旁觀者來監視shared_ptr中關連的資源是否存在。 weak_ptr還能夠用來返回this指針和解決循環引用的問題。

  1. 智能指針的循環引用

44.  遇到coredump要怎麼調試???

  • core dump又叫核心轉儲。當程序運行過程當中發生異常, 程序異常退出時, 由操做系統把程序當前的內存情況存儲在一個core文件中, 叫core dump。

45.  內存檢查工具的瞭解???

linux可使用開源的Valgrind工具包,包含多個工具:Memcheck經常使用語檢測malloc和new這類的問題,callgrind用來檢查函數調用,cachegrind緩存使用,helgrind多線程程序中的競爭。除了valgrind還能夠用mtrace這些工具

46.  模板的用法與適用場景

代碼可重用,泛型編程,在不知道參數類型下,函數模板和類模板

47.  成員初始化列表的概念,爲何用成員初始化列表會快一些(性能優點)?

使用初始化list這樣能夠直接調用成員的構造函數,不用去賦值產生臨時變量,因此更快。若是不用,可能會去調用成員的構造函數和賦值構造函數(多出來的)。

48.  用過C++ 11嗎,知道C++ 11哪些新特性?

Ambda表達式, 智能指針 移動,auto,範圍for,decltype,array,forward_list,tuple(元組),正則表達式庫,隨機數庫,bitset運算

  • c++11提供的<random>實現了隨機數庫,它經過隨機數引擎(random_number_engines)產生隨機數序列,隨機數分佈類(random-number distribution)使用隨機數引擎生成服從特定機率分佈的隨機數。

http://www.javashuo.com/article/p-pqeqhgcx-hm.html

  • tuple是一個固定大小的不一樣類型值的集合,是泛化的std::pair

https://blog.csdn.net/fjb2080/article/details/15809097

tuple<const char*, int>tp = make_tuple(sendPack,nSendSize); //構造一個tuple

這個tuple等價於一個結構體

struct A

{

char* p;

int len;

};

用tuple<const char*, int>tp就能夠不用建立這個結構體了,而做用是同樣的,是否是更簡潔直觀了。還有一種方法也能夠建立元組,用std::tie,它會建立一個元組的左值引用。

auto tp = return std::tie(1, "aa", 2);

//tp的類型實際是:std::tuple<int&,string&, int&>

再看看如何獲取它的值:

const char* data = tp.get<0>(); //獲取第一個值

int len = tp.get<1>(); //獲取第二個值

  • 引入了emplace_front、emplace、emplace_back,這些操做構造而不是拷貝元素。這些操做分別對應於push_front、insert、push_back。Emplace成員使用這些參數在容器管理的內存空間中直接構造函數。例如,在c的末尾構造一個Sales_data

   c. emplace_back( 「666-556695」 , 25 , 52.36);///直接使用Sales_data的三個參數就能夠了

   c.push_back(「666-556695」 , 25 , 52.36); //錯誤,.push_back不接受3個參數

c.push_back( Sales_data(「666-556695」 , 25 , 52.36) ); //正確,建立一個臨時對象

 

49.  C++的調用慣例(簡單一點C++函數調用的壓棧過程)

函數調用你們都不陌生,調用者向被調用者傳遞一些參數,而後執行被調用者的代碼,最後被調用者向調用者返回結果。

對於程序,編譯器會對其分配一段內存,在邏輯上能夠分爲代碼段,數據段,堆,棧

代碼段:保存程序文本,指令指針EIP就是指向代碼段,可讀可執行不可寫

數據段:保存初始化的全局變量和靜態變量,可讀可寫不可執行

BSS(靜態內存分配):未初始化的全局變量和靜態變量

堆(Heap):動態分配內存,向地址增大的方向增加,可讀可寫可執行

棧(Stack):存放局部變量,函數參數,當前狀態,函數調用信息等,向地址減少的方向增加,很是很是重要,可讀可寫可執行

程序開始,從main開始,首先將參數壓入棧,而後壓入函數返回地址,進行函數調用,經過跳轉指定進入函數,將函數內部的變量去堆棧上開闢空間,執行函數功能,執行完成,取回函數返回地址,進行下一個函數。

 

 

 

50.  C++的四種強制轉換

  • static_cast:能夠實現相同類型之間的轉換,如:double b ;int  a = static_cast<int>(b);
  • reinterpreter_cast :能夠實現不一樣類型之間的轉換,如指針到整型

int* aas = new int[1] ;

aas[0] = 1;

int b = reinterpret_cast<int>(aas);   ////b中的值爲aas 指針的地址

  • const_cast :中的類型必須是指針、引用或指向對象成員類型的指針

const int constA = 10;

const int *pConstA = &constA;

int* b = const_cast<int*>(pConstA);

*b = *b + 1;

printf("%d ,%d",constA,*b);/////10  11     能夠發現constA中的值並無發生變化,由於constA是一個常量,若constA沒有const的修飾,最後的結果將是11  11

  • dynamic_cast實現的是多態類之間的的轉換

對於「向上轉換」(即派生類指針或引用類型轉換爲其基類類型),不管是指針仍是引用向上轉換都是安全地。

對於「向下轉型」有兩種狀況:

一種是基類指針所指對象是派生類類型的,這種轉換是安全的;另外一種是基類指針所指對象爲基類類型,在這種狀況下dynamic_cast在運行時作檢查,轉換失敗,返回結果爲0;

 

53.  volatile關鍵字

volatile關鍵字是一種限定符用來聲明一個對象在程序中能夠被語句外的東西修改,好比操做系統、硬件或併發執行線程。

遇到該關鍵字,編譯器再也不對該變量的代碼進行優化,再也不從寄存器中讀取變量的值,而是直接從它所在的內存中讀取值,即便它前面的指令剛剛從該處讀取過數據。並且讀取的數據馬上被保存。

通常說來,volatile用在以下的幾個地方:

(1)、中斷服務程序中修改的供其它程序檢測的變量須要加volatile;

(2)、多任務環境下各任務間共享的標誌應該加volatile;

(3)、存儲器映射的硬件寄存器一般也要加volatile說明,由於每次對它的讀寫均可能有不一樣

54.  優化程序的幾種方法

http://www.javashuo.com/article/p-hvuvuhyo-hp.html

一、 選擇合適的算法和數據結構

二、 使用盡可能小的數據類型

三、 減小運算的強度

四、 結構體成員的佈局

五、 循環優化

六、 提升CPU的並行性(使用並行代碼,把長的代碼分解成短的)

七、 循環不變計算(不使用的循環變量,放到外面)

八、 函數優化(內聯函數)

55.  public,protected和private訪問權限和繼承

 

 

無論哪一種繼承方式,父類的私有成員都不能夠訪問,只有間接的經過公有成員才能獲取到私有成員的值。

protected存在的意義是當我不想向外部暴露某個函數或者成員變量,可是我又想讓派生類知道和訪問這個成員,就將其用protected標誌。

將普通函數添加爲友元函數,能夠實如今該函數內調用類內的私有與保護成員

在類外定義對象,該對象只能訪問類內的公有成員二不能訪問其類內的私有與保護成員

類內的成員可使用類內的全部成員,繼承類可使用父類的保護成員函數

 

56.  decltype()和auto

  • decltype類型指示符

Const int c = 0,&b = c;

Decltype(c) x =3;//x是const int 類型

Decltype(b) y = x;//y是const int&類型;因此定義y時必須初始化

  • auto

使用auto也能在一條語句中聲明多個變量,由於一條語句只能有一個基本的數據類型。

Auto定義的變量必須有初值,這樣才能推導出類型是什麼

57.  inline和宏定義的區別

(1)內聯函數在編譯時展開,宏在預編譯時展開;

(2)內聯函數直接嵌入到目標代碼中,宏是簡單的作文本替換;

(3)內聯函數有類型檢測、語法判斷等功能,而宏沒有;

(4)inline函數是函數,宏不是;

(5)宏定義時要注意書寫(參數要括起來)不然容易出現歧義,內聯函數不會產生歧義;

  1. C++和C的類型安全

59.  調試程序的方法:

概括法、演繹法、測試法,回溯法,暴力法。設置斷點,進入調試,單步運行,進入函數,查看變量。linux gbk,win windbg

60.  類型別名

Typedef  double  wages; //wages是double的同義詞

Typedef  wages   base, *p; // base是double的同義詞,p是double*的同義詞

相關文章
相關標籤/搜索