C++面試題

C++ 基礎(chǔ)

1. 引用和指針有什么區(qū)別?

一般指的是某塊內(nèi)存的地址,通過(guò)這個(gè)地址,我們可以尋址到這塊內(nèi)存;而引用是一個(gè)變量的別名。指針可以為空,引用不能為空。

2. #define, extern, static和const有什么區(qū)別?

define主要是用于定義宏,編譯器編譯時(shí)做相關(guān)的字符替換工作,主要用來(lái)增加代碼可讀性。

const定義的數(shù)據(jù)在程序開始前就在全局變量區(qū)分配了空間,生命周期內(nèi)其值不可修改。

static修飾局部變量時(shí),該變量便存放在靜態(tài)數(shù)據(jù)區(qū),其生命周期一直持續(xù)到整個(gè)程序執(zhí)行結(jié)束,static修飾全局變量,全局變量在本源文件中被訪問(wèn)到,也可以在同一個(gè)工程的其它源文件中被訪問(wèn)。

extern用在變量或者函數(shù)的聲明前,用來(lái)說(shuō)明“此變量/函數(shù)是在別處定義的,要在此處引用”。

3. 靜態(tài)鏈接和動(dòng)態(tài)鏈接有什么區(qū)別?

靜態(tài)鏈接,無(wú)論缺失的地址位于其它目標(biāo)文件還是鏈接庫(kù),鏈接庫(kù)都會(huì)逐個(gè)找到各目標(biāo)文件中缺失的地址。采用此鏈接方式生成的可執(zhí)行文件,可以獨(dú)立載入內(nèi)存運(yùn)行;
動(dòng)態(tài)鏈接,鏈接器先從所有目標(biāo)文件中找到部分缺失的地址,然后將所有目標(biāo)文件組織成一個(gè)可執(zhí)行文件。如此生成的可執(zhí)行文件,仍缺失部分函數(shù)和變量的地址,待文件執(zhí)行時(shí),需連同所有的鏈接庫(kù)文件一起載入內(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)體變量或者類對(duì)象為const,其mutable成員也可以被修改。
象const一樣,volatile是一個(gè)類型修飾符。volatile修飾的數(shù)據(jù),編譯器不可對(duì)其進(jìn)行執(zhí)行期寄存于寄存器的優(yōu)化。這種特性,是為了滿足多線程同步、中斷、硬件編程等特殊需要。遇到這個(gè)關(guān)鍵字聲明的變量,編譯器對(duì)訪問(wèn)該變量的代碼就不再進(jìn)行優(yōu)化,從而可以提供對(duì)特殊地址的直接訪問(wèn)。

6. 全局變量和局部變量有什么區(qū)別?操作系統(tǒng)和編譯器是怎么知道的?

全局變量是整個(gè)程序都可訪問(wèn)的變量,生存期從程序開始到程序結(jié)束;局部變量存在于模塊中(比如某個(gè)函數(shù)),只有在模塊中才可以訪問(wèn),生存期從模塊開始到模塊結(jié)束。
全局變量分配在全局?jǐn)?shù)據(jù)段,在程序開始運(yùn)行的時(shí)候被加載。局部變量則分配在程序的堆棧中。因此,操作系統(tǒng)和編譯器可以通過(guò)內(nèi)存分配的位置來(lái)知道來(lái)區(qū)分全局變量和局部變量。

7. shared_ptr, weak_ptr, unique_ptr 分別是什么?

unique_ptr 實(shí)現(xiàn)獨(dú)占式擁有或嚴(yán)格擁有的智能指針,通過(guò)禁用拷貝構(gòu)造和賦值的方式保證同一時(shí)間內(nèi)只有一個(gè)智能指針可以指向該對(duì)象;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ù),是用來(lái)解決shared_ptr循環(huán)引用的問(wèn)題。

8. RAII是什么?

RAII技術(shù)的核心是獲取完資源就馬上交給資源管理。標(biāo)準(zhǔn)庫(kù)中的智能指針和鎖便是比較常用的RAII工具。RAII類需要慎重考慮資源拷貝的合理性。

9. 右值引用有什么作用?

