??專欄導(dǎo)讀
??作者簡介:花想云,在讀本科生一枚,致力于 C/C++、Linux 學(xué)習(xí)。
??本文收錄于 C++系列,本專欄主要內(nèi)容為 C++ 初階、C++ 進階、STL 詳解等,專為大學(xué)生打造全套 C++ 學(xué)習(xí)教程,持續(xù)更新!
??相關(guān)專欄推薦:C語言初階系列 、C語言進階系列 、數(shù)據(jù)結(jié)構(gòu)與算法
??文章導(dǎo)讀
本章我們將學(xué)習(xí)C++的內(nèi)存分配
與動態(tài)內(nèi)存管理
。理解new/delete
的用法與實現(xiàn)的原理,并簡單了解定位new表達式
。
??C/C++內(nèi)存分布
在C語言階段,我們已經(jīng)學(xué)習(xí)過內(nèi)存分布,認識了什么是堆、堆棧、靜態(tài)區(qū)、常量區(qū)等。如下圖:
我們再次認識一下這幾個區(qū)域:
-
棧
:又叫堆棧,主要存放非靜態(tài)局部變量、函數(shù)參數(shù)、返回值等等,棧是向下增長的; -
內(nèi)存映射段
:是高效的I/O映射方式,用于裝載一個共享的動態(tài)內(nèi)存庫。用戶可使用系統(tǒng)接口創(chuàng)建共享共享內(nèi)存,做進程間通信。(Linux系列文章中有講解); -
堆
:用于程序運行時動態(tài)內(nèi)存分配,堆是可以上增長的; -
數(shù)據(jù)段
:存儲全局數(shù)據(jù)和靜態(tài)數(shù)據(jù); -
代碼段
:存儲可執(zhí)行的代碼與只讀常量;
??牛刀小試
我們可以通過以下測試例題來檢驗自己是否還清晰的記得C語言內(nèi)存分配的知識。
??測試例題
#include<stdio.h>
#include<stdlib.h>
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在哪里?____ staticGlobalVar在哪里?____
staticVar在哪里?____ localVar在哪里?____
num1在哪里?____char2在哪里?____ *char2在哪里?___
pChar3在哪里?____ *pChar3在哪里?____
ptr1在哪里?____ *ptr1在哪里?____填空題:
sizeof(num1) = ____; sizeof(char2) = ____;
strlen(char2) = ____; sizeof(pChar3) = ____;
strlen(pChar3) = ____;sizeof(ptr1) = ____;
??答案
從左至右,從上至下給出:
CCCAA AAADAB 40 5 4 4/8 4 4/8
??C語言動態(tài)內(nèi)存管理
C語言動態(tài)內(nèi)存管理在我之前的文章中已經(jīng)詳細介紹過,鏈接如下:
- C語言動態(tài)內(nèi)存管理與柔性數(shù)組
??C++動態(tài)內(nèi)存管理
C++在C語言的基礎(chǔ)上引進了新的動態(tài)內(nèi)存管理的方式——通過new
和delete
操作符進行動態(tài)內(nèi)存管理。
??對于內(nèi)置類型
對于內(nèi)置類型。new/delete
與malloc/free
幾乎是一樣的。
- 動態(tài)申請一個
int
類型的空間;
//int* p1 = (int*)malloc(sizeof(int)); //C
int* p1 = new int; //C++
- 動態(tài)申請一個
int
類型的空間并初始化為10
;
int* p2 = new int(10);
cout << *p2 << endl;
- 動態(tài)申請
10
個int
類型的空間;
int* p3 = new int[10];
- 釋放空間
//free(p1) //C
//釋放單個元素的空間
delete p1;
delete p2;
//釋放多個元素的空間
delete[] p3;
??對于自定義類型
對于自定義類型,new/delete
與malloc/free
的最大區(qū)別是 new/delete
對于自定義類型除了申請/釋放
空間還會調(diào)用對應(yīng)的構(gòu)造函數(shù)
與析構(gòu)函數(shù)
。
class A
{
public:
A()
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
- malloc與free
A* p4 = (A*)malloc(sizeof(A));
free(p4);
- new與delete
A* p5 = new A;
delete p5;
??operator new與operator delete函數(shù)
new
和delete
是用戶進行動態(tài)內(nèi)存申請和釋放的操作符
,operator new
和operator delete
是系統(tǒng)提供的全局函數(shù)
,new
在底層調(diào)用operator new
全局函數(shù)來申請空間,delete
在底層通過operator delete
全局函數(shù)來釋放空間。
??那么operator new與operator delete又是什么呢?
-
operator new
其實是對malloc
進行封裝
后的產(chǎn)物;
之前我們使用malloc
函數(shù)申請空間時,有非常重要的一點就是要對malloc
申請的結(jié)果做檢查。如果malloc申請失敗會返回空指針
。
??示例1
int* a = (int*)malloc(sizeof(int));
if (a == NULL)
{
perror("malloc fail");
exit(-1);
}
每次都要對返回值進行檢查,未免感到有點麻煩。在C++中,系統(tǒng)通過對malloc
申請與檢查的工作進行封裝推出了operator new
函數(shù)。當operator new
申請空間失敗時,會通過拋異常
(之后會將)的方式告訴用戶。
??示例2
// 失敗了拋異常
int* p1 = (int*)operator new(sizeof(int*));
-
operator delete
作用與free
相同;
??示例3
int* p1 = (int*)operator new(sizeof(int*));
operator delete(p1);
??new與delete的實現(xiàn)原理
??對于內(nèi)置類型
如果申請的是內(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)造;
??delete的原理
- 在申請的空間上執(zhí)行
析構(gòu)函數(shù)
,完成對象中資源的清理工作; - 調(diào)用
operator delete
函數(shù)釋放對象
的空間;
??new T[N]的原理
- 調(diào)用
operator new[]
函數(shù),在operator new[]
中實際調(diào)用operator new
函數(shù)完成N
個對象空間的申請; - 在申請的空間上執(zhí)行
N次構(gòu)造函數(shù)
;
??delete[]的原理
3. 在釋放的對象空間上執(zhí)行N次析構(gòu)函數(shù)
,完成N個對象
中資源的清理;
4. 調(diào)用operator delete[]
釋放空間,實際在operator delete[]
中調(diào)用operator delete
來釋放空間;
??對于調(diào)用析構(gòu)函數(shù)的理解
有的小伙伴可能并不清楚為什么要執(zhí)行析構(gòu)函數(shù)
再執(zhí)行operator delete
函數(shù),請思考一下,二者的操作對象是同一個空間嗎?
答案:
-
析構(gòu)函數(shù)
是對象
調(diào)用的,目的是清理對象內(nèi)部申請的資源
(例如:動態(tài)開辟的數(shù)組、自定義類型的變量等); -
operator delete
是用來釋放存儲對象所在的空間
。
通俗的理解就是:我申請了一塊空間A,并將A的地址交給指針變量P
保存,A里面存儲了一個對象。但是對象內(nèi)部又申請了一塊空間B,B里面存儲了一些其它的數(shù)據(jù)。當我們delete p
時,不能直接釋放A,因為A里面存儲的對象又申請了一塊空間B,我們得首先釋放B,不然B就無法釋放了。釋放B需要對象來調(diào)用它的析構(gòu)函數(shù)
,B成功的釋放了,接下來釋放A,調(diào)用operator delete
來釋放A。
??示例
定義一個棧
類:
class stack
{
public:
stack()
:_size(0),
_capacity(0)
{
_a = new int[10];
cout << "stack()" << endl;
}
~stack()
{
cout << "~stack()" << endl;
}
private:
int* _a;
int _size;
int _capacity;
};
new一個對象并釋放:
void Test()
{
stack* ps = new stack;
delete ps;
}
??定位new表達式
定位new表達式是在已分配的原始內(nèi)存空間中調(diào)用構(gòu)造函數(shù)初始化一個對象
。
??使用場景
定位new表達式在實際中一般是配合內(nèi)存池
使用。因為內(nèi)存池分配出的內(nèi)存沒有初始化,所以如果是自定義類型的對象,需要使用new
的定義表達式進行顯示調(diào)構(gòu)造函數(shù)
進行初始化。
??示例
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A()" << this << endl;
}
~A()
{
cout << "~A()" << this << endl;
}
private:
int _a;
};
void Test5()
{
A* p1 = (A*)malloc(sizeof(A));// p1現(xiàn)在指向的只不過是與A對象相同大小的一段空間,還不能算是一個對象,因為構(gòu)造函數(shù)沒有執(zhí)行
new(p1)A; //注意:如果A的構(gòu)造函數(shù)有參數(shù)時需要傳參new(p1)(參數(shù)列表)
p1->~A();
free(p1);
A* p2 = (A*)operator new(sizeof(A));
new(p2)A(10);
p2->~A();
operator delete(p2);
}
??總結(jié)
??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ù)
完成空間中資源的清理;
文章來源:http://www.zghlxwxcb.cn/news/detail-416691.html
點擊下方個人名片,可添加博主的個人QQ,交流會更方便哦~
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓文章來源地址http://www.zghlxwxcb.cn/news/detail-416691.html
到了這里,關(guān)于C++——內(nèi)存分配與動態(tài)內(nèi)存管理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!