C++ 基礎(chǔ)
1. 引用和指針有什么區(qū)別?
一般指的是某塊內(nèi)存的地址狗热,通過這個(gè)地址钞馁,我們可以尋址到這塊內(nèi)存;而引用是一個(gè)變量的別名斗搞。指針可以為空指攒,引用不能為空。
2. #define, extern, static和const有什么區(qū)別僻焚?
define主要是用于定義宏允悦,編譯器編譯時(shí)做相關(guān)的字符替換工作,主要用來增加代碼可讀性虑啤。
const定義的數(shù)據(jù)在程序開始前就在全局變量區(qū)分配了空間隙弛,生命周期內(nèi)其值不可修改。
static修飾局部變量時(shí)狞山,該變量便存放在靜態(tài)數(shù)據(jù)區(qū)全闷,其生命周期一直持續(xù)到整個(gè)程序執(zhí)行結(jié)束,static修飾全局變量萍启,全局變量在本源文件中被訪問到总珠,也可以在同一個(gè)工程的其它源文件中被訪問。
extern用在變量或者函數(shù)的聲明前勘纯,用來說明“此變量/函數(shù)是在別處定義的局服,要在此處引用”。
3. 靜態(tài)鏈接和動(dòng)態(tài)鏈接有什么區(qū)別驳遵?
靜態(tài)鏈接淫奔,無論缺失的地址位于其它目標(biāo)文件還是鏈接庫,鏈接庫都會(huì)逐個(gè)找到各目標(biāo)文件中缺失的地址堤结。采用此鏈接方式生成的可執(zhí)行文件唆迁,可以獨(dú)立載入內(nèi)存運(yùn)行鸭丛;
動(dòng)態(tài)鏈接,鏈接器先從所有目標(biāo)文件中找到部分缺失的地址唐责,然后將所有目標(biāo)文件組織成一個(gè)可執(zhí)行文件鳞溉。如此生成的可執(zhí)行文件,仍缺失部分函數(shù)和變量的地址妒蔚,待文件執(zhí)行時(shí)穿挨,需連同所有的鏈接庫文件一起載入內(nèi)存,再由鏈接器完成剩余的地址修復(fù)工作肴盏,才能正常執(zhí)行科盛。
4. 變量的聲明和定義有什么區(qū)別
變量的定義:用于為變量分配存儲(chǔ)空間,還可以為變量指定初始值菜皂。在一個(gè)程序中贞绵,變量有且僅有一個(gè)定義。
變量的聲明:用于向程序表明變量的類型和名字恍飘。程序中變量可以聲明多次榨崩,但只能定義一次。
5. volatile 和 mutable 有什么作用
在C++中章母,mutable是為了突破const的限制而設(shè)置的母蛛。被mutable修飾的變量,將永遠(yuǎn)處于可變的狀態(tài)乳怎,即使在一個(gè)const函數(shù)中彩郊,甚至結(jié)構(gòu)體變量或者類對象為const,其mutable成員也可以被修改蚪缀。
象const一樣秫逝,volatile是一個(gè)類型修飾符。volatile修飾的數(shù)據(jù),編譯器不可對其進(jìn)行執(zhí)行期寄存于寄存器的優(yōu)化询枚。這種特性,是為了滿足多線程同步违帆、中斷、硬件編程等特殊需要金蜀。遇到這個(gè)關(guān)鍵字聲明的變量刷后,編譯器對訪問該變量的代碼就不再進(jìn)行優(yōu)化,從而可以提供對特殊地址的直接訪問渊抄。
6. 全局變量和局部變量有什么區(qū)別惠险?操作系統(tǒng)和編譯器是怎么知道的?
全局變量是整個(gè)程序都可訪問的變量抒线,生存期從程序開始到程序結(jié)束;局部變量存在于模塊中(比如某個(gè)函數(shù))渣慕,只有在模塊中才可以訪問嘶炭,生存期從模塊開始到模塊結(jié)束抱慌。
全局變量分配在全局?jǐn)?shù)據(jù)段,在程序開始運(yùn)行的時(shí)候被加載眨猎。局部變量則分配在程序的堆棧中抑进。因此,操作系統(tǒng)和編譯器可以通過內(nèi)存分配的位置來知道來區(qū)分全局變量和局部變量睡陪。
7. shared_ptr, weak_ptr, unique_ptr 分別是什么寺渗?
unique_ptr 實(shí)現(xiàn)獨(dú)占式擁有或嚴(yán)格擁有的智能指針,通過禁用拷貝構(gòu)造和賦值的方式保證同一時(shí)間內(nèi)只有一個(gè)智能指針可以指向該對象兰迫;shared_ptr增加了引用計(jì)數(shù)信殊,每次有新的shared_ptr指向同一個(gè)資源時(shí)計(jì)數(shù)會(huì)增加,當(dāng)計(jì)數(shù)為0時(shí)自動(dòng)釋放資源汁果;構(gòu)造新的weak_ptr指針不會(huì)增加shared_ptr的引用計(jì)數(shù)涡拘,是用來解決shared_ptr循環(huán)引用的問題。
8. RAII是什么据德?
RAII技術(shù)的核心是獲取完資源就馬上交給資源管理鳄乏。標(biāo)準(zhǔn)庫中的智能指針和鎖便是比較常用的RAII工具。RAII類需要慎重考慮資源拷貝的合理性棘利。
9. 右值引用有什么作用橱野?
普通引用為左值引用,無法指向右值善玫,但是 const 左值引用可以指向右值水援;右值引用指向的是右值,本質(zhì)上也是把右值提升為一個(gè)左值蝌焚,并定義一個(gè)右值引用通過 std::move 指向該左值裹唆。右值引用和 std::move 被廣泛用于在 STL 和自定義類中實(shí)現(xiàn)移動(dòng)語義,避免拷貝只洒,從而提升程序性能许帐。
10. 函數(shù)重載和函數(shù)重寫
重寫(覆蓋)的規(guī)則:
1、重寫方法的參數(shù)列表必須完全與被重寫的方法的相同,否則不能稱其為重寫而是重載毕谴。
2成畦、重寫方法的訪問修飾符一定要大于被重寫方法的訪問修飾符(public > protected > default > private)。
3涝开、重寫的方法的返回值必須和被重寫的方法的返回一致循帐。
4、重寫的方法所拋出的異常必須和被重寫方法的所拋出的異常一致舀武,或者是其子類拄养。
5、被重寫的方法不能為private银舱,否則在其子類中只是新定義了一個(gè)方法瘪匿,并沒有對其進(jìn)行重寫跛梗。
6、靜態(tài)方法不能被重寫為非靜態(tài)的方法(會(huì)編譯出錯(cuò))棋弥。
重載的規(guī)則:
1核偿、在使用重載時(shí)只能通過相同的方法名、不同的參數(shù)形式實(shí)現(xiàn)顽染。不同的參數(shù)類型可以是不同的參數(shù)類型漾岳,不同的參數(shù)個(gè)數(shù),不同的參數(shù)順序(參數(shù)類型必須不一樣)粉寞。
2尼荆、不能通過訪問權(quán)限、返回類型仁锯、拋出的異常進(jìn)行重載耀找。
3、方法的異常類型和數(shù)目不會(huì)對重載造成影響业崖。
11. C++的頂層const和底層const野芒?
頂層 const 表示指針本身是個(gè)常量;
底層 const 表示指針?biāo)傅膶ο笫且粋€(gè)常量a双炕。
12. 拷貝初始化狞悲、直接初始化、列表初始化?
直接初始化實(shí)際上是要求編譯器使用普通的函數(shù)匹配來選擇與我們提供的參數(shù)最匹配的構(gòu)造函數(shù)妇斤。
拷貝初始化實(shí)際上是要求編譯器將右側(cè)運(yùn)算對象拷貝到正在創(chuàng)建的對象中摇锋,通常用拷貝構(gòu)造函數(shù)來完成。
C++11標(biāo)準(zhǔn)中{}的初始化方式是對聚合類型的初始化站超,是以拷貝的形式來賦值的荸恕。
C++面向?qū)ο?/h2>
1. 純虛函數(shù)和虛函數(shù)表
如果類中存在虛函數(shù),那么該類的大小就會(huì)多4個(gè)字節(jié)死相,然而這4個(gè)字節(jié)就是一個(gè)指針的大小融求,這個(gè)指針指向虛函數(shù)表,這個(gè)指針將被放置與類所有成員之前算撮。對于多重繼承的派生類來說生宛,它含有與父類數(shù)量相對應(yīng)的虛函數(shù)指針。
2. 為什么基類的構(gòu)造函數(shù)不能定義為虛函數(shù)肮柜?
從存儲(chǔ)空間角度陷舅,虛函數(shù)對應(yīng)一個(gè)指向vtable虛函數(shù)表的指針,這大家都知道审洞,可是這個(gè)指向vtable的指針其實(shí)是存儲(chǔ)在對象的內(nèi)存空間的莱睁。問題出來了,如果構(gòu)造函數(shù)是虛的,就需要通過 vtable來調(diào)用仰剿,可是對象還沒有實(shí)例化耙箍,也就是內(nèi)存空間還沒有,怎么找vtable呢酥馍?所以構(gòu)造函數(shù)不能是虛函數(shù)。
從使用角度阅酪,虛函數(shù)主要用于在信息不全的情況下旨袒,能使重載的函數(shù)得到對應(yīng)的調(diào)用。構(gòu)造函數(shù)本身就是要初始化實(shí)例术辐,那使用虛函數(shù)也沒有實(shí)際意義呀砚尽。所以構(gòu)造函數(shù)沒有必要是虛函數(shù)。虛函數(shù)的作用在于通過父類的指針或者引用來調(diào)用它的時(shí)候能夠變成調(diào)用子類的那個(gè)成員函數(shù)辉词。而構(gòu)造函數(shù)是在創(chuàng)建對象時(shí)自動(dòng)調(diào)用的必孤,不可能通過父類的指針或者引用去調(diào)用,因此也就規(guī)定構(gòu)造函數(shù)不能是虛函數(shù)瑞躺。
構(gòu)造函數(shù)不需要是虛函數(shù)敷搪,也不允許是虛函數(shù),因?yàn)閯?chuàng)建一個(gè)對象時(shí)我們總是要明確指定對象的類型幢哨,盡管我們可能通過實(shí)驗(yàn)室的基類的指針或引用去訪問它但析構(gòu)卻不一定赡勘,我們往往通過基類的指針來銷毀對象。這時(shí)候如果析構(gòu)函數(shù)不是虛函數(shù)捞镰,就不能正確識別對象類型從而不能正確調(diào)用析構(gòu)函數(shù)闸与。
從實(shí)現(xiàn)上看,vbtl在構(gòu)造函數(shù)調(diào)用后才建立岸售,因而構(gòu)造函數(shù)不可能成為虛函數(shù)從實(shí)際含義上看践樱,在調(diào)用構(gòu)造函數(shù)時(shí)還不能確定對象的真實(shí)類型(因?yàn)樽宇悤?huì)調(diào)父類的構(gòu)造函數(shù));而且構(gòu)造函數(shù)的作用是提供初始化凸丸,在對象生命期只執(zhí)行一次拷邢,不是對象的動(dòng)態(tài)行為,也沒有必要成為虛函數(shù)甲雅。
當(dāng)一個(gè)構(gòu)造函數(shù)被調(diào)用時(shí)解孙,它做的首要的事情之一是初始化它的VPTR。因此抛人,它只能知道它是“當(dāng)前”類的弛姜,而完全忽視這個(gè)對象后面是否還有繼承者。當(dāng)編譯器為這個(gè)構(gòu)造函數(shù)產(chǎn)生代碼時(shí)妖枚,它是為這個(gè)類的構(gòu)造函數(shù)產(chǎn)生代碼——既不是為基類廷臼,也不是為它的派生類(因?yàn)轭惒恢勒l繼承它)。所以它使用的VPTR必須是對于這個(gè)類的VTABLE。而且荠商,只要它是最后的構(gòu)造函數(shù)調(diào)用寂恬,那么在這個(gè)對象的生命期內(nèi),VPTR將保持被初始化為指向這個(gè)VTABLE, 但如果接著還有一個(gè)更晚派生的構(gòu)造函數(shù)被調(diào)用莱没,這個(gè)構(gòu)造函數(shù)又將設(shè)置VPTR指向它的 VTABLE初肉,等.直到最后的構(gòu)造函數(shù)結(jié)束。VPTR的狀態(tài)是由被最后調(diào)用的構(gòu)造函數(shù)確定的饰躲。這就是為什么構(gòu)造函數(shù)調(diào)用是從基類到更加派生類順序的另一個(gè)理由牙咏。但是,當(dāng)這一系列構(gòu)造函數(shù)調(diào)用正發(fā)生時(shí)嘹裂,每個(gè)構(gòu)造函數(shù)都已經(jīng)設(shè)置VPTR指向它自己的VTABLE妄壶。如果函數(shù)調(diào)用使用虛機(jī)制,它將只產(chǎn)生通過它自己的VTABLE的調(diào)用寄狼,而不是最后的VTABLE(所有構(gòu)造函數(shù)被調(diào)用后才會(huì)有最后的VTABLE)丁寄。
3. 什么時(shí)候需要定義虛析構(gòu)函數(shù)?
一般基類的虛成員函數(shù)泊愧,子類重載的時(shí)候要求是完全一致伊磺,也就是除了函數(shù)體,都要一毛一樣拼卵。而析構(gòu)函數(shù)同樣也是成員函數(shù)奢浑,虛析構(gòu)函數(shù)也會(huì)進(jìn)入虛表,唯一不同的是,函數(shù)名并不要求一致,而且逗载,你如果不寫蓄拣,編譯器也會(huì)幫你生成,而且如果基類有virtual,編譯器也會(huì)默認(rèn)給子類添加。但是不論如何它依舊遵守多態(tài)的規(guī)則,也就是說莺丑,如果你的析構(gòu)函數(shù)是虛函數(shù),調(diào)用虛函數(shù)的規(guī)則也遵守多態(tài)原則墩蔓,也就是會(huì)調(diào)用子類的析構(gòu)函數(shù)梢莽,這和其他虛函數(shù)的機(jī)制完全一致,并沒有什么不同奸披。而子類析構(gòu)函數(shù)具有析構(gòu)掉基類的職責(zé)昏名,所以不會(huì)造成內(nèi)存泄漏。而基類并不知道自己的子類阵面。
4. 構(gòu)造函數(shù)和析構(gòu)函數(shù)能拋出異常嗎轻局?
不能洪鸭。
5. 多繼承存在什么問題?如何消除多繼承中的二義性仑扑?
在繼承時(shí)览爵,基類之間或基類與派生類之間發(fā)生成員同名時(shí),將出現(xiàn)對成員訪問的不確定性镇饮,即同名二義性蜓竹。解決二義性的方案:利用作用域運(yùn)算符::,用于限定派生類使用的是哪個(gè)基類的成員储藐;在派生類中定義同名成員梅肤,覆蓋基類中的相關(guān)成員。
6. 如果類A是一個(gè)空類邑茄,那么sizeof(A)的值為多少?如果不為空大小是多少俊啼?
A為空肺缕,大小是1;不為空授帕,A的大小是所有非靜態(tài)成員大小之和同木。
7. 類型轉(zhuǎn)換分為哪幾種?各自有什么樣的特點(diǎn)跛十?
自動(dòng)類型轉(zhuǎn)換
特點(diǎn): 數(shù)據(jù)范圍從小到大轉(zhuǎn)換彤路,不需要進(jìn)行代碼的特殊處理,編譯器自動(dòng)完成芥映。
強(qiáng)制類型轉(zhuǎn)換
特點(diǎn): 數(shù)據(jù)范圍從大到小轉(zhuǎn)換洲尊,需要進(jìn)行特殊的格式處理,會(huì)損失精度奈偏。
類型轉(zhuǎn)換函數(shù)
- static_cast(靜態(tài)類型轉(zhuǎn)換)
靜態(tài)類型轉(zhuǎn)換坞嘀,編譯的時(shí)c++編譯器會(huì)做類型檢查,基本類型能轉(zhuǎn)換但是不能轉(zhuǎn)換指針類型 - reinterpreter_cast(重新解釋類型轉(zhuǎn)換惊来,interpreter丽涩,v.詮釋,說明)
若不同類型之間裁蚁,進(jìn)行強(qiáng)制類型轉(zhuǎn)換矢渊,用reinterpret_cast進(jìn)行重新解釋 - dynamic_cast(動(dòng)態(tài)類型轉(zhuǎn)換)
C++中重要的,安全的基類和子類之間轉(zhuǎn)換枉证,運(yùn)行時(shí)類型檢查 - const_cast(常量類型準(zhǔn)換)
去除變量的只讀屬性
8. RTTI是什么矮男?其原理是什么?
RTTI是Runtime Type Identification的縮寫刽严,意思是運(yùn)行時(shí)類型識別昂灵。C++引入這個(gè)機(jī)制是為了讓程序在運(yùn)行時(shí)能根據(jù)基類的指針或引用來獲得該指針或引用所指的對象的實(shí)際類型避凝。簡單的講,RTTI是在一個(gè)類的虛函數(shù)表里面添加了一個(gè)新的類型條目眨补。但是現(xiàn)在RTTI的類型識別已經(jīng)不限于此了管削,它還能通過typeid操作符識別出所有的基本類型(int,指針等)的變量對應(yīng)的類型撑螺。
C++通過以下的兩個(gè)操作提供RTTI:
- typeid運(yùn)算符含思,該運(yùn)算符返回其表達(dá)式或類型名的實(shí)際類型。
- type_info類里面的比較運(yùn)算符
- dynamic_cast運(yùn)算符甘晤,該運(yùn)算符將基類的指針或引用安全地轉(zhuǎn)換為派生類類型的指針或引用含潘。
9. C++的空類有哪些成員函數(shù)
默認(rèn)構(gòu)造函數(shù)、 默認(rèn)拷貝構(gòu)造函數(shù)线婚、 默認(rèn)析構(gòu)函數(shù)遏弱、 默認(rèn)賦值運(yùn)算符,以及取址運(yùn)算符和 const 取址運(yùn)算符塞弊。
10. 虛函數(shù)表屬于類還是對象漱逸?虛函數(shù)表什么內(nèi)存空間?
虛函數(shù)表是class specific的游沿,也就是針對一個(gè)類來說的饰抒,這里有點(diǎn)像一個(gè)類里面的staic成員變量,即它是屬于一個(gè)類所有對象的诀黍,不是屬于某一個(gè)對象特有的袋坑,是一個(gè)類所有對象共有的。
gcc編譯器的實(shí)現(xiàn)中虛函數(shù)表vtable存放在可執(zhí)行文件的只讀數(shù)據(jù)段.rodata中眯勾。
C++ STL
1. vector, array, deque 的區(qū)別
vector是動(dòng)態(tài)數(shù)組枣宫,array被封裝成容器的C++數(shù)組,deque是雙向數(shù)組吃环,首尾都支持增刪镶柱。
2. Vector如何釋放空間?
想要徹底釋放內(nèi)存,C11引入了shrink_to_fit();模叙,在執(zhí)行完clear()后執(zhí)行歇拆,可完全釋放內(nèi)存
3. 如何在共享內(nèi)存上使用STL標(biāo)準(zhǔn)庫?
- 想像一下把STL容器范咨,例如map, vector, list等等故觅,放入共享內(nèi)存中,IPC一旦有了這些強(qiáng)大的通用數(shù)據(jù)結(jié)構(gòu)做輔助渠啊,無疑進(jìn)程間通信的能力一下子強(qiáng)大了很多输吏。
我們沒必要再為共享內(nèi)存設(shè)計(jì)其他額外的數(shù)據(jù)結(jié)構(gòu),另外替蛉,STL的高度可擴(kuò)展性將為IPC所驅(qū)使贯溅。STL容器被良好的封裝拄氯,默認(rèn)情況下有它們自己的內(nèi)存管理方案。
當(dāng)一個(gè)元素被插入到一個(gè)STL列表(list)中時(shí)它浅,列表容器自動(dòng)為其分配內(nèi)存译柏,保存數(shù)據(jù)〗慊簦考慮到要將STL容器放到共享內(nèi)存中鄙麦,而容器卻自己在堆上分配內(nèi)存。
一個(gè)最笨拙的辦法是在堆上構(gòu)造STL容器镊折,然后把容器復(fù)制到共享內(nèi)存胯府,并且確保所有容器的內(nèi)部分配的內(nèi)存指向共享內(nèi)存中的相應(yīng)區(qū)域,這基本是個(gè)不可能完成的任務(wù)恨胚。
- 假設(shè)進(jìn)程A在共享內(nèi)存中放入了數(shù)個(gè)容器骂因,進(jìn)程B如何找到這些容器呢?
一個(gè)方法就是進(jìn)程A把容器放在共享內(nèi)存中的確定地址上(fixed offsets)赃泡,則進(jìn)程B可以從該已知地址上獲取容器侣签。另外一個(gè)改進(jìn)點(diǎn)的辦法是,進(jìn)程A先在共享內(nèi)存某塊確定地址上放置一個(gè)map容器急迂,然后進(jìn)程A再創(chuàng)建其他容器,然后給其取個(gè)名字和地址一并保存到這個(gè)map容器里蹦肴。
進(jìn)程B知道如何獲取該保存了地址映射的map容器僚碎,然后同樣再根據(jù)名字取得其他容器的地址。
4. map 阴幌、set勺阐、multiset、multimap 底層原理及其相關(guān)面試題
底層數(shù)據(jù)結(jié)構(gòu)都是紅黑樹矛双。
5. vector迭代器失效的情況
當(dāng)插入一個(gè)元素到vector中渊抽,由于引起了內(nèi)存重新分配,所以指向原內(nèi)存的迭代器全部失效议忽。
當(dāng)刪除容器中一個(gè)元素后,該迭代器所指向的元素已經(jīng)被刪除懒闷,那么也造成迭代器失效。erase方法會(huì)返回下一個(gè)有效的迭代器栈幸,所以當(dāng)我們要?jiǎng)h除某個(gè)元素時(shí)愤估,需要it=vec.erase(it);。
6. vector 的 reserve() 和 resize() 方法之間有什么區(qū)別速址?
reserve是直接擴(kuò)充到已經(jīng)確定的大小玩焰,可以減少多次開辟、釋放空間的問題(優(yōu)化push_back)芍锚,就可以提高效率昔园,其次還可以減少多次要拷貝數(shù)據(jù)的問題蔓榄。reserve只是保證vector中的空間大小(capacity)最少達(dá)到參數(shù)所指定的大小n默刚。reserve()只有一個(gè)參數(shù)甥郑。
resize()可以改變有效空間的大小,也有改變默認(rèn)值的功能羡棵。capacity的大小也會(huì)隨著改變壹若。resize()可以有多個(gè)參數(shù)。
7. unordered_map , unordered_set 底層原理及其相關(guān)面試題
底層數(shù)據(jù)結(jié)構(gòu)都是哈希表皂冰,都是通過開鏈法解決沖突店展。
8. STL內(nèi)存優(yōu)化?
嚴(yán)格遵守"commit or rollback"原則秃流。該原則規(guī)定赂蕴,在批量初始化過程中。要么產(chǎn)生全部的必要元素舶胀。要么不產(chǎn)生一個(gè)元素概说,即要么不做,做了就做好做全嚣伐。
在初始化過程中糖赔,會(huì)先推斷待初始化的元素類型是否為內(nèi)置類型,若為內(nèi)置類型POD(Plain Old Data)轩端,則直接調(diào)用更加底層的函數(shù)放典,上面三個(gè)函數(shù)相應(yīng)的底層函數(shù)分別為:memmove(b1,b,e-b)、fill(b,e,t)和fill(b,n,x)基茵。若數(shù)據(jù)類型為其它類型奋构,則循環(huán)調(diào)用construct(iter,t)函數(shù),這樣做的目的是為了提高效率拱层。
9. emplace和push的區(qū)別弥臼?
push則是先構(gòu)造元素,再將其插入容器根灯;emplace可以直接傳入構(gòu)造對象需要的元素径缅,然后自己調(diào)用其構(gòu)造函數(shù)。
C++內(nèi)存管理
1. 變量的存儲(chǔ)位置烙肺?程序的內(nèi)存分配芥驳?
在C++中,內(nèi)存區(qū)分為5個(gè):堆茬高、棧兆旬、自由存儲(chǔ)區(qū)、全局/靜態(tài)存儲(chǔ)區(qū)怎栽、常量存儲(chǔ)區(qū)丽猬。new是在自由存儲(chǔ)區(qū)開辟內(nèi)存宿饱。
在C中,內(nèi)存區(qū)分為堆脚祟、棧谬以、全局/靜態(tài)存儲(chǔ)區(qū)、常量存儲(chǔ)區(qū)由桌。malloc是在堆上開辟內(nèi)存为黎。
2. 內(nèi)存的分配方式有幾種?
從全局存儲(chǔ)區(qū)域分配:這時(shí)內(nèi)存在程序編譯階段就已經(jīng)分配好行您,該內(nèi)存在程序運(yùn)行的整個(gè)周期都有效铭乾,如:全局變量、static靜態(tài)變量娃循。
從棧區(qū)分配:在執(zhí)行函數(shù)的時(shí)候炕檩,函數(shù)中的局部變量的存儲(chǔ)單元都可以從棧中分配,函數(shù)執(zhí)行結(jié)束后這些存儲(chǔ)單元都會(huì)被自動(dòng)釋放捌斧,實(shí)現(xiàn)從棧中分配存儲(chǔ)單元運(yùn)算操作內(nèi)置于處理器的指令集中笛质,效率很高 但是分配的內(nèi)存容量有限。
從堆中分配:也稱為動(dòng)態(tài)內(nèi)存分配捞蚂,在程序運(yùn)行期間妇押,可以使用malloc和new申請任意數(shù)量的內(nèi)存單元,由程序員決定在什么時(shí)候使用free和delete釋放內(nèi)存姓迅。
4. 堆和棧有什么區(qū)別敲霍?
- 棧由系統(tǒng)自動(dòng)分配/回收,而堆是人為手動(dòng)分配/回收队贱;
- 棧獲得的空間較小,而堆獲得的空間較大潭袱;
- 棧由系統(tǒng)自動(dòng)分配柱嫌,速度較快,而堆一般速度比較慢屯换;
- 棧是連續(xù)的空間编丘,而堆是不連續(xù)的空間。
5. 靜態(tài)內(nèi)存分配和動(dòng)態(tài)內(nèi)存分配有什么區(qū)別彤悔?
- 時(shí)間不同:
靜態(tài)分配發(fā)生在程序的編譯和鏈接的時(shí)候嘉抓。
動(dòng)態(tài)分配發(fā)生在程序調(diào)入和執(zhí)行的時(shí)候。
- 空間不同:
靜態(tài)分配只能是有棧來分配(有編譯器來完成晕窑,比如定義一個(gè)局部變量 int b = 1)
動(dòng)態(tài)分配可以是堆分配(malloc分配抑片,需要手動(dòng)回收內(nèi)存)或者棧分配(編譯器來完成,自動(dòng)回收內(nèi)存)
- 靈活度不同:
靜態(tài)分配需要提前指定空間大小杨赤,不能再動(dòng)態(tài)改變大小敞斋。
動(dòng)態(tài)分配不需要提前分配存儲(chǔ)空間截汪,可以動(dòng)態(tài)的調(diào)整大小。
- 生命周期不同:
靜態(tài)分配的內(nèi)存在程序一開始運(yùn)行就會(huì)分配內(nèi)存植捎,直到程序結(jié)束了衙解,內(nèi)存才會(huì)被釋放。
動(dòng)態(tài)分配的內(nèi)存是在程序調(diào)用函數(shù)時(shí)才被分配焰枢,函數(shù)結(jié)束了蚓峦,動(dòng)態(tài)內(nèi)存就應(yīng)該被釋放掉(別忘了手動(dòng)釋放)。
6. 如何構(gòu)造一個(gè)類济锄,使得只能在堆上或只能在棧上分配內(nèi)存暑椰?
容易想到將構(gòu)造函數(shù)設(shè)為私有。在構(gòu)造函數(shù)私有之后拟淮,無法在類外部調(diào)用構(gòu)造函數(shù)來構(gòu)造類對象干茉,只能使用new運(yùn)算符來建立對象。然而很泊,前面已經(jīng)說過角虫,new 運(yùn)算符的執(zhí)行過程分為兩步,C++提供new運(yùn)算符的重載委造,其實(shí)是只允許重載operator new()函數(shù)戳鹅,而operator new()函數(shù)只用于分配內(nèi)存,無法提供構(gòu)造功能昏兆。因此枫虏,這種方法不可以。
當(dāng)對象建立在棧上面時(shí)爬虱,是由編譯器分配內(nèi)存空間的隶债,調(diào)用構(gòu)造函數(shù)來構(gòu)造棧對象。當(dāng)對象使用完后跑筝,編譯器會(huì)調(diào)用析構(gòu)函數(shù)來釋放棧對象所占的空間死讹。編譯器管理了對象的整個(gè)生命周期。如果編譯器無法調(diào)用類的析構(gòu)函數(shù)曲梗,情況會(huì)是怎樣的呢赞警?比如,類的析構(gòu)函數(shù)是私有的虏两,編譯器無法調(diào)用析構(gòu)函數(shù)來釋放內(nèi)存愧旦。所以,編譯器在為類對象分配椂ò眨空間時(shí)笤虫,會(huì)先檢查類的析構(gòu)函數(shù)的訪問性,其實(shí)不光是析構(gòu)函數(shù),只要是非靜態(tài)的函數(shù)耕皮,編譯器都會(huì)進(jìn)行檢查境蜕。如果類的析構(gòu)函數(shù)是私有的,則編譯器不會(huì)在椓柰#空間上為類對象分配內(nèi)存粱年。因此,將析構(gòu)函數(shù)設(shè)為私有罚拟,類對象就無法建立在棧上了台诗。
7. 淺拷貝和深拷貝有什么區(qū)別?
深拷貝拷貝指針?biāo)傅刂返臄?shù)據(jù)赐俗;淺拷貝拷貝指針本身拉队。
8. 字節(jié)對齊的原則是什么?
字節(jié)對齊與具體編譯器相關(guān)粱快,但一般都遵循以下三條規(guī)則:
- 結(jié)構(gòu)、聯(lián)合或類的數(shù)據(jù)成員叔扼,第一個(gè)相對于首地址放在偏移為0的地方;
- 結(jié)構(gòu)与柑、聯(lián)合或類的各成員相對于首地址的偏移量,都是#pragma pack指定的數(shù)值和該成員大小中較小那個(gè)的整數(shù)倍挠将。如有需要編譯器會(huì)在成員之間加上填充字節(jié);
- 結(jié)構(gòu)汞斧、聯(lián)合或類的總大小為最寬基本類型成員大小與#pragma pack指定的數(shù)值中較小那個(gè)的整數(shù)倍统台,如有需要編譯器會(huì)在最末一個(gè)成員之后加上填充字節(jié)拔鹰。
更多技術(shù)分享瀏覽我的博客:
本文由mdnice多平臺(tái)發(fā)布