普通引用為左值引用,無(wú)法指向右值,但是 const 左值引用可以指向右值;右值引用指向的是右值,本質(zhì)上也是把右值提升為一個(gè)左值,并定義一個(gè)右值引用通過(guò) std::move 指向該左值。右值引用和 std::move 被廣泛用于在 STL 和自定義類中實(shí)現(xiàn)移動(dòng)語(yǔ)義,避免拷貝,從而提升程序性能。

10. 函數(shù)重載和函數(shù)重寫

重寫(覆蓋)的規(guī)則:
1、重寫方法的參數(shù)列表必須完全與被重寫的方法的相同,否則不能稱其為重寫而是重載。
2、重寫方法的訪問(wèn)修飾符一定要大于被重寫方法的訪問(wèn)修飾符(public > protected > default > private)。
3、重寫的方法的返回值必須和被重寫的方法的返回一致。
4、重寫的方法所拋出的異常必須和被重寫方法的所拋出的異常一致,或者是其子類。
5、被重寫的方法不能為private,否則在其子類中只是新定義了一個(gè)方法,并沒有對(duì)其進(jìn)行重寫。
6、靜態(tài)方法不能被重寫為非靜態(tài)的方法(會(huì)編譯出錯(cuò))。
重載的規(guī)則:
1、在使用重載時(shí)只能通過(guò)相同的方法名、不同的參數(shù)形式實(shí)現(xiàn)。不同的參數(shù)類型可以是不同的參數(shù)類型,不同的參數(shù)個(gè)數(shù),不同的參數(shù)順序(參數(shù)類型必須不一樣)。
2、不能通過(guò)訪問(wèn)權(quán)限、返回類型、拋出的異常進(jìn)行重載。
3、方法的異常類型和數(shù)目不會(huì)對(duì)重載造成影響。

11. C++的頂層const和底層const?

頂層 const 表示指針本身是個(gè)常量;
底層 const 表示指針?biāo)傅膶?duì)象是一個(gè)常量a。

12. 拷貝初始化、直接初始化、列表初始化?

直接初始化實(shí)際上是要求編譯器使用普通的函數(shù)匹配來(lái)選擇與我們提供的參數(shù)最匹配的構(gòu)造函數(shù)。
拷貝初始化實(shí)際上是要求編譯器將右側(cè)運(yùn)算對(duì)象拷貝到正在創(chuàng)建的對(duì)象中,通常用拷貝構(gòu)造函數(shù)來(lái)完成。
C++11標(biāo)準(zhǔn)中{}的初始化方式是對(duì)聚合類型的初始化,是以拷貝的形式來(lái)賦值的。

C++面向?qū)ο?/h2>

1. 純虛函數(shù)和虛函數(shù)表

如果類中存在虛函數(shù),那么該類的大小就會(huì)多4個(gè)字節(jié),然而這4個(gè)字節(jié)就是一個(gè)指針的大小,這個(gè)指針指向虛函數(shù)表,這個(gè)指針將被放置與類所有成員之前。對(duì)于多重繼承的派生類來(lái)說(shuō),它含有與父類數(shù)量相對(duì)應(yīng)的虛函數(shù)指針。

2. 為什么基類的構(gòu)造函數(shù)不能定義為虛函數(shù)?

從存儲(chǔ)空間角度,虛函數(shù)對(duì)應(yīng)一個(gè)指向vtable虛函數(shù)表的指針,這大家都知道,可是這個(gè)指向vtable的指針其實(shí)是存儲(chǔ)在對(duì)象的內(nèi)存空間的。問(wèn)題出來(lái)了,如果構(gòu)造函數(shù)是虛的,就需要通過(guò) vtable來(lái)調(diào)用,可是對(duì)象還沒有實(shí)例化,也就是內(nèi)存空間還沒有,怎么找vtable呢?所以構(gòu)造函數(shù)不能是虛函數(shù)。

從使用角度,虛函數(shù)主要用于在信息不全的情況下,能使重載的函數(shù)得到對(duì)應(yīng)的調(diào)用。構(gòu)造函數(shù)本身就是要初始化實(shí)例,那使用虛函數(shù)也沒有實(shí)際意義呀。所以構(gòu)造函數(shù)沒有必要是虛函數(shù)。虛函數(shù)的作用在于通過(guò)父類的指針或者引用來(lái)調(diào)用它的時(shí)候能夠變成調(diào)用子類的那個(gè)成員函數(shù)。而構(gòu)造函數(shù)是在創(chuàng)建對(duì)象時(shí)自動(dòng)調(diào)用的,不可能通過(guò)父類的指針或者引用去調(diào)用,因此也就規(guī)定構(gòu)造函數(shù)不能是虛函數(shù)。

