柚子快報邀請碼778899分享:c語言 C/C++內(nèi)存管理
柚子快報邀請碼778899分享:c語言 C/C++內(nèi)存管理
? ? ? ?各位nb的程序員們,大家好,當(dāng)我們要學(xué)一門語言并且要將它學(xué)到熟練的話,我們就不得不要去學(xué)習(xí)它的內(nèi)存管理這部分的內(nèi)容了,基于此處,我們這里就來學(xué)習(xí)一下C++中的內(nèi)存管理方面的知識。
目錄
1.C/C++內(nèi)存發(fā)布
? ? 1.1.棧
? ? 1.2.內(nèi)存映射段
? ? 1.3.堆
?? ?1.4.數(shù)據(jù)段
?? ?1.5.代碼段
2.C語言中的動態(tài)內(nèi)存管理方式
? ? 2.1.malloc函數(shù)
? ? 2.2.realloc函數(shù)
?? ?2.3.calloc函數(shù)
? ? 2.4.free函數(shù)
3.C++中的內(nèi)存管理方式
? ? 3.1.new/delete操作內(nèi)置類型
? ? 3.2new和delete操作自定義類型
4.operator new和operator delete函數(shù)(了解知識)
5.new和delete的實(shí)現(xiàn)原理
? ? 5.1內(nèi)置類型
? ? 5.2自定義類型
6.malloc / free和new / delete的區(qū)別
7.定位new表達(dá)式(placement—new)(了解)
1.C/C++內(nèi)存發(fā)布
? ? ? ?我們先來看一下C/C++中空間的發(fā)布情況(用圖表示更為清晰):
結(jié)合上面的那幅圖,我們這里來一一解釋一下各個空間:
? ? 1.1.棧
? ? ? ?又叫堆?!庆o態(tài)局部變量/函數(shù)參數(shù)/返回值等等,棧是向下增長的(執(zhí)行程序也是在棧中進(jìn)行的,如下圖所示)。
? ? 1.2.內(nèi)存映射段
? ? ? ?是高效的I/O映射方式,用于裝載一個?共享的動態(tài)內(nèi)存庫。用戶可使系統(tǒng)接口創(chuàng)建共享內(nèi)存,做進(jìn)程間通信。(后面會講到,這里了解一下即可)
? ? 1.3.堆
? ? ? ?用于程序運(yùn)行時動態(tài)內(nèi)存分布,堆是可以向上增長的。
?? ?1.4.數(shù)據(jù)段
? ? ? ?存儲全局?jǐn)?shù)據(jù)和靜態(tài)數(shù)據(jù)。
?? ?1.5.代碼段
? ? ? ?可執(zhí)行的代碼/只讀常量。
2.C語言中的動態(tài)內(nèi)存管理方式
? ? 2.1.malloc函數(shù)
? ? ? ?這個函數(shù)的功能就是創(chuàng)建一塊空間,它創(chuàng)建的這塊空間是在堆上開創(chuàng)的,它開創(chuàng)的空間與我們在定義變量時系統(tǒng)為我們開創(chuàng)的空間不同的地方在于,它開創(chuàng)的空間屬于是動態(tài)空間,也就是可以在這塊空間的基礎(chǔ)上還可以對這塊空間再次進(jìn)行擴(kuò)容的操作。
大家可以通過這個鏈接學(xué)習(xí)一下這個malloc函數(shù):malloc - C++ Reference
? ? 2.2.realloc函數(shù)
? ? ? ?進(jìn)行擴(kuò)容操作,這個函數(shù)的主要作用就是對malloc和calloc函數(shù)所開創(chuàng)的空間進(jìn)行擴(kuò)容操作。
大家可以通過這個鏈接學(xué)習(xí)一下這個realloc函數(shù):realloc - C++ Reference
?? ?2.3.calloc函數(shù)
? ? ? ?這個函數(shù)的作用是和malloc函數(shù)的作用差不多,都是在堆上開創(chuàng)一塊動態(tài)空間,只不過這個函數(shù)和malloc函數(shù)不同的是,malloc函數(shù)它只是開創(chuàng)一塊空間,而calloc函數(shù)不僅僅是開創(chuàng)一塊空間,在將這塊空間開創(chuàng)好后,會自動將這塊空間進(jìn)行初始化操作,默認(rèn)初始化為0。
大家可以通過這個鏈接學(xué)習(xí)一下這個calloc函數(shù):calloc - C++ Reference
? ? 2.4.free函數(shù)
? ? ? ?我們前面所講的malloc和calloc函數(shù),這兩個函數(shù)開創(chuàng)好空間之后,我們在使用完這個空間之后,我們就需要將這個開創(chuàng)的空間釋放掉,如果我們不釋放掉的話,程序執(zhí)行結(jié)束后,這塊空間在堆中就會以一種懸空的狀態(tài)仍然留在堆中,如果長期以往這樣下去的話,就會造成內(nèi)存泄漏的問題,系統(tǒng)就會崩潰,為了防止這種問題的誕生,所以我們在用完這個開創(chuàng)的空間之后,我們就可以使用free這個函數(shù)將這塊開創(chuàng)的空間釋放掉。
大家可以通過這個鏈接學(xué)習(xí)一下這個free函數(shù):free - C++ Reference
3.C++中的內(nèi)存管理方式
? ? ? ?C語言中的內(nèi)存管理方式也可以在C++中繼續(xù)使用,但是在有些地方上就顯得有點(diǎn)無能為力了,而且使用起來反而還比較麻煩,因此在C++中又提出了自己的內(nèi)存管理方式:通過new和delete操作符來進(jìn)行動態(tài)內(nèi)存管理。
? ? 3.1.new/delete操作內(nèi)置類型
? ? ? ?new的功能類似于C語言中malloc函數(shù)的功能,從堆中申請一塊空間,而delete的功能就類似于C語言中的free函數(shù)的功能,也就是歸還這塊空間的使用權(quán)。
#include
using namespace std;
int main()
{
int* a1 = new int;//從堆上動態(tài)申請一個int類型的空間,用int類型的指針去a1指向這塊空間。
int* a2 = new int(10);//從堆上動態(tài)申請一塊int類型的空間,創(chuàng)建好后,將這塊空間自動初始化為10,我們使用int類型的指針a2去接收它。
int* a3 = new int(20);//new是一個操作符,int是開創(chuàng)的空間的類型,20是初始化的值。
int* a4 = new int[10];//從堆上動態(tài)開辟10個類型為int的連續(xù)空間,開創(chuàng)好后并不會對其進(jìn)行初始化操作。
int* a5 = new int[10] {0};//從堆上動態(tài)開辟10個類型為int的連續(xù)空間,開創(chuàng)好后對這10個空間均進(jìn)行初始化操作,并且均初始化為0。
int* a6 = new int[10] {1, 2, 3, 4, 5, 6};//從堆上動態(tài)開辟10個類型為10個類型為int的連續(xù)空間,這塊空間的前6個空間分別初始化為1,2,3,4,5,6其余的空間默認(rèn)初始化為0。
delete a1;//釋放我們這里所創(chuàng)建的空間。
delete a2;//dalete是操作符。
delete a3;
delete[] a4;//由于a4指向的是一塊連續(xù)的空間,因此我們這里的delete后面要加上[],若要問為什么,就是C++語法規(guī)定的。
delete[] a5;
delete[] a6;
return 0;
}
注意:(1).我們在對a6指針指向的那塊空間進(jìn)行初始化操作時,我們只初始化了這塊連續(xù)的空間的前6個空間,其余的空間我們C++默認(rèn)初始化為0,這里的默認(rèn)初始化我們需要注意一下,就是初始化這里的默認(rèn)初始化為0,是在給了編譯器的初始值的情況下,并且所給的初始值的個數(shù)要小于所開的空間個數(shù)的前提下,初始化后剩余的空間編譯器才會默認(rèn)初始化為0,如果我們這里不給初始值的話,編譯器是不會對所開的空間進(jìn)行初始化操作的。
? ? ? ? ? ?(2).申請和釋放單個元素的空間,我們這里使用new和delete,申請和釋放連續(xù)的空間,我們這里使用new[ ]和delete[ ],一定要配套起來使用。
? ? 3.2new和delete操作自定義類型
? ? ? ?我們這里在進(jìn)行講解操作前先來補(bǔ)充一個知識:new/delete和malloc/free的最大區(qū)別就是new/delete對于自定義類型它除了會進(jìn)行開空間的操作以外還會自動調(diào)用這個自定義類型的構(gòu)造函數(shù)和析構(gòu)函數(shù),而malloc/free只會開空間和釋放相應(yīng)的空間。
#include
using namespace std;
class date
{
public:
date(int year = 1, int month = 1, int day = 1)
{
cout << "date()" << endl;
}
//編譯器會在這里自動生成一個拷貝構(gòu)造,我們這里就不寫了。
~date()
{
cout << "~date()" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
date* d1 = new date;//date();我們從打印結(jié)果來看的話,當(dāng)我們使用new函數(shù)開創(chuàng)了一個date自定義類型的空間時,它確實(shí)是自動調(diào)用了date類的構(gòu)造函數(shù)。
delete d1;//~date();從打印結(jié)果來看的話,我們在調(diào)用delete函數(shù)的時候,確實(shí)是會自動調(diào)用d1對象的析構(gòu)函數(shù)。
date* d2 = new date[10];//這里會連續(xù)輸出10行date(),因為我們在這里開創(chuàng)的是一塊數(shù)組,這個數(shù)組中有10個空間,也就相當(dāng)于是連續(xù)開創(chuàng)了10個date類型的空間,因此編譯器在這里會調(diào)用10次構(gòu)造函數(shù)。
//接下來我們來看一下這里的初始化的問題。
date* d3 = new date{ 2024,9,13 };//我們在堆上動態(tài)申請了一塊的date連續(xù)的內(nèi)存空間,我們對其進(jìn)行進(jìn)行初始化操作(換句話說,其實(shí)也就是這里的傳參操作),這種初始化的方式是叫做隱式類型轉(zhuǎn)換的方式,也就是說,這里編譯器會根據(jù)這3個實(shí)參實(shí)現(xiàn)構(gòu)造一個date類型的臨時對象,構(gòu)造好后,會調(diào)用拷貝構(gòu)造函數(shù)將這個臨時對象中的數(shù)據(jù)拷貝到這個我們剛剛開創(chuàng)的新空間中。
//構(gòu)造函數(shù)的方式進(jìn)行初始化(有名)
date b1(1, 2, 3);
date b2(4, 5, 6);
date b3(7, 8, 9);
date* d4 = new date[3]{ b1,b2,b3 };//這里會連續(xù)輸出3行date(date& d),這里編譯器其實(shí)會調(diào)用拷貝構(gòu)造函數(shù)分別將b1,b2,b3這3個對象中的數(shù)據(jù)一一拷貝到剛剛開創(chuàng)的3個date類型的空間中。
//匿名對象進(jìn)行初始化
date* d5 = new date[3]{ date(11,22,33), date(44,55,66),date(77,88,99) };
delete[10] d2;
delete d3;
delete[3] d4;
delete[3] d5;
return 0;
}
4.operator new和operator delete函數(shù)(了解知識)
? ? ? ?new和delete是用戶進(jìn)行動態(tài)內(nèi)存申請和釋放的操作符,operator new和operator delete是系統(tǒng)提供的全局函數(shù),new在底層調(diào)用operator new全局函數(shù)來申請空間,delete在底層調(diào)用operator delete來釋放空間。
? ? ? ?operator new實(shí)際上也是通過malloc函數(shù)來申請空間的,如果malloc函數(shù)申請空間成功的話就直接返回,否則就執(zhí)行用戶提供的空間不足應(yīng)對措施,如果用戶提供該措施的話就繼續(xù)申請,否則就會拋異常。operator dalete最終是通過free函數(shù)來釋放空間的。
5.new和delete的實(shí)現(xiàn)原理
? ? 5.1內(nèi)置類型
? ? ? ?如果申請的是內(nèi)置類型的空間,new和malloc,delete和free基本類似,不同的地方是:new/delete申請和釋放的是單個元素的空間,new[ ]和delete[ ]申請和釋放的是連續(xù)的空間,而new在申請空間失敗時會被拋異常,malloc會返回NULL。
? ? 5.2自定義類型
? ? ? ?(1).new的原理
? ? ? ? ? ? ? 1>.調(diào)用operator new函數(shù)申請空間。
? ? ? ? ? ? ? 2>.在申請的空間上執(zhí)行析構(gòu)函數(shù),完成對象的構(gòu)造。
? ? ? ?(2).delete的原理
? ? ? ? ? ? ? 1>.在空間上執(zhí)行析構(gòu)函數(shù),完成對象中資源的清理工作。
? ? ? ? ? ? ? 2>.調(diào)用operator dalete函數(shù),釋放對象的空間。
? ? ? ?(3).new [T]的原理
? ? ? ? ? ? ? 1>.調(diào)用operator new[ ]函數(shù),在operator new[ ]中實(shí)際調(diào)用operator new函數(shù)完成N個對象空間的申請。
? ? ? ? ? ? ? 2>.在申請的空間上執(zhí)行N次析構(gòu)函數(shù)。
? ? ? ?(4).delete的原理
? ? ? ? ? ? ? 1>.在釋放的對象空間上執(zhí)行N次析構(gòu)函數(shù),完成N個對象中資源的清理。
? ? ? ? ? ? ? 2>.調(diào)用operator dalete[ ]釋放空間,實(shí)際在operator dalete[ ]中調(diào)用operator dalete函數(shù)來釋放空間。
? ? ? ?總結(jié):malloc函數(shù)和free函數(shù)配套使用,new和delete函數(shù)配套使用,申請和釋放空間一定要配套,否則的話,會有出錯的風(fēng)險。
6.malloc / free和new / delete的區(qū)別
? ? ? ?(1).共同點(diǎn):都是從堆上申請空間,并且需要用戶手動進(jìn)行釋放。
? ? ? ?(2).不同點(diǎn):1>.malloc和free是函數(shù),new/delete是操作符。
? ? ? ? ? ? ? 2>.malloc申請的空間不會繼續(xù)初始化操作,而new則可以自動進(jìn)行初始化操作。
? ? ? ? ? ? ? 3>.malloc申請空間時,需要手動計算空間的大小并進(jìn)行傳遞,new只需要在其后面跟上空間的類型即可,如果是多個對象的話,在[ ]中指定對象的個數(shù)就可以了。
? ? ? ? ? ? ? 4>.malloc函數(shù)的返回值未void*類型,在使用時我們必須將它強(qiáng)轉(zhuǎn),而new是不需要的,因為new后面跟的就是這個空間的類型。
? ? ? ? ? ? ? 5>.malloc申請空間失敗時,返回的是NULL,因此使用時必須要判空,而new不需要,大那是new需要捕獲異常。
? ? ? ? ? ? ? 6>.申請自定義類型的對象時,malloc/free只會開辟空間,不會調(diào)用構(gòu)造函數(shù)與析構(gòu)函數(shù),而new在申請空間的同時就會自動調(diào)用構(gòu)造函數(shù)完成對象的初始化操作,delete在釋放空間前會調(diào)用析構(gòu)函數(shù)完成空間中資源的清理釋放。
7.定位new表達(dá)式(placement—new)(了解)
? ? ? ?定位new表達(dá)式是在已分配的原始內(nèi)存空間中調(diào)用構(gòu)造函數(shù)初始化一個對象。
? ? ? ?使用格式:new (place_address) type? ? ? ? ? ? ? ? ? ? ? ? ?}? ?place_address必須是一個指針
? ? ? ? ? ? ? ? ? ? ? ? ?new (place_address) type(initializer_list)? }? ?initializer_list是類型的初始化列表
? ? ? ?使用場景:在實(shí)際中一般是配合內(nèi)存池一起使用的,因為內(nèi)存池它所分配出來的空間不會進(jìn)行初始化,所以如果自定義類型的對象,需要使用new的定義表達(dá)式進(jìn)行調(diào)用構(gòu)造函數(shù)以使得對對象進(jìn)行初始化操作。
#include
using namespace std;
class date
{
public:
date(int a = 1)
{
cout << "date(int a = 1)" << endl;
}
~date()
{
cout << "~date()" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
//我們在這里先試著使用operator new函數(shù)來開創(chuàng)一塊空間。
date* d1 = (date*)operator new(sizeof(date));//通過這句代碼我們可以得知,這個operator new這個函數(shù)可以直接調(diào)用,我們這里是直接調(diào)用operator new這個函數(shù),因此,這里不會調(diào)用date的構(gòu)造函數(shù),若想調(diào)用構(gòu)造函數(shù)的話,可以使用new的定位表達(dá)式。
new(d1)date;//new是關(guān)鍵字(操作符),d1是指針,date是對象的類型。這個就是定位new的寫法。這樣的話,編譯器就會調(diào)用date類的構(gòu)造函數(shù)給d1指針指向的那塊空間進(jìn)行初始化操作。
//如果date類需要傳值的話,我們這里還可以傳值:new(d1)date(10);
d1->~date();//析構(gòu)函數(shù)的話,我們則可以使用"->"這個操作符直接調(diào)用即可。
operator delete(d1);//釋放空間。
return 0;
}
? ? ? ?OK,這一節(jié)的內(nèi)容我們就講到這里,謝謝大家的觀看,你們的支持就是我最大的動力。
柚子快報邀請碼778899分享:c語言 C/C++內(nèi)存管理
好文閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。