1. 程式人生 > >Cpp面試高頻題

Cpp面試高頻題

(1) 指標和引用的區別

引用:
1.引用初始化完成,將一直繫結一個物件,無法令引用繫結另外一個物件,這就是說引用必須初始化。
2.注意引用初始化的時候,繫結的是一個物件,而引用本身不是一個物件,所以不能定義引用的引用
3.引用的物件型別之間必須匹配

指標:
1.指標本身是一個物件,對物件的操作,對指標同樣可以,例如拷貝賦值等
2.指標沒有規定必須初始化,指標沒有初始化,編譯器會預設分配一個不確定的值。
3.引用不是物件,指標不能指向某個引用
4.指標的型別也必須匹配

(2) 堆和棧的區別

在c++中記憶體的分配分為堆和棧兩種,其中由系統系統自動分配的是棧,由程式設計師主動向作業系統申請的是堆。

棧:由程式自動向作業系統申請分配以及回收,速度快,使用方便,但程式設計師無法控制。若分配失敗,則提示棧溢位錯誤。注意,const區域性變數也儲存在棧區內,棧區向地址減小的方向增長。即建立變數的時候,後建立變數的地址越小。

堆:程式設計師向作業系統申請一塊記憶體,當系統收到程式的申請時,會遍歷一個記錄空閒記憶體地址的連結串列,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閒結點連結串列中刪除,並將該結點的空間分配給程式。分配的速度較慢,地址不連續,容易碎片化。此外,由程式設計師申請,同時也必須由程式設計師負責銷燬,否則則導致記憶體洩露。

堆疊的比較:
使用棧就象我們去飯館裡吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。
使用堆就象是自己動手做喜歡吃的菜餚,比較麻煩,但是比較符合自己的口味,而且自由度大。

在資料結構上,也存在有堆和棧的區別:

棧:是一種連續儲存的資料結構,具有先進後出的性質。通常的操作有入棧(圧棧)、出棧和棧頂元素。想要讀取棧中的某個元素,就要將其之前的所有元素出棧才能完成。

堆:是一種非連續的樹形儲存資料結構,每個節點有一個值,整棵樹是經過排序的。特點是根結點的值最小(或最大),且根結點的兩個子樹也是一個堆。常用來實現優先佇列,存取隨意。
https://www.cnblogs.com/ChenZhongzhou/p/5685537.html

(3) new和delete是如何實現的,new 與 malloc的異同處

new/delete是c++的操作運算子,能夠完成動態記憶體的分配和初始化工作,後者可以完成清理和釋放記憶體的工作,並且在呼叫的時候,編譯器會自動執行物件的建構函式和解構函式。new一個物件的時候,編譯器自動呼叫建構函式,通過從記憶體堆上分配一定的空間,構造出物件,當呼叫delelte的時候呼叫物件的解構函式,銷燬物件。

malloc/free與new/delete異同點

相同點

malloc/free與new/delete都可以用於申請動態記憶體和釋放記憶體,他們申請的空間都在堆上分配。

不同點

1)操作物件不同
malloc/free是C++/C語言的標準庫檔案,new/delete是C++的運算子;
對非內部資料物件,malloc/free無法滿足動態物件要求。物件在建立時要自動執行建構函式,物件消亡之前要自動執行解構函式,而malloc/free是庫函式,不是運算子,故不在編譯器控制權限之內,不能夠將執行建構函式和解構函式強加於malloc/free身上。而由於new/delete是C++語言,能夠完成動態記憶體分配和初始化工作,並能夠完成清理與釋放記憶體工作,即能夠自動執行建構函式和解構函式;

2)用法不同
malloc分配記憶體空間前需要計算分配記憶體大小;而new能夠自動分配記憶體空間;
malloc是底層函式,其函式返回值型別為void *;而new運算子呼叫無參建構函式,故返回值為對應物件的指標;
malloc函式型別不是安全的,編譯器不對其進行型別轉換、型別安全的相關檢查。malloc申請空間後,不會對其初始化,要單獨初始化;而new型別是安全的,因為它內建了sizeof、型別轉換和型別安全檢查功能,且在建立物件時,就完成了初始化工作,一般初始化呼叫無參建構函式;