構(gòu)造函數(shù)不需要是虛函數(shù),也不允許是虛函數(shù),因?yàn)閯?chuàng)建一個(gè)對(duì)象時(shí)我們總是要明確指定對(duì)象的類型,盡管我們可能通過(guò)實(shí)驗(yàn)室的基類的指針或引用去訪問(wèn)它但析構(gòu)卻不一定,我們往往通過(guò)基類的指針來(lái)銷毀對(duì)象。這時(shí)候如果析構(gòu)函數(shù)不是虛函數(shù),就不能正確識(shí)別對(duì)象類型從而不能正確調(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í)還不能確定對(duì)象的真實(shí)類型(因?yàn)樽宇悤?huì)調(diào)父類的構(gòu)造函數(shù));而且構(gòu)造函數(shù)的作用是提供初始化,在對(duì)象生命期只執(zhí)行一次,不是對(duì)象的動(dòng)態(tài)行為,也沒有必要成為虛函數(shù)。

當(dāng)一個(gè)構(gòu)造函數(shù)被調(diào)用時(shí),它做的首要的事情之一是初始化它的VPTR。因此,它只能知道它是“當(dāng)前”類的,而完全忽視這個(gè)對(duì)象后面是否還有繼承者。當(dāng)編譯器為這個(gè)構(gòu)造函數(shù)產(chǎn)生代碼時(shí),它是為這個(gè)類的構(gòu)造函數(shù)產(chǎn)生代碼——既不是為基類,也不是為它的派生類(因?yàn)轭惒恢勒l(shuí)繼承它)。所以它使用的VPTR必須是對(duì)于這個(gè)類的VTABLE。而且,只要它是最后的構(gòu)造函數(shù)調(diào)用,那么在這個(gè)對(duì)象的生命期內(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)生通過(guò)它自己的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ī)則,也就是說(shuō),如果你的析構(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. 多繼承存在什么問(wèn)題?如何消除多繼承中的二義性?

在繼承時(shí),基類之間或基類與派生類之間發(fā)生成員同名時(shí),將出現(xiàn)對(duì)成員訪問(wè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ù)

  1. static_cast(靜態(tài)類型轉(zhuǎn)換)
    靜態(tài)類型轉(zhuǎn)換,編譯的時(shí)c++編譯器會(huì)做類型檢查,基本類型能轉(zhuǎn)換但是不能轉(zhuǎn)換指針類型
  2. reinterpreter_cast(重新解釋類型轉(zhuǎn)換,interpreter,v.詮釋,說(shuō)明)
    若不同類型之間,進(jìn)行強(qiáng)制類型轉(zhuǎn)換,用reinterpret_cast進(jìn)行重新解釋
  3. dynamic_cast(動(dòng)態(tài)類型轉(zhuǎn)換)
    C++中重要的,安全的基類和子類之間轉(zhuǎn)換,運(yùn)行時(shí)類型檢查
  4. const_cast(常量類型準(zhǔn)換)
    去除變量的只讀屬性
8. RTTI是什么?其原理是什么?

RTTI是Runtime Type Identification的縮寫,意思是運(yùn)行時(shí)類型識(shí)別。C++引入這個(gè)機(jī)制是為了讓程序在運(yùn)行時(shí)能根據(jù)基類的指針或引用來(lái)獲得該指針或引用所指的對(duì)象的實(shí)際類型。簡(jiǎn)單的講,RTTI是在一個(gè)類的虛函數(shù)表里面添加了一個(gè)新的類型條目。但是現(xiàn)在RTTI的類型識(shí)別已經(jīng)不限于此了,它還能通過(guò)typeid操作符識(shí)別出所有的基本類型(int,指針等)的變量對(duì)應(yīng)的類型。

C++通過(guò)以下的兩個(gè)操作提供RTTI:

  1. typeid運(yùn)算符,該運(yùn)算符返回其表達(dá)式或類型名的實(shí)際類型。
  2. type_info類里面的比較運(yùn)算符
  3. 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ù)表屬于類還是對(duì)象?虛函數(shù)表什么內(nèi)存空間?

