??個人主頁:Quitecoder
??專欄:c++筆記倉
朋友們大家好,本篇文章我們詳細(xì)講解c++中的動態(tài)內(nèi)存管理
1.C/C++內(nèi)存分布
我們來看內(nèi)存區(qū)域劃分
數(shù)據(jù)段就是我們所說的全局變量,代碼段是我們所說的常量區(qū),我們需要重點關(guān)注的是堆區(qū),這部分是由我們自己控制的
int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
static int staticVar = 1;
int localVar = 1;
int num1[10] = { 1, 2, 3, 4 };
char char2[] = "abcd";
const char* pChar3 = "abcd";
int* ptr1 = (int*)malloc(sizeof(int) * 4);
int* ptr2 = (int*)calloc(4, sizeof(int));
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
free(ptr1);
free(ptr3);
}
選擇題:
選項: A.棧 B.堆 C.數(shù)據(jù)段(靜態(tài)區(qū)) D.代碼段(常量區(qū))
globalVar在哪里?__1__ staticGlobalVar在哪里?__2__
staticVar在哪里?__3__ localVar在哪里?__4__
num1 在哪里?__5__
char2在哪里?__6__ *char2在哪里?_7__
pChar3在哪里?__8__ *pChar3在哪里?__9__
ptr1在哪里?_10___ *ptr1在哪里?__11__
我們來依次討論:
-
globalVar
是全局變量,不是靜態(tài)的,所以它存儲在數(shù)據(jù)段(靜態(tài)區(qū)) -
staticGlobalVar
也是全局變量,但它是靜態(tài)的,因此它同樣存儲在數(shù)據(jù)段(靜態(tài)區(qū)) -
staticVar
是函數(shù)內(nèi)的靜態(tài)變量,所以它存儲在數(shù)據(jù)段(靜態(tài)區(qū)),因為它的生命周期貫穿程序的整個執(zhí)行期 -
localVar
是局部變量,存儲在棧上 -
num1
是局部變量,它是數(shù)組,存儲在棧上 -
char2
是局部變量,它是數(shù)組首元素的地址,存儲在棧上 -
*char2
(即char2數(shù)組的內(nèi)容)存儲在棧上,因為char2
本身就在棧上 -
pChar3
是局部指針變量,存儲在棧上 -
*pChar3
指向的內(nèi)容(即字符串"abcd")存儲在代碼段(常量區(qū)) -
ptr1
是局部指針變量,存儲在棧上 -
*ptr1
指向的內(nèi)容(即通過malloc
分配的內(nèi)存)存儲在堆上
-
*char2
(局部字符數(shù)組)
當(dāng)你聲明一個局部字符數(shù)組并用一個字符串字面量初始化它,如char char2[] = "abcd";
時,編譯器在棧上為數(shù)組分配內(nèi)存,然后將字符串字面量的內(nèi)容(包括結(jié)尾的\0
字符)復(fù)制到這塊內(nèi)存中。因此,char2
和它的內(nèi)容(*char2
指向的內(nèi)容)都存儲在棧上 -
*pChar3
(字符串字面量指針)
另一方面,當(dāng)你使用指針指向一個字符串字面量,如const char* pChar3 = "abcd";
時,這個字符串字面量存儲在程序的只讀數(shù)據(jù)段(或稱為代碼段、常量區(qū))中。pChar3
本身作為一個局部指針變量存儲在棧上,但它指向的字符串(“abcd”)實際上存儲在常量區(qū)。這是因為字符串字面量被視為常量數(shù)據(jù),編譯器會將它們放在程序的常量區(qū)域內(nèi),這個區(qū)域通常是只讀的,以防止程序意外修改它的內(nèi)容。因此,盡管pChar3
是一個指針,存儲在棧上,但它指向的字符串內(nèi)容存儲在常量區(qū)
總結(jié):
-
*char2
不在常量區(qū),因為char2
是局部字符數(shù)組,其內(nèi)容直接存儲在棧上。 -
*pChar3
在常量區(qū),因為它指向的是一個字符串字面量,字符串字面量被存儲在程序的常量區(qū)域,這部分內(nèi)存是只讀的。
當(dāng)我們討論變量存儲在哪里時,通常涉及到幾個關(guān)鍵區(qū)域:棧(Stack)、堆(Heap)、數(shù)據(jù)段(Data Segment,又稱靜態(tài)區(qū))、和代碼段(Code Segment,又稱常量區(qū))。每種類型的變量根據(jù)其特性和聲明周期被存儲在這些區(qū)域中的相應(yīng)位置
-
棧是用于存儲局部變量、函數(shù)參數(shù)等的內(nèi)存區(qū)域。當(dāng)一個函數(shù)被調(diào)用時,其局部變量和一些書keeping信息被推入棧中;當(dāng)函數(shù)執(zhí)行完成,這些信息被從棧上彈出。棧是自動管理的,開發(fā)者無需手動分配或釋放內(nèi)存。
-
堆是用于動態(tài)內(nèi)存分配的內(nèi)存區(qū)域。不同于棧,開發(fā)者需要顯式地從堆上分配內(nèi)存(如使用
malloc
或new
),并在不再需要時釋放這些內(nèi)存(如使用free
或delete
)。 -
數(shù)據(jù)段,又稱為靜態(tài)區(qū),用于存儲全局變量、靜態(tài)變量等。這些變量的生命周期貫穿整個程序執(zhí)行期,因此它們被存儲在一個特定的、持久的內(nèi)存區(qū)域中。
-
代碼段,又稱為常量區(qū),用于存儲程序的執(zhí)行代碼和常量數(shù)據(jù),如字符串字面量。這部分內(nèi)存是只讀的,用來保證程序代碼的安全性
2.C語言中動態(tài)內(nèi)存管理方式:malloc/calloc/realloc/free
在C語言中,動態(tài)內(nèi)存管理是通過一組標(biāo)準(zhǔn)庫函數(shù)完成的,包括malloc
, calloc
, realloc
, 和 free
。這些函數(shù)允許程序在運行時動態(tài)地分配、調(diào)整和釋放堆內(nèi)存,這是對于管理變化的數(shù)據(jù)量和大小特別有用的能力。下面是這些函數(shù)的基本用法和它們之間的區(qū)別:
malloc
-
用法:
void* malloc(size_t size);
-
功能:分配指定字節(jié)數(shù)的未初始化內(nèi)存。它返回一個指向分配的內(nèi)存的指針。如果分配失敗,返回
NULL
。 -
示例:
int* ptr = (int*)malloc(sizeof(int) * 4);
這行代碼為4個整數(shù)分配了內(nèi)存
calloc
-
用法:
void* calloc(size_t num, size_t size);
-
功能:為指定數(shù)量的元素分配內(nèi)存,每個元素的大小也在參數(shù)中指定,并自動初始化所有位為0。如果分配失敗,返回
NULL
。 -
示例:
int* ptr = (int*)calloc(4, sizeof(int));
這行代碼為4個整數(shù)分配了內(nèi)存,并將它們初始化為0。
realloc
-
用法:
void* realloc(void* ptr, size_t size);
-
功能:調(diào)整之前調(diào)用
malloc
或calloc
分配的內(nèi)存塊的大小。如果新的大小大于原始大小,可能會移動內(nèi)存塊到新的位置以提供足夠的連續(xù)空間。如果realloc
的第一個參數(shù)是NULL
,它的行為就像malloc
。 -
示例:
ptr = (int*)realloc(ptr, sizeof(int) * 8);
這行代碼將之前分配的內(nèi)存大小調(diào)整為8個整數(shù)的大小。
free
-
用法:
void free(void* ptr);
-
功能:釋放之前通過
malloc
,calloc
, 或realloc
分配的內(nèi)存。一旦內(nèi)存被釋放,那塊內(nèi)存就不能再被訪問了。 - 注意:嘗試釋放未經(jīng)分配的內(nèi)存塊或多次釋放同一個內(nèi)存塊是不安全的,可能導(dǎo)致未定義行為
注意
- 在使用這些函數(shù)時,確保正確處理內(nèi)存分配失敗的情況,并在內(nèi)存不再需要時使用
free
來避免內(nèi)存泄露。 - 當(dāng)使用
realloc
時,如果分配失敗,原始內(nèi)存不會被釋放。因此,建議先將realloc
的返回值賦給一個臨時指針,以檢查是否分配成功,再重新賦值給原始指針,以避免內(nèi)存泄漏。 - 始終確保只對通過
malloc
,calloc
, 或realloc
分配的指針使用free
,并且每個分配的內(nèi)存塊只被free
一次
3.c++內(nèi)存管理方式
C語言內(nèi)存管理方式在C++中可以繼續(xù)使用,但有些地方就無能為力,而且使用起來比較麻煩,因此C++又提出了自己的內(nèi)存管理方式:通過
new
和delete
操作符進(jìn)行動態(tài)內(nèi)存管理
3.1new/delete對內(nèi)置類型的操作
new
的基本用法
Type* variable = new Type(arguments);
- Type:要分配的對象類型
- variable:指向分配的內(nèi)存的指針
- arguments:傳遞給構(gòu)造函數(shù)的參數(shù)(如果需要的話)
示例:
int* ptr1 = new int;
在堆上分配了一個int
大小的內(nèi)存
int* ptr2 = new int[10];
加上方括號[ ]
表示分配了十個int
大小的內(nèi)存
釋放:
對于ptr,我們直接delete
delete ptr1;
釋放數(shù)組對象的內(nèi)存ptr2,我們需要加上方括號:
delete [] ptr2;
我們也可以分配內(nèi)存的同時直接初始化:
int* ptr5 = new int(5);
動態(tài)申請一個int
類型的空間并初始化為5
我們也可以同時開辟多個空間完成初始化:
int* ptr6 = new int[10] {1,2,3,4,5};
后面的空間默認(rèn)初始化為零
- 盡管
new
和delete
提供了對象構(gòu)造和析構(gòu)的自動管理,但程序員仍然需要負(fù)責(zé)確保每個用new
分配的內(nèi)存都被對應(yīng)的delete
釋放,以避免內(nèi)存泄露 - 與
malloc
和free
一樣,試圖delete
一個未經(jīng)new
分配的指針,或者對同一個指針執(zhí)行多次delete
,都是未定義行為,并且可能導(dǎo)致程序崩潰 - 當(dāng)使用
new[]
分配數(shù)組時,必須使用對應(yīng)的delete[]
來釋放內(nèi)存。使用錯誤的delete
形式也是未定義行為
來看下面的代碼:
struct ListNode
{
ListNode* _next;
int _val;
ListNode(int val)
:_next(nullptr)
,_val(val)
{}
};
struct ListNode* CreateListNode(int val)
{
struct ListNode* newnode = (struct ListNode*)malloc(sizeof(struct ListNode));
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->_next = NULL;
newnode->_val = val;
return newnode;
}
這是c語言構(gòu)造一個節(jié)點并完成初始化的過程,我們來看c++的實現(xiàn):
int main()
{
ListNode* node1 = new ListNode(1);
return 0;
}
這行代碼自動為ListNode
對象分配了內(nèi)存,并調(diào)用了其構(gòu)造函數(shù)進(jìn)行初始化。這種方式更簡潔,也更安全,因為它保證了對象在使用前被正確初始化,注意這里L(fēng)istNode是自定義類型,除了開空間還會調(diào)用構(gòu)造函數(shù)
只要我們寫好構(gòu)造函數(shù),我們發(fā)現(xiàn)new的使用是十分方便的
我們來構(gòu)建一個鏈表:
ListNode* CreateList(int n)
{
ListNode head(-1); // 哨兵位
ListNode* tail = &head;
int val;
printf("請依次輸入%d個節(jié)點的值:>", n);
for (size_t i = 0; i < n; i++)
{
cin >> val;
tail->_next = new ListNode(val);
tail = tail->_next;
}
return head._next;
}
我們輸入五個值,1 2 3 4 5
哨兵節(jié)點:
ListNode head(-1)
;這行代碼創(chuàng)建了一個局部的哨兵節(jié)點,它的值被設(shè)為-1(這個值通常是任意的,因為哨兵節(jié)點本身不存儲任何有意義的數(shù)據(jù))。哨兵節(jié)點的主要目的是簡化在鏈表頭部的插入和刪除操作,因為你總是有一個非空的節(jié)點作為鏈表的起始點,從而避免了處理空鏈表的特殊情況
最后,函數(shù)通過return head._next;
返回新構(gòu)建鏈表的頭節(jié)點。由于head是一個哨兵節(jié)點,它的_next成員實際上指向鏈表的第一個真實節(jié)點(如果有的話),或者是nullptr(如果n為0或用戶沒有輸入任何有效數(shù)據(jù))
3.1.1拋異常
我們不用手動檢查new是否開辟成功,new失敗了會拋出異常
void func()
{
int n = 1;
while (1)
{
int* p = new int[1024 * 1024*100];
cout <<n<<"->"<< p << endl;
++n;
}
}
我們一次申請400M的空間大小
再看c語言版本
void func()
{
int n = 1;
while (1)
{
//int* p = new int[1024 * 1024 * 100];
int* p = (int*)malloc(1024 * 1024 * 400);
cout << n << "->" << p << endl;
++n;
}
}
開辟失敗,程序無限循環(huán)并返回空
c++中的拋異常:
try
{
func();
}
catch (const exception& e)
{
cout << e.what() << endl;
}
這段代碼是C++中的一個示例,展示了如何使用try-catch語句來處理異常。這里的重點是捕獲并處理func()
函數(shù)中可能拋出的異常。如果func()
函數(shù)執(zhí)行中出現(xiàn)了問題,它將拋出一個異常,這個異常會被catch塊捕獲。捕獲到的異常類型為const std::exception&
,這是C++標(biāo)準(zhǔn)異常類型的一個基類。在catch塊中,通過e.what()
調(diào)用來獲取并打印出異常的具體信息
-
try塊:在try塊中的代碼執(zhí)行時,如果發(fā)生了異常(即代碼拋出了異常),那么try塊中的剩余代碼將不會繼續(xù)執(zhí)行,而是跳轉(zhuǎn)到相應(yīng)的catch塊中處理異常
-
catch塊:此代碼段用于捕獲類型為
const std::exception&
的異常。這意味著它能夠捕獲任何是std::exception
實例或其派生類的異常。通過常量引用捕獲異常是一種最佳實踐,因為這樣可以避免異常對象的切片問題,并且可以最小化性能開銷 -
const exception& e:這里聲明了一個名為
e
的引用,它引用了被捕獲的異常。const
限定符表明在catch塊中,e
是不會被修改的 -
e.what():
std::exception
及其派生類有一個名為what()
的成員函數(shù),它返回一個描述異常的空終止字符序列(C風(fēng)格字符串)。cout << e.what() << endl;
語句將這個消息打印到標(biāo)準(zhǔn)輸出中
后續(xù)我們還會遇到這個函數(shù),再詳細(xì)講解
來看拋異常的結(jié)果:
3.2new/delete對自定義類型的操作
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main()
{
A* p1 = new A(1);
delete p1;
return 0;
}
new/delete
和 malloc/free
最大區(qū)別是 new/delete
對于【自定義類型】除了開空間,還會調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù)
A* p1 = new A(1);
00007FF798AA260B mov ecx,4
00007FF798AA2610 call operator new (07FF798AA104Bh)
00007FF798AA2615 mov qword ptr [rbp+108h],rax
00007FF798AA261C cmp qword ptr [rbp+108h],0
00007FF798AA2624 je main+50h (07FF798AA2640h)
00007FF798AA2626 mov edx,1
00007FF798AA262B mov rcx,qword ptr [rbp+108h]
00007FF798AA2632 call A::A (07FF798AA1343h)
00007FF798AA2637 mov qword ptr [rbp+138h],rax
00007FF798AA263E jmp main+5Bh (07FF798AA264Bh)
00007FF798AA2640 mov qword ptr [rbp+138h],0
00007FF798AA264B mov rax,qword ptr [rbp+138h]
00007FF798AA2652 mov qword ptr [rbp+0E8h],rax
00007FF798AA2659 mov rax,qword ptr [rbp+0E8h]
00007FF798AA2660 mov qword ptr [p1],rax
new過程跳轉(zhuǎn)到構(gòu)造函數(shù)
delete調(diào)用析構(gòu)函數(shù)
打印結(jié)果如下:
A():000001DB79796B50
~A():000001DB79796B50
我們發(fā)現(xiàn),匯編代碼中有這一步:
00007FF798AA2610 call operator new (07FF798AA104Bh)
operator new,接下來我們來講解這一部分
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在底層通過operator delete全局函數(shù)來釋放空間
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
void* p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
// 如果申請內(nèi)存失敗了,這里會拋出bad_alloc 類型異常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
operator new
:該函數(shù)實際通過malloc來申請空間,當(dāng)malloc申請空間成功時直接返回;申請空間失敗,嘗試執(zhí)行空間不足應(yīng)對措施,如果改應(yīng)對措施用戶設(shè)置了,則繼續(xù)申請,否則拋異常。
static const std::bad_alloc nomem;
申請失敗則會拋異常
void operator delete(void* pUserData)
{
_CrtMemBlockHeader* pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /* block other threads */
__TRY
/* get a pointer to memory block header */
pHead = pHdr(pUserData);
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg(pUserData, pHead->nBlockUse);
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
_free_dbg(pUserData, pHead->nBlockUse);
operator delete: 該函數(shù)最終是通過free來釋放空間的
通過上述兩個全局函數(shù)的實現(xiàn)知道,operator new 實際也是通過malloc來申請空間,如果malloc申請空間成功就直接返回,否則執(zhí)行用戶提供的空間不足應(yīng)對措施,如果用戶提供該措施就繼續(xù)申請,否則就拋異常。operator delete 最終是通過free來釋放空間的
我們只需要簡單了解一下,并不需要深入理解
5.new和delete的實現(xiàn)原理
如果申請的是內(nèi)置類型的空間,new和malloc,delete和free基本類似,不同的地方是:new/delete申請和釋放的是單個元素的空間,new[]和delete[]申請的是連續(xù)空間,而且new在申請空間失敗時會拋異常,malloc會返回NULL
自定義類型:
-
new的原理
- 調(diào)用operator new函數(shù)申請空間
- 在申請的空間上執(zhí)行構(gòu)造函數(shù),完成對象的構(gòu)造
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main()
{
A* p1 = new A(1);
delete p1;
return 0;
}
-
delete的原理:
- 在空間上執(zhí)行析構(gòu)函數(shù),完成對象中資源的清理工作
- 調(diào)用operator delete函數(shù)釋放對象的空間
class Stack
{
public:
Stack()
{
_a = (int*)malloc(sizeof(int) * 4);
_top = 0;
_capacity = 4;
}
~Stack()
{
free(_a);
_top = _capacity = 0;
}
private:
int* _a;
int _top;
int _capacity;
};
int main()
{
Stack* pst = new Stack;
delete pst;
return 0;
}
這里進(jìn)行了雙層嵌套:
我們就很清楚的能看到,現(xiàn)需要調(diào)用析構(gòu)函數(shù)再進(jìn)行釋放
-
new T[N]的原理
- 調(diào)用
operator new[]
函數(shù),在operator new[]
中實際調(diào)用operator new函數(shù)完成N個對象空間的申請 - 在申請的空間上執(zhí)行N次構(gòu)造函數(shù)
- 調(diào)用
-
delete[]
的原理- 在釋放的對象空間上執(zhí)行N次析構(gòu)函數(shù),完成N個對象中資源的清理
- 調(diào)用
operator delete[]
釋放空間,實際在operator delete[]
中調(diào)用operator delete來釋放空間
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main()
{
A* p1 = new A;
A* p2 = new A[10];
delete p1;
delete[]p2;
return 0;
}
在這段代碼中,p2
是指向由 new A[10]
分配的對象數(shù)組的指針。雖然你可能會認(rèn)為 p2
只需要分配足夠存儲 10 個 A
類型對象的空間,即 10 * sizeof(A)
,實際上編譯器通常會分配額外的空間來存儲有關(guān)數(shù)組本身的信息,比如數(shù)組的大小。這是因為在執(zhí)行 delete[] p2;
時,系統(tǒng)需要知道要調(diào)用多少次析構(gòu)函數(shù)
讓我們具體看一下為什么會這樣:
-
對象數(shù)組的內(nèi)存分配:當(dāng)你創(chuàng)建一個對象數(shù)組時,例如
new A[10]
,C++ 需要知道在稍后釋放數(shù)組時應(yīng)該調(diào)用多少次析構(gòu)函數(shù)。為此,它可能在分配給數(shù)組的內(nèi)存塊中存儲一些額外的元數(shù)據(jù),通常是數(shù)組的長度 -
析構(gòu)函數(shù)調(diào)用:在使用
delete[] p2;
釋放內(nèi)存時,這個額外存儲的信息就被用來確保為數(shù)組中的每個元素正確調(diào)用析構(gòu)函數(shù) -
內(nèi)存布局:因此,分配給
p2
的內(nèi)存實際上包含了更多比簡單的10 * sizeof(A)
字節(jié)。首先是數(shù)組長度的元數(shù)據(jù)(大小取決于系統(tǒng)和編譯器),緊接著是 10 個A
類型對象的存儲空間 -
字節(jié)大小:如果
sizeof(A)
是 4(假設(shè)int
類型是 4 字節(jié),并且沒有類對齊導(dǎo)致的額外空間),那么僅對象部分就占用了 40 字節(jié)。加上存儲數(shù)組大小的額外空間,總大小就會超過 40 字節(jié)
我們再來看內(nèi)置類型:
int* p1=new int[10];
00007FF7F031206B mov ecx,28h
剛好開辟了四十個字節(jié)的空間,因為它不需要調(diào)用析構(gòu)函數(shù)
6.簡單了解定位new表達(dá)式(placement-new)
定位new表達(dá)式是在已分配的原始內(nèi)存空間中調(diào)用構(gòu)造函數(shù)初始化一個對象
使用格式:new (place_address) type
或者new (place_address) type(initializer-list)
place_address
必須是一個指針,initializer-list
是類型的初始化列表
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main()
{
int* p1 = new int[10];
return 0;
}
A* p1 = (A*)malloc(sizeof(A));
p1現(xiàn)在指向的只不過是與A對象相同大小的一段空間,還不能算是一個對象,因為構(gòu)造函數(shù)沒有執(zhí)行
new(p1)A;
顯示調(diào)用構(gòu)造函數(shù)對一塊已經(jīng)有的空間的初始化
定位new表達(dá)式在實際中一般是配合內(nèi)存池使用。因為內(nèi)存池分配出的內(nèi)存沒有初始化,所以如果是自定義類型的對象,需要使用new的定義表達(dá)式進(jìn)行顯示調(diào)構(gòu)造函數(shù)進(jìn)行初始化
7.概念辨析
7.1 malloc/free和new/delete的區(qū)別
malloc/free和new/delete的共同點是:都是從堆上申請空間,并且需要用戶手動釋放。不同的地方是:
- malloc和free是函數(shù),new和delete是操作符
- malloc申請的空間不會初始化,new可以初始化
- malloc申請空間時,需要手動計算空間大小并傳遞,new只需在其后跟上空間的類型即可,如果是多個對象,
[]
中指定對象個數(shù)即可 - malloc的返回值為
void*
, 在使用時必須強轉(zhuǎn),new不需要,因為new后跟的是空間的類型 - malloc申請空間失敗時,返回的是NULL,因此使用時必須判空,new不需要,但是new需要捕獲異常
- 申請自定義類型對象時,malloc/free只會開辟空間,不會調(diào)用構(gòu)造函數(shù)與析構(gòu)函數(shù),而new在申請空間后會調(diào)用構(gòu)造函數(shù)完成對象的初始化,delete在釋放空間前會調(diào)用析構(gòu)函數(shù)完成空間中資源的清理
7.2 內(nèi)存泄漏
- 什么是內(nèi)存泄漏:內(nèi)存泄漏指因為疏忽或錯誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存的情況。內(nèi)存泄漏并不是指內(nèi)存在物理上的消失,而是應(yīng)用程序分配某段內(nèi)存后,因為設(shè)計錯誤,失去了對該段內(nèi)存的控制,因而造成了內(nèi)存的浪費。
- 內(nèi)存泄漏的危害:長期運行的程序出現(xiàn)內(nèi)存泄漏,影響很大,如操作系統(tǒng)、后臺服務(wù)等等,出現(xiàn)內(nèi)存泄漏會導(dǎo)致響應(yīng)越來越慢,最終卡死
分類:
-
堆內(nèi)存泄漏(Heap leak):
堆內(nèi)存指的是程序執(zhí)行中依據(jù)須要分配通過malloc / calloc / realloc / new等從堆中分配的一塊內(nèi)存,用完后必須通過調(diào)用相應(yīng)的 free或者delete 刪掉。假設(shè)程序的設(shè)計錯誤導(dǎo)致這部分內(nèi)存沒有被釋放,那么以后這部分空間將無法再被使用,就會產(chǎn)生Heap Leak -
系統(tǒng)資源泄漏
指程序使用系統(tǒng)分配的資源,比方套接字、文件描述符、管道等沒有使用對應(yīng)的函數(shù)釋放掉,導(dǎo)致系統(tǒng)資源的浪費,嚴(yán)重可導(dǎo)致系統(tǒng)效能減少,系統(tǒng)執(zhí)行不穩(wěn)定文章來源:http://www.zghlxwxcb.cn/news/detail-853760.html
本節(jié)內(nèi)容到此結(jié)束!!求大家三連啊!文章來源地址http://www.zghlxwxcb.cn/news/detail-853760.html
到了這里,關(guān)于【c/c++】深入探秘:C++內(nèi)存管理的機制的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!