operator new對應於malloc,且operator new可以過載,可以自定義記憶體分配策略,甚至不做記憶體分配,甚至分配到非記憶體裝置上;但malloc不能。

free只進行釋放空間;而delete則釋放空間的同時呼叫解構函式。
此外delete使用是注意釋放陣列的方法為delete []陣列名。

聯絡

new和delete功能覆蓋了malloc/free,但因C++程式常會用到C函式,而C函式只能使用malloc/free管理動態記憶體。此外,使用是malloc和free搭配使用,new和delete搭配使用,不能混亂使用。
http://blog.csdn.net/yzhang6_10/article/details/51136018

(4) C和C++的區別

C語言是面向過程的,C++是面向物件的

C++包括普通的C程式設計,還有面向物件的程式設計,同時有有泛型程式設計,模板程式設計等新功能。

從專案上講,C語言更過的是具象,C語言更注重功能,按照功能邏輯一個個程式設計,而C++設計出發點就是不同的,它是一種抽象,將設計用抽象的方式表達出來。C++很明顯在抽象,設計模式,封裝上有C語言完全不能比的特性,這些特性在實際上專案後期的維護中會帶來很大的改變。

(5) C++、Java的聯絡與區別,包括語言特性、垃圾回收、應用場景等(java的垃圾回收機制)

聯絡:

兩者都是面向物件的語言,兩者都能實現面向物件的核心思想(封裝、繼承、多型)。但是由於c++為了相容c語言,java不相容C,它是一種完全的面嚮物件語言。

區別:

語言特性:

1.指標:c++有指標來訪問記憶體,而JAVA中對於使用者態,程式設計者無法找到指標來直接訪問記憶體指標,只有限定版的引用,更加安全。
2.多重繼承:c++支援多重繼承,可以繼承多個父類。JAVA不支援多重繼承,但是允許一個類繼承多個介面。
3.資料型別和類:Java是完全面向物件的語言,所有函式和變數都必須是類的一部分。除了基本資料型別之外,其餘的都作為類物件,包括陣列。物件將資料和方法結合起來,把它們封裝在類中,這樣每個物件都可實現自己的特點和行為。而c++允許將函式和變數定義為全域性的。
4.自動記憶體管理:java記憶體支援自動對無用記憶體回收管理,c++需要人為的使用delete去釋放記憶體。
5.c++支援操作符過載,但是java不支援操作符過載

垃圾回收:

java記憶體支援自動對無用記憶體回收管理,c++需要人為的使用delete去釋放記憶體。

應用場景:

C++相對於java來看是偏底層的語言,應用場景也是一些偏底層的軟體,例如:影象,客戶端,桌面軟體等。
JAVA則是偏向應用的語言,相對來說,生態圈較好,有一些高階特性也比較好用,一般是上層應用軟體,例如移動裝置的軟體,web網頁後臺邏輯開發等等。

(6) struct和class的區別

在c++中,使用class和struct的關鍵字區別是,預設的訪問許可權不一樣,如果用struct去定義,則第一個訪問說明符之前,預設的是public的,但是class,第一個訪問說明符之前,預設的就是privite的區別,在定義類上這是唯一的區別。

繼承:class繼承預設是private繼承,而struct繼承預設是public繼承

(7) define 和const的區別(編譯階段、安全性、記憶體佔用等)

編譯階段:define 是預編譯階段展開,而const是在執行階段使用

安全性:const常量是有資料型別的,那麼編譯器會對const變數的型別等安全性進行檢查,但是define只是在預編譯階段展開,不會進行型別的安全檢查,替換時可能產生安全錯誤。

記憶體佔用:define不會佔用記憶體,單純的替換而已,const會佔用記憶體,會有對應的記憶體地址。

