delete 和 delete []的真正區別
- delete 對應 new delete[]對應new[]
- 對于簡單類型包括簡單類型數組,delete 與delete[]沒有區別。對于自定義類型數組,delete 只會刪除一個元素,delete 則會刪除所有元素。
指針和數組的區別
?
野指針是什么
野指針就是指向一個已刪除的對象或者未申請訪問受限內存區域的指針
請你回答一下為什么析構函數必須是虛函數?為什么C++默認的析構函數不是虛函數?
將可能會被繼承的父類的析構函數設置為虛函數,可以保證當我們new一個子類,然后使用基類指針指向該子類對象,釋放基類指針時可以釋放掉子類的空間,防止內存泄漏。如果不設置基類指針只能訪問非繼承成員。
C++默認的析構函數不是虛函數是因為虛函數需要額外的虛函數表和虛表指針,占用額外的內存。而對于不會被繼承的類來說,其析構函數如果是虛函數,就會浪費內存。因此C++默認的析構函數不是虛函數,而是只有當需要當作父類時,設置為虛函數。
請你來說一下C++中析構函數的作用
析構函數與構造函數對應,當對象結束其生命周期,如對象所在的函數已調用完畢時,系統會自動執行析構函數。
析構函數名也應與類名相同,只是在函數名前面加一個位取反符~,例如~stud( ),以區別于構造函數。它不能帶任何參數,也沒有返回值(包括void類型)。只能有一個析構函數,不能重載。
如果用戶沒有編寫析構函數,編譯系統會自動生成一個缺省的析構函數(即使自定義了析構函數,編譯器也總是會為我們合成一個析構函數,并且如果自定義了析構函數,編譯器在執行時會先調用自定義的析構函數再調用合成的析構函數),它也不進行任何操作。所以許多簡單的類中沒有用顯式的析構函數。
如果一個類中有指針,且在使用的過程中動態的申請了內存,那么最好顯示構造析構函數在銷毀類之前,釋放掉申請的內存空間,避免內存泄漏。
類析構順序:1)派生類本身的析構函數;2)對象成員析構函數;3)基類析構函數。
請你來說一下靜態函數和虛函數的區別
靜態函數在編譯的時候就已經確定運行時機,虛函數在運行的時候動態綁定。虛函數因為用了虛函數表機制,調用的時候會增加一次內存開銷
以下四行代碼的區別是什么? const char * arr = "123"; char * brr = "123"; const char crr[] = "123"; char drr[] = "123";
const char * arr = "123";
//字符串123保存在常量區,const本來是修飾arr指向的值不能通過arr去修改,但是字符串“123”在常量區,本來就不能改變,所以加不加const效果都一樣
char * brr = "123";
//字符串123保存在常量區,這個arr指針指向的是同一個位置,同樣不能通過brr去修改"123"的值
const char crr[] = "123";
//這里123本來是在棧上的,但是編譯器可能會做某些優化,將其放到常量區
char drr[] = "123";
//字符串123保存在棧區,可以通過drr去修改
請你回答一下new/delete與malloc/free的區別是什么
首先,new/delete是C++的關鍵字,而malloc/free是C語言的庫函數,后者使用必須指明申請內存空間的大小,對于類類型的對象,后者不會調用構造函數和析構函數。
請你說說C++如何處理返回值?
生成一個臨時變量,把它的引用作為函數參數傳入函數內。
請你說一說select fork,wait,exec函數
父進程產生子進程使用fork拷貝出來一個父進程的副本,此時只拷貝了父進程的頁表,兩個進程都讀同一塊內存,當有進程寫的時候使用寫實拷貝機制分配內存,exec函數可以加載一個elf文件去替換父進程,從此父進程和子進程就可以運行不同的程序了。fork從父進程返回子進程的pid,從子進程返回0.調用了wait的父進程將會發生阻塞,直到有子進程狀態改變,執行成功返回0,錯誤返回-1。exec執行成功則子進程從新的程序開始運行,無返回值,執行失敗返回-1
請你來說一下map和set有什么區別,分別又是怎么實現的?
map和set都是C++的關聯容器,其底層實現都是紅黑樹(RB-Tree)。由于 map 和set所開放的各種操作接口,RB-tree 也都提供了,所以幾乎所有的 map 和set的操作行為,都只是轉調 RB-tree 的操作行為。
map和set區別在于:
(1)map中的元素是key-value(關鍵字—值)對:關鍵字起到索引的作用,值則表示與索引相關聯的數據;Set與之相對就是關鍵字的簡單集合,set中每個元素只包含一個關鍵字。
(2)set的迭代器是const的,不允許修改元素的值;map允許修改value,但不允許修改key。其原因是因為map和set是根據關鍵字排序來保證其有序性的,如果允許修改key的話,那么首先需要刪除該鍵,然后調節平衡,再插入修改后的鍵值,調節平衡,如此一來,嚴重破壞了map和set的結構,導致iterator失效,不知道應該指向改變前的位置,還是指向改變后的位置。所以STL中將set的迭代器設置成const,不允許修改迭代器的值;而map的迭代器則不允許修改key值,允許修改value值。
(3)map支持下標操作,set不支持下標操作。map可以用key做下標,map的下標運算符[ ]將關鍵碼作為下標去執行查找,如果關鍵碼不存在,則插入一個具有該關鍵碼和mapped_type類型默認值的元素至map中,因此下標運算符[ ]在map應用中需要慎用,const_map不能用,只希望確定某一個關鍵值是否存在而不希望插入元素時也不應該使用,mapped_type類型沒有默認值也不應該使用。如果find能解決需要,盡可能用find。
unordered map底層結構是哈希表
?請你說一說vector和list的區別,應用,越詳細越好
1、概念:
1)Vector
連續存儲的容器,動態數組,在堆上分配空間
底層實現:數組
兩倍容量增長:
vector 增加(插入)新元素時,如果未超過當時的容量,則還有剩余空間,那么直接添加到最后(插入指定位置),然后調整迭代器。
如果沒有剩余空間了,則會重新配置原有元素個數的兩倍空間,然后將原空間元素通過復制的方式初始化新空間,再向新空間增加元素,最后析構并釋放原空間,之前的迭代器會失效。
性能:
訪問:O(1)
插入:在最后插入(空間夠):很快
在最后插入(空間不夠):需要內存申請和釋放,以及對之前數據進行拷貝。
在中間插入(空間夠):內存拷貝
在中間插入(空間不夠):需要內存申請和釋放,以及對之前數據進行拷貝。
刪除:在最后刪除:很快
在中間刪除:內存拷貝
適用場景:經常隨機訪問,且不經常對非尾節點進行插入刪除。
2、List
動態鏈表,在堆上分配空間,每插入一個元數都會分配空間,每刪除一個元素都會釋放空間。
底層:雙向鏈表
性能:
訪問:隨機訪問性能很差,只能快速訪問頭尾節點。
插入:很快,一般是常數開銷
刪除:很快,一般是常數開銷
適用場景:經常插入刪除大量數據
2、區別:
1)vector底層實現是數組;list是雙向 鏈表。
2)vector支持隨機訪問,list不支持。
3)vector是順序內存,list不是。
4)vector在中間節點進行插入刪除會導致內存拷貝,list不會。
5)vector一次性分配好內存,不夠時才進行2倍擴容;list每次插入新節點都會進行內存申請。
6)vector隨機訪問性能好,插入刪除性能差;list隨機訪問性能差,插入刪除性能好。
3、應用
vector擁有一段連續的內存空間,因此支持隨機訪問,如果需要高效的隨即訪問,而不在乎插入和刪除的效率,使用vector。
list擁有一段不連續的內存空間,如果需要高效的插入和刪除,而不關心隨機訪問,則應使用list。
請你來說一下STL中迭代器的作用,有指針為何還要迭代器
1、迭代器
Iterator(迭代器)模式又稱Cursor(游標)模式,用于提供一種方法順序訪問一個聚合對象中各個元素, 而又不需暴露該對象的內部表示。或者這樣說可能更容易理解:Iterator模式是運用于聚合對象的一種模式,通過運用該模式,使得我們可以在不知道對象內部表示的情況下,按照一定順序(由iterator提供的方法)訪問聚合對象中的各個元素。
由于Iterator模式的以上特性:與聚合對象耦合,在一定程度上限制了它的廣泛運用,一般僅用于底層聚合支持類,如STL的list、vector、stack等容器類及ostream_iterator等擴展iterator。
2、迭代器和指針的區別
迭代器不是指針,是類模板,表現的像指針。他只是模擬了指針的一些功能,通過重載了指針的一些操作符,->、*、++、--等。迭代器封裝了指針,是一個“可遍歷STL( Standard Template Library)容器內全部或部分元素”的對象,?本質是封裝了原生指針,是指針概念的一種提升(lift),提供了比指針更高級的行為,相當于一種智能指針,他可以根據不同類型的數據結構來實現不同的++,--等操作。
迭代器返回的是對象引用而不是對象的值,所以cout只能輸出迭代器使用*取值后的值而不能直接輸出其自身。
3、迭代器產生原因
Iterator類的訪問方式就是把不同集合類的訪問邏輯抽象出來,使得不用暴露集合內部的結構而達到循環遍歷集合的效果。
請你回答一下STL里resize和reserve的區別
resize():改變當前容器內含有元素的數量(size()),eg: vector<int>v; v.resize(len);v的size變為len,如果原來v的size小于len,那么容器新增(len-size)個元素,元素的值為默認為0.當v.push_back(3);之后,則是3是放在了v的末尾,即下標為len,此時容器是size為len+1;
reserve():改變當前容器的最大容量(capacity),它不會生成元素,只是確定這個容器允許放入多少對象,如果reserve(len)的值大于當前的capacity(),那么會重新分配一塊能存len個對象的空間,然后把之前v.size()個對象通過copy construtor復制過來,銷毀之前的內存;
左右值
在C++11中可以取地址的、有名字的就是左值,反之,不能取地址的、沒有名字的就是右值(將亡值或純右值)。
const左值引用會延長右值的生命周期。
?