虛函數(shù)表是class specific的,也就是針對(duì)一個(gè)類來(lái)說(shuō)的,這里有點(diǎn)像一個(gè)類里面的staic成員變量,即它是屬于一個(gè)類所有對(duì)象的,不是屬于某一個(gè)對(duì)象特有的,是一個(gè)類所有對(duì)象共有的。

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)庫(kù)?
  1. 想像一下把STL容器,例如map, vector, list等等,放入共享內(nèi)存中,IPC一旦有了這些強(qiáng)大的通用數(shù)據(jù)結(jié)構(gòu)做輔助,無(wú)疑進(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ù)。

  1. 假設(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)確定的大小,可以減少多次開辟、釋放空間的問(wèn)題(優(yōu)化push_back),就可以提高效率,其次還可以減少多次要拷貝數(shù)據(jù)的問(wèn)題。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)都是哈希表,都是通過(guò)開鏈法解決沖突。

8. STL內(nèi)存優(yōu)化?
  1. 嚴(yán)格遵守"commit or rollback"原則。該原則規(guī)定,在批量初始化過(guò)程中。要么產(chǎn)生全部的必要元素。要么不產(chǎn)生一個(gè)元素,即要么不做,做了就做好做全。

  2. 在初始化過(guò)程中,會(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)造對(duì)象需要的元素,然后自己調(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)存的分配方式有幾種?
  1. 從全局存儲(chǔ)區(qū)域分配:這時(shí)內(nèi)存在程序編譯階段就已經(jīng)分配好,該內(nèi)存在程序運(yùn)行的整個(gè)周期都有效,如:全局變量、static靜態(tài)變量。

  2. 從棧區(qū)分配:在執(zhí)行函數(shù)的時(shí)候,函數(shù)中的局部變量的存儲(chǔ)單元都可以從棧中分配,函數(shù)執(zhí)行結(jié)束后這些存儲(chǔ)單元都會(huì)被自動(dòng)釋放,實(shí)現(xiàn)從棧中分配存儲(chǔ)單元運(yùn)算操作內(nèi)置于處理器的指令集中,效率很高 但是分配的內(nèi)存容量有限。

  3. 從堆中分配:也稱為動(dòng)態(tài)內(nèi)存分配,在程序運(yùn)行期間,可以使用malloc和new申請(qǐng)任意數(shù)量的內(nèi)存單元,由程序員決定在什么時(shí)候使用free和delete釋放內(nèi)存。

4. 堆和棧有什么區(qū)別?
  1. 棧由系統(tǒng)自動(dòng)分配/回收,而堆是人為手動(dòng)分配/回收;
  2. 棧獲得的空間較小,而堆獲得的空間較大;
  3. 棧由系統(tǒng)自動(dòng)分配,速度較快,而堆一般速度比較慢;
  4. 棧是連續(xù)的空間,而堆是不連續(xù)的空間。
5. 靜態(tài)內(nèi)存分配和動(dòng)態(tài)內(nèi)存分配有什么區(qū)別?
  1. 時(shí)間不同:

靜態(tài)分配發(fā)生在程序的編譯和鏈接的時(shí)候。

動(dòng)態(tài)分配發(fā)生在程序調(diào)入和執(zhí)行的時(shí)候。

  1. 空間不同:

靜態(tài)分配只能是有棧來(lái)分配(有編譯器來(lái)完成,比如定義一個(gè)局部變量 int b = 1)

動(dòng)態(tài)分配可以是堆分配(malloc分配,需要手動(dòng)回收內(nèi)存)或者棧分配(編譯器來(lái)完成,自動(dòng)回收內(nèi)存)

  1. 靈活度不同:

靜態(tài)分配需要提前指定空間大小,不能再動(dòng)態(tài)改變大小。

動(dòng)態(tài)分配不需要提前分配存儲(chǔ)空間,可以動(dòng)態(tài)的調(diào)整大小。

  1. 生命周期不同:

靜態(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ù)私有之后,無(wú)法在類外部調(diào)用構(gòu)造函數(shù)來(lái)構(gòu)造類對(duì)象,只能使用new運(yùn)算符來(lái)建立對(duì)象。然而,前面已經(jīng)說(shuō)過(guò),new 運(yùn)算符的執(zhí)行過(guò)程分為兩步,C++提供new運(yùn)算符的重載,其實(shí)是只允許重載operator new()函數(shù),而operator new()函數(shù)只用于分配內(nèi)存,無(wú)法提供構(gòu)造功能。因此,這種方法不可以。