(8) 在C++中const和static的用法(定義,用途)

定義:

在C++中,const成員變數也不能在類定義處初始化,只能通過建構函式初始化列表進行,並且必須有建構函式。

const資料成員 只在某個物件生存期內是常量,而對於整個類而言卻是可變的。因為類可以建立多個物件,不同的物件其const資料成員的值可以不同。所以不能在類的宣告中初始化const資料成員,因為類的物件沒被建立時,編譯器不知道const資料成員的值是什麼。

const資料成員的初始化只能在類的建構函式的初始化列表中進行。要想建立在整個類中都恆定的常量,應該用類中的列舉常量來實現,或者static cosnt。

static表示的是靜態的。類的靜態成員函式、靜態成員變數是和類相關的,而不是和類的具體物件相關的。即使沒有具體物件,也能呼叫類的靜態成員函式和成員變數。一般類的靜態函式幾乎就是一個全域性函式,只不過它的作用域限於包含它的檔案中。

在C++中,static靜態成員變數不能在類的內部初始化。在類的內部只是宣告,定義必須在類定義體的外部,通常在類的實現檔案中初始化,如:double Account::Rate=2.25;static關鍵字只能用於類定義體內部的宣告中,定義時不能標示為static。

用途

cosnt成員函式主要目的是防止成員函式修改物件的內容。即const成員函式不能修改成員變數的值,但可以訪問成員變數。當方法成員函式時,該函式只能是const成員函式。

static成員函式主要目的是作為類作用域的全域性函式。不能訪問類的非靜態資料成員。類的靜態成員函式沒有this指標,這導致:1、不能直接存取類的非靜態成員變數,呼叫非靜態成員函式2、不能被宣告為virtual.
https://www.cnblogs.com/phpzhou/p/6390869.html

(9) const和static在類中使用的注意事項(定義、初始化和使用)

定義:

const可以在類內部定義,但是定義的位置不能初始化;static只能在類內部宣告,定義只能在類的外面,並且定義的時候不能加static關鍵字

初始化:

const不能再定義的位置初始化,只能在類的建構函式的初始化列表中初始化;static初始化不能再類的內部進行初始化,必須在外部定義的時候初始化。

使用:

const的使用主要目的是防止成員函式修改物件的內容,即const成員函式不能修改成員變數的值,但可以訪問成員變數。
static的使用目的是作為類作用域的全域性函式。不能訪問類的非靜態資料成員,類的靜態成員函式沒有this指標,這導致不能直接存取類的非靜態成員變數,呼叫非靜態成員函式,不能宣告為virtual.

https://www.cnblogs.com/phpzhou/p/6390869.html

(10) C++中的const類成員函式(用法和意義)

const類成員函式有這麼幾種:

1.void fun() const;
表明是常量成員函式,這個const表明了該函式不會改變任何成員資料的值。
2.void fun(const a) const;
表明是引數是常量的常量成員函式,接收的引數是常量,同時不能改變成員資料的值。

意義:為什麼要這麼做?

這是為了保證它能被const常量物件呼叫,我們都知道,在定義一個物件或者一個變數時,如果在型別前加一個const,如const int x;,則表示定義的量為一個常量,它的值不能被修改。但是建立的物件卻可以呼叫成員函式,呼叫的成員函式很有可能改變物件的值。所以這個時候const類成員函式就出現了。

於是,我們把那些肯定不會修改物件的各個屬性值的成員函式加上const說明符,這樣,在編譯時,編譯器將對這些const成員函式進行檢查,如果確實沒有修改物件值的行為,則檢驗通過。以後,如果一個const常物件呼叫這些const成員函式的時候,編譯器將會允許。
http://blog.csdn.net/u010661782/article/details/49020595

(11) 計算下面幾個類的大小:

class A {};: sizeof(A) = 1;
class A { virtual Fun(){} };: sizeof(A) = 4(32位機器)/8(64位機器);
class A { static int a; };: sizeof(A) = 1;
class A { int a; };: sizeof(A) = 4;
class A { static int a; int b; };: sizeof(A) = 4;

