C/C++的記憶體管理
C/C++記憶體分佈
- 程式碼段:可執行的程式碼、只讀常量
- 全域性資料區:儲存全域性資料和靜態資料(static修飾)
- 堆區:使用malloc、calloc、realloc等申請的空間
- 棧區:非靜態區域性變數、函式引數、返回值等等
C/C++語言中動態記憶體管理方式
C中使用malloc/calloc/realloc申請堆上的空間,和棧申請空間不同的是,堆上的空間需要程式設計人員自己呼叫函式free()釋放。
malloc和free
malloc用來動態記憶體的開闢:
void* malloc(size_t size)
- 這個函式向記憶體申請一塊連續可用的空間,並返回指向這塊空間的指標;
- 如果開闢成功,則返回一個指向開闢好空間的指標;
- 如果開闢失敗,則返回一個NULL指標,因此malloc的返回值一定要做檢查;
- 返回值的型別是 void* ,所以malloc函式並不知道開闢空間的型別,具體在使用的時候使用者自己來決定。
free函式用來釋放動態開闢的記憶體:
void free(void *ptr)
- 如果引數 ptr 指向的空間不是動態開闢的,那free函式的行為是未定義的;
- 如果引數 ptr 是NULL指標,則函式什麼事都不做。
calloc
void* calloc(size_t num,size_t size)
- 函式的功能是為 num 個大小為 size 的元素開闢一塊空間,並且把空間的每個位元組初始化為0;
- 與函式 malloc 的區別只在於 calloc 會在返回地址之前把申請的空間的每個位元組初始化為全0。
realloc
void *realloc(void *ptr,size_t szie)
- ptr 是要調整的記憶體地址,size 調整之後新大小;
- 返回值為調整之後的記憶體起始位置;
- 這個函式調整原記憶體空間大小的基礎上,還會將原來記憶體中的資料移動到新的空間。
malloc、calloc、realloc三者的區別
calloc在動態分配完記憶體後,自動初始化該記憶體空間為零,而malloc不初始化,裡邊資料是隨機的垃圾資料;realloc也不會對所申請的記憶體空間進行初始化,但它讓記憶體動態空間管理更加靈活。三者都申請空間成功返回空間的首地址,否則返回NULL。
在C++ 中單個元素的空間使用new來申請,delete來釋放;多個元素的空間使用new[ ]來申請,delete[ ]來釋放;不能交叉使用。
這裡需要注意,new和delete是使用者進行動態記憶體申請和釋放的操作符,operator new 和operator delete是系統提供的全域性函式,new在底層呼叫operatornew全域性函式來申請空間,delete在底層通過operator delete全域性函式來釋放空間。
new的原理
1. 呼叫operator new函式申請空間
2. 在申請的空間上執行建構函式,完成物件的構造
delete的原理
1. 在空間上執行解構函式,完成物件中資源的清理工作
2. 呼叫operator delete函式釋放物件的空間
new T[N]的原理
1. 呼叫operator new[ ]函式,在operator new[ ]中實際呼叫operator new函式完成N個物件空間的申
請
2. 在申請的空間上執行N次建構函式
delete[ ]的原理
1. 在釋放的物件空間上執行N次解構函式,完成N個物件中資源的清理
2. 呼叫operator delete[ ]釋放空間,實際在operator delete[]中呼叫operator delete來釋放空間
定位new表示式(placement-new)
定位new表示式是在已分配的原始記憶體空間中呼叫建構函式初始化一個物件。
使用格式:
- new (place_address) type或者new (place_address) type(initializer-list)
- place_address必須是一個指標,initializer-list是型別的初始化列表
使用場景
定位new表示式在實際中一般是配合記憶體池使用。因為記憶體池分配出的記憶體沒有初始化,所以如果是自定義
型別的物件,需要使用new的定義表示式進行顯示調建構函式進行初始化。
malloc/free和new/delete的共同點是:都是從堆上申請空間,並且需要使用者手動釋放。
malloc/free和new/delete的區別
- malloc和free是函式,new和delete是操作符
- malloc申請的空間不會初始化,new可以初始化
- malloc申請空間時,需要手動計算空間大小並傳遞,new只需在其後跟上空間的型別即可
- malloc的返回值為void*, 在使用時必須強轉,new不需要,因為new後跟的是空間的型別
- malloc申請空間失敗時,返回的是NULL,因此使用時必須判空,new不需要,但是new需要捕獲異常
- 申請自定義型別物件時,malloc/free只會開闢空間,不會呼叫建構函式與解構函式,而new在申請空間後會呼叫建構函式完成物件的初始化,delete在釋放空間前會呼叫解構函式完成空間中資源的清理
- new/delete比malloc和free的效率稍微低點,因為new/delete的底層封裝了malloc/free
程式碼說明
#include<iostream>
#include<stdlib.h>
using namespace std;
//記憶體空間管理
class Test
{
public:
Test(int data=1)
: _data(data)
{
cout << "Test():" << this << endl;
}
~Test()
{
cout << "~Test():" << this << endl;
}
private:
int _data;
};
void Test1()
{
// 申請單個Test型別的空間
Test* p1 = new Test;
delete p1;
//申請多個Test型別的空間
Test *p2 = new Test[10];
//delete[]所釋放的位元組數為10*sizeof(test)+1,表示申請空間的大小+用於存放申請元素個數所佔用的一個位元組
delete[]p2;
}
void Test2()
{
//p現在指向的只不過是與Test物件相同大小的一段空間,還不能算是一個物件,因為建構函式沒有執行
Test *p = (Test*)malloc(sizeof(Test));
//定位new表示式在實際中一般是配合記憶體池使用。因為記憶體池分配出的記憶體沒有初始化,所以如果是自定義
//型別的物件,需要使用new的定義表示式進行顯示調建構函式進行初始化。
new(p) Test();
}
/*
*讓一個類只能在堆上建立物件;
*建立物件有兩個途徑:1.呼叫建構函式 2.呼叫拷貝建構函式
*封鎖這兩個途徑
*/
class Test3 {
public:
//提供在堆上建立物件的函式
static Test3 *HeapCreate()
{
return new Test3;
}
private:
//聲名建構函式和拷貝建構函式為私有(C++98)
Test3() {};
Test3(const Test3&) {};
//C++11
/*Test3(const Test3&)=delete;*/
};
/*
*讓一個類只能在棧上建立物件;
*在堆上建立物件的途徑:1.new 2.new[] 但new[]是對new多次使用,所以只需封鎖new
*/
class Test4{
public:
Test4()
{}
private:
void* operator new(size_t size){}//一般封鎖了new,同時也封鎖delete
void operator delete(void *ptr){}
};
int main()
{
//Test1();
//Test2();
//Test3 *p = Test3::HeapCreate();
//Test4 *p=new Test4;
system("pause");
return 0;
}