某日二師兄參加X(jué)XX科技公司的C++工程師開(kāi)發(fā)崗位6面:
面試官: 如何在堆上申請(qǐng)一塊內(nèi)存?
二師兄:常用的方法有malloc,new等。
面試官:兩者有什么區(qū)別?
二師兄:malloc是向操作系統(tǒng)申請(qǐng)一塊內(nèi)存,這塊內(nèi)存沒(méi)有經(jīng)過(guò)初始化,通常需要使用memset手動(dòng)初始化。而new一般伴隨三個(gè)動(dòng)作,向操作系統(tǒng)申請(qǐng)一塊內(nèi)存,并執(zhí)行類型的默認(rèn)構(gòu)造函數(shù),然后返回類的指針。
面試官:嗯,那你知道calloc和realloc嗎?
二師兄:calloc比malloc多做了一步,就是把申請(qǐng)的內(nèi)存初始化成0。而realloc則可以改變當(dāng)前指針?biāo)赶虻膬?nèi)存塊的大小。
面試官:好的。那么你知道這些api/操作符失敗會(huì)發(fā)生什么嗎?
二師兄:malloc/calloc/realloc失敗會(huì)返回NULL,而new失敗則會(huì)拋出異常。
面試官:有沒(méi)有讓new失敗不拋出異常的方法?
二師兄:好像有,但是我不記得了。。。
面試官:沒(méi)關(guān)系。。。我們都知道new和delete成對(duì)出現(xiàn),new[]和delete[]也是成對(duì)出現(xiàn),那么我想問(wèn),如果使用new[]創(chuàng)建的對(duì)象用delete釋放了會(huì)發(fā)生什么?為什么?
二師兄:額。。。內(nèi)存泄漏?對(duì),會(huì)發(fā)生內(nèi)存泄漏。因?yàn)閮?nèi)存沒(méi)有被釋放。
面試官:好的。我們都知道C++中的內(nèi)存管理是一個(gè)比較麻煩的事情,現(xiàn)在有個(gè)需求,需要在程序中記錄主動(dòng)申請(qǐng)的內(nèi)存和主動(dòng)釋放的內(nèi)存,以確保沒(méi)有發(fā)生內(nèi)存泄漏。有什么好的方法嗎?
二師兄:可以重載new和delete運(yùn)算符。
面試官:如何重載new和delete運(yùn)算符?
二師兄:我得查一下資料,這個(gè)重載用的很少。。。
面試官:(笑)好吧,最后一個(gè)問(wèn)題,咱們上面一直在討論堆中的內(nèi)存的分配和釋放,請(qǐng)問(wèn)一下,如果在棧上分配一塊固定的內(nèi)存?棧中的內(nèi)存如何釋放?
二師兄:額。。。(思考)使用 char[size] ? 應(yīng)該不需要手動(dòng)釋放。
面試官:好的,回去等通知吧。
對(duì)于二師兄的表現(xiàn),小伙伴們能給打幾分呢?我們先看看二師兄在面試中表現(xiàn)不太好的地方:
面試官:有沒(méi)有讓new失敗不拋出異常的方法?
在C++中我們可以使用以下方法使得new運(yùn)算符不拋出異常,
int* p = new (std::nothrow) int(42);
if(p == nullptr)
{
//分配失敗
}
這個(gè)特性需要C++11支持。
再看下一個(gè)問(wèn)題:
如果使用new[]創(chuàng)建的對(duì)象用delete釋放了會(huì)發(fā)生什么?
一定會(huì)發(fā)生內(nèi)存泄漏嗎?答案是,不一定。這取決于類型T。我們先看第一種情況:
class Foo
{
public:
Foo():num_(42){}
private:
int num_;
};
Foo* pf = new Foo[1024];
delete pf;
當(dāng)類型T沒(méi)有管理資源時(shí),delete pf會(huì)把整個(gè)申請(qǐng)的1024個(gè)Foo所占用的內(nèi)存全部歸還給操作系統(tǒng),此時(shí)并沒(méi)有內(nèi)存泄漏。再看下一種情況:
class Foo
{
public:
Foo():num_(new int(42)){}
~Foo(){delete num_;}
private:
int* num_;
};
Foo* pf = new Foo[1024];
delete pf;
此時(shí)會(huì)造成內(nèi)存泄漏,原因很簡(jiǎn)單。在執(zhí)行delete[]時(shí),首先逆序執(zhí)行每個(gè)元素的析構(gòu)函數(shù),然后再把整塊內(nèi)存歸還給操作系統(tǒng)。而delete只會(huì)把內(nèi)存還給操作系統(tǒng),沒(méi)有執(zhí)行析構(gòu)函數(shù)。當(dāng)類沒(méi)有資源需要管理時(shí),執(zhí)行與不執(zhí)行析構(gòu)函數(shù)都無(wú)關(guān)緊要,但是當(dāng)類中需要管理資源時(shí),析構(gòu)函數(shù)的執(zhí)行就至關(guān)重要了。
如何重載new和delete運(yùn)算符?
#include <iostream>
#include <cstdlib>
#include <map>
struct MemoryInfo {
size_t size;
const char* file;
int line;
};
std::map<void*, MemoryInfo> memoryMap;
void* operator new(size_t size, const char* file, int line) {
void* ptr = std::malloc(size);
memoryMap[ptr] = {size, file, line};
return ptr;
}
void operator delete(void* ptr) noexcept {
auto it = memoryMap.find(ptr);
if (it != memoryMap.end()) {
std::free(ptr);
memoryMap.erase(it);
}
}
#define new new(__FILE__, __LINE__)
int main() {
int* p = new int(42);
for (const auto& [ptr, info] : memoryMap) {
std::cout << "Memory allocated at " << ptr << " with size " << info.size
<< " in file " << info.file << " at line " << info.line << std::endl;
}
delete p;
for (const auto& [ptr, info] : memoryMap) {
std::cout << "Memory allocated at " << ptr << " with size " << info.size
<< " in file " << info.file << " at line " << info.line << std::endl;
}
return 0;
}
最后一個(gè)問(wèn)題:
如果在棧上分配一塊固定的內(nèi)存?棧中的內(nèi)存如何釋放?
使用alloca,雖然簡(jiǎn)單,但是很多人可能都沒(méi)有接觸過(guò):
int* p = (int*)alloca(4);
*p = 42;
棧上申請(qǐng)的內(nèi)存不需要手動(dòng)釋放。注意,如果棧溢出,alloca的行為時(shí)未定義的。
好了,今日份面試到這里就結(jié)束了,小伙伴們,對(duì)于今天二師兄的面試,能打幾分呢?文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-472313.html
關(guān)注我,帶你21天“精通”C++!(狗頭)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-472313.html
到了這里,關(guān)于C++面試八股文:如何在堆上和棧上分配一塊內(nèi)存?的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!