1.確切的說,類只是一個型別定義,它是沒有大小可言的。 用sizeof運算子對一個型別名操作,得到的是具有該型別實體的大小。

2.一個物件的大小大於等於所有非靜態成員大小的總和,大於的部分是由編譯器自主新增的。
C++標準規定類的大小不為0,空類的大小為1,當類不包含虛擬函式和非靜態資料成員時,其物件大小也為1。 如果在類中聲明瞭虛擬函式(不管是1個還是多個),那麼在例項化物件時,編譯器會自動在物件裡安插一個指標指向虛擬函式表VTable,在32位機器上,一個物件會增加4個位元組來儲存此指標,它是實現面向物件中多型的關鍵。而虛擬函式本身和其他成員函式一樣,是不佔用物件的空間的。

3.物件佔空的記憶體不算成員函式的佔空的記憶體,只算成員變數的記憶體。

所以:

空類,編譯器為了能存放物件,給他自動分配了一個位元組的地址
虛擬函式的A,存在有虛擬函式表,編譯器給這個類添加了指向虛擬函式表的函式指標,主要這是必須的,指標的大小取決於實體地址的大小
靜態變數,靜態變數是是不包括在這個類其中的,所以算類的大小是沒有靜態成員變數的。
int,存在成員變數,相當於計算成員變數的大小,int型4個位元組
同樣的道理,靜態變數的大小是不包括在這個類當中的。

(12) 給一個程式碼,求輸出結果

class A
{
public:
A(int x){}
}

問:A a = 1;是否正確, 如果正確, 那麼它呼叫了哪些函式?

這類題目更常見的是在基類和子類有不同實現方法。(虛擬函式相關,栗子很多,不多說了)

正確。由於A沒有顯示的宣告,所以可以用int型進行強制轉換,編譯器碰到這種情況,
首先,如果是沒有優化的編譯器,對1進行強制轉換int型,然後進行呼叫預設的賦值函式,1賦值給x,然後呼叫建構函式構造。

其次,如果是有優化的編譯器,這個時候編譯器可能會直接將1強制轉換為A型別,呼叫了一次建構函式,然後在賦值給a,這個時候呼叫的是預設的賦值建構函式。

(13) C++的STL介紹(這個系列也很重要,建議侯捷老師的這方面的書籍與視訊),其中包括記憶體管理allocator,函式,實現機理,多執行緒實現等

STL是一個c++裡面非常強大的庫,c11引進的,裡面封裝例如容器,泛型演算法等。

(14) STL原始碼中的hash表的實現

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) vector使用的注意點及其原因,頻繁對vector呼叫push_back()對效能的影響和原因。

vector壓入容器的物件都是拷貝操作,而且vector的資料存放都是連續儲存的,所以在操作vector操作時,應該儘量避免對尾部操作之後的地方插入刪除操作,因為這樣會造成元素的移動,造成大量的開銷。

頻繁對vector呼叫push_back()會導致效能下降,這是由於系統每次給vector分配固定大小的空間,這個空間可能比使用者想分配的空間大一些,但是頻繁的使用push_back向容器中插入元素,會導致記憶體分配空間不夠,會再次將整個物件的儲存空間重新分配,將舊的元素移動到新的空間中,這樣扥開銷是非常大的,所以不能頻繁的對vector呼叫push_back()。

(18) C++中的過載和重寫的區別:

過載:函式名相同,函式的引數個數、引數型別或引數順序三者中必須至少有一種不同。函式返回值的型別可以相同,也可以不相同。發生在一個類內部。

重定義:也叫做隱藏,子類重新定義父類中有相同名稱的非虛擬函式 ( 引數列表可以不同 ) ,指派生類的函式遮蔽了與其同名的基類函式。發生在繼承中,查詢方式是從子類到父類。

