1. 指針和引用的區別html
2. 堆和棧的區別linux
3. malloc/free與new/delete異同點,new和delete是如何實現的,new 與 malloc的異同處c++
相同點:程序員
不一樣點:正則表達式
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 []數組名。
4. C和C++的區別?面向對象編程的特色?
"面向過程的模型",按這種模型編寫的程序以一系列的線性步驟(代碼)爲特徵,可被理解爲做用於數據的代碼.
"面向對象的模型",按這種模型編寫的程序圍繞着程序的數據(對象)和針對該對象而嚴格定義的接口來組織程序,它的特色是數據控制代碼的訪問.經過把控制權轉移到數據上,面向對象的模型在組織方式上有:抽象,封裝,繼承和多態的好處.
(數據抽象)如一棵樹的高度、一本書的頁數等。不過,有些特徵並不能用這樣的辦法來表示,(過程抽象)如一隻狗會跑。用數據來表示這個跑的動做是作不到的,由於這是一個動做過程,而不是一種狀態。
多態指一樣的方法在不一樣的對象中有不一樣的表現方式。多態實現的方法,
靜態多態----函數重載 泛型編程
動態多態----虛函數
5. Struct和class的區別
當struct和class中都定義了構造函數,就不能使用大括號對其進行初始化
若沒有定義構造函數,struct可使用{ }進行初始化,而只有當class的全部數據成員及函數爲public時,可使用{ }進行初始化
因此struct更適合當作是一個數據結構的實現體,class更適合當作是一個對象的實現體。
6. define 和const的區別(編譯階段、安全性、內存佔用等)
define宏是在預處理階段展開。
const常量是編譯運行階段使用。
define宏沒有類型,不作任何類型檢查,僅僅是展開。
const常量有具體的類型,在編譯階段會執行類型檢查。
define宏僅僅是展開,有多少地方使用,就展開多少次,不會分配內存。
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定義的常量在內存中有若干個拷貝。
編譯器一般不爲普通const常量分配存儲空間,而是將它們保存在符號表中,這使得它成爲一個編譯期間的常量,沒有了存儲與讀內存的操做,使得它的效率也很高
7. C/C++中static和const關鍵字的做用總結
(1)函數體內 static 變量的做用範圍爲該函數體,不一樣於auto 變量,該變量的內存只被分配一次,所以其值在下次調用時仍維持上次的值;
(2)在模塊內的 static 全局變量能夠被模塊內所用函數訪問,但不能被模塊外其它函數訪問;
(3)在模塊內的 static 函數只可被這一模塊內的其它函數調用,這個函數的使用範圍被限制在聲明它的模塊內;
(4)在類中的 static 成員變量屬於整個類所擁有,對類的全部對象只有一份拷貝;
(5)在類中的 static 成員函數屬於整個類所擁有,這個函數不接收 this 指針,於是只能訪問類的 static 成員變量。
(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關鍵字
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
{
};
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");//直接初始化
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
優勢:
● 有序性,這是map結構最大的優勢,其元素的有序性在不少應用中都會簡化不少的操做
● 紅黑樹,內部實現一個紅黑書使得map的不少操做在lgn的時間複雜度下就能夠實現,所以效率很是的高
缺點:
● 空間佔用率高,由於map內部實現了紅黑樹,雖然提升了運行效率,可是由於每個節點都須要額外保存父節點,孩子節點以及紅/黑性質,使得每個節點都佔用大量的空間
● 適用處,對於那些有順序要求的問題,用map會更高效一些
unordered_map
優勢:
● 由於內部實現了哈希表,所以其查找速度很是的快
缺點:
● 哈希表的創建比較耗費時間
● 適用處,對於查找問題,unordered_map會更加高效一些,所以遇到查找問題,常會考慮一下用unordered_map
16. STL中vector的實現
注意兩個點:
1.vector有備用空間,當備用空間不夠的時候,會從新開闢原空間兩倍的空間進行重寫分配。
2.vector支持隨機的存取,可是最好是選擇從末尾插入,由於從中間插入會致使元素的移動,帶來了性能的開銷。
17. STL容器的幾種迭代器以及對應的容器(輸入迭代器,輸出迭代器,前向迭代器,雙向迭代器,隨機訪問迭代器)
19. vector使用的注意點及其緣由,頻繁對vector調用push_back()對性能的影響和緣由。
20. C++中的重載和重寫的區別
21. C++內存管理,內存池技術(熱門問題),與csapp中幾種內存分配方式對比學習加深理解???
c++的內存管理延續c語言的內存管理,可是也增長了其餘的,例如智能指針,除了常見的堆棧的內存管理以外,c++支持智能指針,智能指針的對象進行賦值拷貝等操做的時候,每一個智能指針都有一個關聯的計數器,該計數器記錄共享該對象的指針個數,當最後一個指針被銷燬的時候,計數器爲0,會自動調用析構函數來銷燬函數。
22. 介紹面向對象的三大特性,而且舉例說明每個
繼承,多態,封裝。
23. C++多態的實現
多態經過覆蓋和重載來完成。
24. C++虛函數相關(虛函數表,虛函數指針),虛函數的實現原理(包括單一繼承,多重繼承等)(拓展問題:爲何基類指針指向派生類對象時能夠調用派生類成員函數,基類的虛函數存放在內存的什麼區,虛函數表指針vptr的初始化時間)
虛函數分爲兩種,純虛函數和虛函數,純虛函數適用於抽象基類,不須要定義,相似一種接口,是多態的典型處理方式。
一個類若是定義了虛函數,那麼編譯器會自動爲它加上一個虛函數表,並提供一個指向虛函數表的指針,子類經過繼承,能夠覆蓋父類的虛函數,當用戶調用虛函數的時候,會調用指針,去虛函數表中找匹配的虛函數,若是當前對象有覆蓋的虛函數,則去執行覆蓋的虛函數,不然執行父類的虛函數。
26. this指針
27. 析構函數通常寫成虛函數的緣由
delete父類指針時,能夠調用對象真正的析構函數。不然可能會出錯,如子類中有指針類型,而且申請了內存,這時就會形成內存泄漏。
28. 構造函數、拷貝構造函數和賦值操做符的區別
30. 構造函數爲何通常不定義爲虛函數
三個緣由:
1.虛函數的做用是什麼?是實現部分或默認的功能,並且該功能能夠被子類所修改。若是父類的構造函數設置成虛函數,那麼子類的構造函數會直接覆蓋掉父類的構造函數。而父類的構造函數就失去了一些初始化的功能。這與子類的構造須要先完成父類的構造的流程相違背了。而這個後果會至關嚴重。
2.虛函數的調用是須要經過「虛函數表」來進行的,而虛函數表也須要在對象實例化以後纔可以進行調用。在構造對象的過程當中,尚未爲「虛函數表」分配內存。因此,這個調用也是違背先實例化後調用的準則。
3.虛函數的調用是由父類指針進行完成的,而對象的構造則是由編譯器完成的,因爲在建立一個對象的過程當中,涉及到資源的建立,類型的肯定,而這些是沒法在運行過程當中肯定的,須要在編譯的過程當中就肯定下來。而多態是在運行過程當中體現出來的,因此是不可以經過虛函數來建立構造函數的,與實例化的次序不一樣也有關係。
31. 構造函數的幾種關鍵字(default delete 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. 對象複用的瞭解,零拷貝的瞭解
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. 內存泄露的定義,如何檢測與避免?
42. 手寫智能指針的實現(shared_ptr和weak_ptr實現的區別)
shared_ptr使用引用計數,每個shared_ptr的拷貝都指向相同的內存。在最後一個shared_ptr析構的時候,內存纔會被釋放。
注意事項:
1.不要用一個原始指針初始化多個shared_ptr。
2.不要再函數實參中建立shared_ptr,在調用函數以前先定義以及初始化它。
3.不要將this指針做爲shared_ptr返回出來。
4.要避免循環引用。
<1>Unique_ptr是一個獨佔的智能指針,他不容許其餘的智能指針共享其內部的指針,不容許經過賦值將一個unique_ptr賦值給另一個 unique_ptr。
<2>unique_ptr不容許複製,但能夠經過函數返回給其餘的unique_ptr,還能夠經過std::move來轉移到其餘的unique_ptr,這樣它自己就再也不 擁有原來指針的全部權了。
<3>若是但願只有一個智能指針管理資源或管理數組就用unique_ptr,若是但願多個智能指針管理同一個資源就用shared_ptr。
弱引用的智能指針weak_ptr是用來監視shared_ptr的,不會使引用計數加一,它無論理shared_ptr內部的指針,主要是爲了監視shared_ptr的生命週期,更像是shared_ptr的一個助手。 weak_ptr沒有重載運算符*和->,由於它不共享指針,不能操做資源,主要是爲了經過shared_ptr得到資源的監測權,它的構造不會增長引用計數,它的析構不會減小引用計數,純粹只是做爲一個旁觀者來監視shared_ptr中關連的資源是否存在。 weak_ptr還能夠用來返回this指針和解決循環引用的問題。
44. 遇到coredump要怎麼調試???
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運算
http://www.javashuo.com/article/p-pqeqhgcx-hm.html
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>(); //獲取第二個值
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++的四種強制轉換
int* aas = new int[1] ;
aas[0] = 1;
int b = reinterpret_cast<int>(aas); ////b中的值爲aas 指針的地址
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在運行時作檢查,轉換失敗,返回結果爲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
Const int c = 0,&b = c;
Decltype(c) x =3;//x是const int 類型
Decltype(b) y = x;//y是const int&類型;因此定義y時必須初始化
使用auto也能在一條語句中聲明多個變量,由於一條語句只能有一個基本的數據類型。
Auto定義的變量必須有初值,這樣才能推導出類型是什麼
57. inline和宏定義的區別
(1)內聯函數在編譯時展開,宏在預編譯時展開;
(2)內聯函數直接嵌入到目標代碼中,宏是簡單的作文本替換;
(3)內聯函數有類型檢測、語法判斷等功能,而宏沒有;
(4)inline函數是函數,宏不是;
(5)宏定義時要注意書寫(參數要括起來)不然容易出現歧義,內聯函數不會產生歧義;
59. 調試程序的方法:
概括法、演繹法、測試法,回溯法,暴力法。設置斷點,進入調試,單步運行,進入函數,查看變量。linux gbk,win windbg
60. 類型別名
Typedef double wages; //wages是double的同義詞
Typedef wages base, *p; // base是double的同義詞,p是double*的同義詞