當(dāng)對(duì)象建立在棧上面時(shí),是由編譯器分配內(nèi)存空間的,調(diào)用構(gòu)造函數(shù)來(lái)構(gòu)造棧對(duì)象。當(dāng)對(duì)象使用完后,編譯器會(huì)調(diào)用析構(gòu)函數(shù)來(lái)釋放棧對(duì)象所占的空間。編譯器管理了對(duì)象的整個(gè)生命周期。如果編譯器無(wú)法調(diào)用類的析構(gòu)函數(shù),情況會(huì)是怎樣的呢?比如,類的析構(gòu)函數(shù)是私有的,編譯器無(wú)法調(diào)用析構(gòu)函數(shù)來(lái)釋放內(nèi)存。所以,編譯器在為類對(duì)象分配棧空間時(shí),會(huì)先檢查類的析構(gòu)函數(shù)的訪問(wèn)性,其實(shí)不光是析構(gòu)函數(shù),只要是非靜態(tài)的函數(shù),編譯器都會(huì)進(jìn)行檢查。如果類的析構(gòu)函數(shù)是私有的,則編譯器不會(huì)在??臻g上為類對(duì)象分配內(nèi)存。因此,將析構(gòu)函數(shù)設(shè)為私有,類對(duì)象就無(wú)法建立在棧上了。

7. 淺拷貝和深拷貝有什么區(qū)別?

深拷貝拷貝指針?biāo)傅刂返臄?shù)據(jù);淺拷貝拷貝指針本身。

8. 字節(jié)對(duì)齊的原則是什么?

字節(jié)對(duì)齊與具體編譯器相關(guān),但一般都遵循以下三條規(guī)則:

  1. 結(jié)構(gòu)、聯(lián)合或類的數(shù)據(jù)成員,第一個(gè)相對(duì)于首地址放在偏移為0的地方;
  2. 結(jié)構(gòu)、聯(lián)合或類的各成員相對(duì)于首地址的偏移量,都是#pragma pack指定的數(shù)值和該成員大小中較小那個(gè)的整數(shù)倍。如有需要編譯器會(huì)在成員之間加上填充字節(jié);
  3. 結(jié)構(gòu)、聯(lián)合或類的總大小為最寬基本類型成員大小與#pragma pack指定的數(shù)值中較小那個(gè)的整數(shù)倍,如有需要編譯器會(huì)在最末一個(gè)成員之后加上填充字節(jié)。

更多技術(shù)分享瀏覽我的博客:

https://thierryzhou.github.io

本文由mdnice多平臺(tái)發(fā)布

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.new、delete、malloc、free關(guān)系 delete會(huì)調(diào)用對(duì)象的析構(gòu)函數(shù),和new對(duì)應(yīng)free只會(huì)釋...
    雪上霜閱讀 312評(píng)論 0 0
  • 1. 結(jié)構(gòu)體和共同體的區(qū)別。 定義: 結(jié)構(gòu)體struct:把不同類型的數(shù)據(jù)組合成一個(gè)整體,自定義類型。共同體uni...
    breakfy閱讀 2,273評(píng)論 0 22
  • 1、以下代碼會(huì)有什么問(wèn)題? 答:操作數(shù)的類型不兼容, int 和 const char* 2、以下代碼輸出什么? ...
    雲(yún)凌禹閱讀 538評(píng)論 0 8
  • 1.數(shù)組名和指針的區(qū)別? 明確一點(diǎn)數(shù)組名不是指針,可以通過(guò)sizeof來(lái)驗(yàn)證。其本質(zhì)是指代數(shù)組這樣一個(gè)數(shù)據(jù)結(jié)構(gòu)。數(shù)...
    CodeO閱讀 2,232評(píng)論 0 1
  • C++??碱}目參考本 1、指針和引用的區(qū)別 實(shí)體與別名 內(nèi)存分配,指針需要引用不需要 sizeof() 對(duì)象的大小...
    公子本姓嚴(yán)閱讀 805評(píng)論 0 0

友情鏈接更多精彩內(nèi)容