重寫:也叫做覆蓋,一般發生在子類和父類繼承關係之間。子類重新定義父類中有相同名稱和引數的虛擬函式。
http://blog.csdn.net/u010275850/article/details/45583705

(19) C ++記憶體管理(熱門問題)

c++的記憶體管理延續c語言的記憶體管理,但是也增加了其他的,例如智慧指標,除了常見的堆疊的記憶體管理之外,c++支援智慧指標,智慧指標的物件進行賦值拷貝等操作的時候,每個智慧指標都有一個關聯的計數器,該計數器記錄共享該物件的指標個數,當最後一個指標被銷燬的時候,計數器為0,會自動呼叫解構函式來銷燬函式。

(20) 介紹面向物件的三大特性,並且舉例說明每一個。

面向物件的三大特性:封裝、繼承、多型。

封裝:將很多有相似特性的內容封裝在一個類中,例如學生的成績學號、課程這些可以封裝在同一個類中;

繼承:某些相似的特性,可以從一個類繼承到另一個類,類似生活中的繼承,例如有個所有的汽車都有4個輪子,那麼我們在父類中定義4個輪子,通過繼承獲得4個輪子的功能,不用再類裡面再去定義這4個輪子的功能。

多型:多型指的相同的功能,不同的狀態,多型在面向物件c++裡面是通過過載和覆蓋來完成的,覆蓋在c++裡面通過虛擬函式來完成的。例如鴨子的例子,所有的鴨子都有顏色,我們可以將這個顏色設定成為一個虛擬函式,通過繼承子類對虛擬函式進行覆蓋,不同子類中有各自的顏色,也就是有各自不同的鴨子顏色,這就是多型的典型表現之一。

(21) 多型的實現(和下個問題一起回答)

(22) C++虛擬函式相關(虛擬函式表,虛擬函式指標),虛擬函式的實現原理(熱門,重要)

多型通過覆蓋和過載來完成。

虛擬函式分為兩種,純虛擬函式和虛擬函式,純虛擬函式適用於抽象基類,不需要定義,類似一種介面,是多型的典型處理方式。

一個類如果定義了虛擬函式,那麼編譯器會自動為它加上一個虛擬函式表,並提供一個指向虛擬函式表的指標,子類通過繼承,可以覆蓋父類的虛擬函式,當用戶呼叫虛擬函式的時候,會呼叫指標,去虛擬函式表中找匹配的虛擬函式,如果當前物件有覆蓋的虛擬函式,則去執行覆蓋的虛擬函式,否則執行父類的虛擬函式。

(23) 實現編譯器處理虛擬函式表應該如何處理

虛擬函式表的主要目的是提供一張表,表上記錄子類父類的虛擬函式地址,通過虛擬函式表可以達到動態繫結的目的。

編譯器首先在編譯階段完成虛擬函式表的建立,然後當給父類的指標初始化指向的是哪個子類,編譯器按照繫結的子類去虛擬函式表中找對應子類的虛擬函式,並繫結,這樣達到了動態繫結的目的,主要這個繫結是發生在執行的階段。

(24) 解構函式一般寫成虛擬函式的原因

因為在繼承中,我們最後要銷燬物件的時候,會呼叫解構函式,這個時候我們希望析構的是子類的物件,那麼我們需要呼叫子類的解構函式,但是這個時候指標又是父類的指標,所以這個時候我們也要對解構函式寫成虛構函式,這樣解構函式的虛屬性也會被繼承,那麼無論我們什麼時候析構,都能動態繫結到我們需要析構的物件上。

(25) 建構函式為什麼一般不定義為虛擬函式

三個原因:
1.虛擬函式的作用是什麼?是實現部分或預設的功能,而且該功能可以被子類所修改。如果父類的建構函式設定成虛擬函式,那麼子類的建構函式會直接覆蓋掉父類的建構函式。而父類的建構函式就失去了一些初始化的功能。這與子類的構造需要先完成父類的構造的流程相違背了。而這個後果會相當嚴重。

2.虛擬函式的呼叫是需要通過“虛擬函式表”來進行的,而虛擬函式表也需要在物件例項化之後才能夠進行呼叫。在構造物件的過程中,還沒有為“虛擬函式表”分配記憶體。所以,這個呼叫也是違背先例項化後呼叫的準則。

3.虛擬函式的呼叫是由父類指標進行完成的,而物件的構造則是由編譯器完成的,由於在建立一個物件的過程中,涉及到資源的建立,型別的確定,而這些是無法在執行過程中確定的,需要在編譯的過程中就確定下來。而多型是在執行過程中體現出來的,所以是不能夠通過虛擬函式來建立建構函式的,與例項化的次序不同也有關係。

那麼虛夠函式為什麼可以設計成虛擬函式呢?由於虛擬函式是釋放物件的時候才執行的,所以一開始也就無法確定析夠函式的。而去由於析構的過程中,是先析構子類物件,後析構父類物件。所以,需要通過虛擬函式來指引子類物件。所以,如果不設定成虛擬函式的話,解構函式是無法執行子類的解構函式的。

http://blog.csdn.net/helinlin007/article/details/51540182

(26) 建構函式或者解構函式中呼叫虛擬函式會怎樣?

程式會崩潰

為什麼呢?這是由於建構函式或者解構函式中呼叫虛擬函式這個時候,子類或許出於一個未初始化的狀態,因為c++中父類先構造然後是子類,那麼父類中構造呼叫子類,都沒有構造,呼叫子類的虛擬函式,顯然是錯誤的。

http://blog.csdn.net/linpengbin/article/details/51560276

(27) 純虛擬函式

純虛擬函式類似java中的介面,因為在實際的一些策略中,我們並不關心使用者建立一個物件,而是希望 有一個通用的概念,或者說是介面,這個就是純虛擬函式的目的。

純虛擬函式不需要定義,我們不能夠為純虛擬函式提供函式體,同樣的,包含純虛擬函式的基類是抽象基類,抽象基類是不能建立物件的,只能通過繼承,繼承子類中覆蓋純虛擬函式,執行自己的功能,子類是可以建立物件的。

(28) 靜態繫結和動態繫結的介紹

靜態繫結:通過使用者定義指標指向的型別來進行繫結,在編譯的時候已經完成。

動態邦迪:c++中虛擬函式的功能,通過虛擬函式表,在執行階段進行繫結,即執行的時候才知道繫結的函式。

(29) 引用是否能實現動態繫結,為什麼引用可以實現

可以實現,因為動態繫結是發生在程式執行階段的,c++中動態繫結是通過對基類的引用或者指標呼叫虛擬函式時發生。

因為引用或者指標的物件是可以在編譯的時候不確定的,如果是直接傳物件的話,在程式編譯的階段就會完成,對於引用,其實就是地址,在編譯的時候可以不繫結物件,在實際執行的時候,在通過虛擬函式繫結物件即可。

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

深拷貝就是拷貝內容,淺拷貝就是拷貝指標

淺拷貝拷貝指標,也就是說同一個物件,拷貝了兩個指標,指向了同一個物件,那麼當銷燬的時候,可能兩個指標銷燬,就會導致記憶體洩漏的問題。

深拷貝不存在這個問題,因為是首先申請和拷貝資料一樣大的記憶體空間,把資料複製過去。這樣拷貝多少次,就有多少個不同的記憶體空間,干擾不到對方

https://www.cnblogs.com/always-chang/p/6107437.html

(31) 物件複用的瞭解,零拷貝的瞭解

物件複用指得是設計模式,物件可以採用不同的設計模式達到複用的目的,最常見的就是繼承和組合模式了。

零拷貝:零拷貝主要的任務就是避免CPU將資料從一塊儲存拷貝到另外一塊儲存,主要就是利用各種零拷貝技術,避免讓CPU做大量的資料拷貝任務,減少不必要的拷貝,或者讓別的元件來做這一類簡單的資料傳輸任務,讓CPU解脫出來專注於別的任務。這樣就可以讓系統資源的利用更加有效。

零拷貝技術常見linux中,例如使用者空間到核心空間的拷貝,這個是沒有必要的,我們可以採用零拷貝技術,這個技術就是通過mmap,直接將核心空間的資料通過對映的方法對映到使用者空間上,即物理上共用這段資料。

https://www.jianshu.com/p/fad3339e3448

(32) 介紹C++所有的建構函式

預設建構函式、一般建構函式、拷貝建構函式

預設建構函式(無引數):如果建立一個類你沒有寫任何建構函式,則系統會自動生成預設的建構函式,或者寫了一個不帶任何形參的建構函式

一般建構函式:一般建構函式可以有各種引數形式,一個類可以有多個一般建構函式,前提是引數的個數或者型別不同(基於c++的過載函式原理)

拷貝建構函式引數為類物件本身的引用,用於根據一個已存在的物件複製出一個新的該類的物件,一般在函式中會將已存在物件的資料成員的值複製一份到新建立的物件中。引數(物件的引用)是不可變的(const型別)。此函式經常用在函式呼叫時使用者定義型別的值傳遞及返回。

(33) 什麼情況下會呼叫拷貝建構函式(三種情況)

(1)用類的一個物件去初始化另一個物件時
(2)當函式的形參是類的物件時(也就是值傳遞時),如果是引用傳遞則不會呼叫
(3)當函式的返回值是類的物件或引用時

(34) 結構體記憶體對齊方式和為什麼要進行記憶體對齊?

1.前面的地址必須是後面的地址正數倍,不是就補齊
2.整個Struct的地址必須是最大位元組的整數倍

為什麼要?

空間換時間,加快cpu訪問記憶體的效率,這是因為許多計算機系統對基本資料型別合法地址做出了一些限制,要求某種型別物件的地址必須是某個值K(通常是2、4或8)的倍數。這種對齊限制簡化了形成處理器和儲存器系統之間介面的硬體設計

https://www.cnblogs.com/jijiji/p/4854581.html

(35) 記憶體洩露的定義,如何檢測與避免?

記憶體洩漏指的是開闢的記憶體沒有釋放,或者是存在使用者操作的錯誤,導致野指標,無法釋放原來分配的記憶體。

工具監測:在vs裡面支援CRT這個庫函式,函式裡面有記憶體監測工具,可以呼叫,在程式中判斷記憶體時否有洩漏。

人為監測:觀測所有new開闢記憶體空間的地方有沒有free掉。

避免:在程式設計習慣上要注意使用盡量使用STL函式,使用vector而不是陣列,使用智慧指標而不是指標。

(36) 手寫實現智慧指標類

template <class T> class SmartPointer {
public:
	//普通建構函式, 設定T * ptr的值,並將引用計數設為1
	SmartPointer(T * ptr) {
		ref = ptr;
		ref_count = new unsigned;
		*ref_count = 1;
	}
	
	//指標拷貝建構函式,新建一個指向已有物件的智慧指標
	//需要先設定ptr和ref_count
	//設為指向sptr的ptr和ref_count
	//並且,因為新建了一個ptr的引用,所以引用計數加一
	SmartPointer(SmartPointer<T> &sptr) {
		ref = sptr.ref;
		ref_count = sptr.ref_count;
		++(*ref_count);
	}

	//rewrite "="
	SmartPointer<T> & operator = (SmartPointer<T> &sptr) {
        //同一個指標,直接返回
        if (this == &sptr)
            return *this;

        //如果計數值大於1,則舊指標計數值-1
		if(*ref_count > 0)
            remove();

        //把舊指標值給新指標
		ref = sptr.ref;
		ref_count = sptr.ref_count;

        //指標計數+1
		++(*ref_count);
		return *this;
	}
	~SmartPointer() {
		remove();
	}

	T getValue() {
		return *ref;
	}

	T getCount() {
		return static_cast<T>(*ref_count);
	}
protected:
    //刪除指標
	void remove() {
		--(*ref_count);

		//如果計數值等於0,則銷燬指標,並執行解構函式
		if (*ref_count == 0) {
			delete ref;
			delete ref_count;
			ref = NULL;
			ref_count = NULL;
		}
	}
private:
	unsigned * ref_count;    //應用計數值
	T * ref;                 //普通指標
};

(37) 除錯程式的方法

這個方式很多,裸機程式,主動除錯,gdb除錯,IDE斷點除錯等。

(38) 遇到coredump要怎麼除錯

記憶體洩漏的方法很多,可以用gdb開啟core檔案,確定出錯的堆疊地點,從而判斷程式出錯的位置。

(39) 記憶體檢查工具的瞭解

在vs裡面支援CRT這個庫函式

(40) 模板的用法與適用場景

模板是C11裡面新增的,使用與在不知道型別的情況下,編寫一個泛型的程式,模板通過用一個指定的關鍵字來代替型別,進行泛型程式設計。

應用場景:應用場景很多,例如我們要程式設計一些和型別無關的程式碼時,STL裡面的很多容器都是用到了模板,容器的功能都可以使用,但並沒有確定容器裡面一定要用指定的型別,可以是任何的型別。

(41) 成員初始化列表的概念,為什麼用成員初始化列表會快一些(效能優勢)?

成員初始化的概念,就是說類的成員使用在定義的時候就使用建構函式初始值列表初始化

使用成員初始化要快些,這裡說的快些是比較的是賦值,如果指定定義變數,沒有列表初始化,那麼這樣變數舊會執行預設的初始化,然後在賦值,這樣就多了一次賦值操作,帶來的開銷取決於資料成員的型別。

除了效率之外,有一些成員必須列表初始化,例如const或者引用

(42) 用過C11嗎,知道C11新特性嗎?(有面試官建議熟悉C11)

用過,C11有許多新的特性

例如:auto、decltype,nullptr,for(auto i:m),lambda表示式,智慧指標等。

(43) C++的呼叫慣例(簡單一點C++函式呼叫的壓棧過程)

函式呼叫大家都不陌生,呼叫者向被呼叫者傳遞一些引數,然後執行被呼叫者的程式碼,最後被呼叫者向呼叫者返回結果。

對於程式,編譯器會對其分配一段記憶體,在邏輯上可以分為程式碼段,資料段,堆,棧

程式碼段:儲存程式文字,指令指標EIP就是指向程式碼段,可讀可執行不可寫
資料段:儲存初始化的全域性變數和靜態變數,可讀可寫不可執行
BSS:未初始化的全域性變數和靜態變數
堆(Heap):動態分配記憶體,向地址增大的方向增長,可讀可寫可執行
棧(Stack):存放區域性變數,函式引數,當前狀態,函式呼叫資訊等,向地址減小的方向增長,非常非常重要,可讀可寫可執行

程式開始,從main開始,首先將引數壓入棧,然後壓入函式返回地址,進行函式呼叫,通過跳轉指定進入函式,將函式內部的變數去堆疊上開闢空間,執行函式功能,執行完成,取回函式返回地址,進行下一個函式。

(44) C++的四種強制轉換

四種強制轉換是static_cast、dynamic_cast、const_cast、reinterpret_cast。

static_cast:靜態強制轉換,類似傳統c語言裡面括號的強制轉換
dynamic_cast:動態強制轉換,主要應用於多型,父子類的型別轉換,dynamic_cast和static_cast不同的是,它會檢查型別轉換是否正確,不能轉換,則會返回null,所以不算是強制轉換。
const_cast:取出const屬性,比較簡單,可以把const型別轉換為非conse指標型別。
reinterpret_cast:一種非常隨意的二進位制轉換,簡單理解對一個二進位制序列的重新解釋。

http://blog.csdn.net/swartz2015/article/details/69651482
https://www.cnblogs.com/BeyondAnyTime/archive/2012/08/23/2652696.html