亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C++的動(dòng)態(tài)內(nèi)存管理你真的了解嗎

 更新時(shí)間:2022年03月10日 10:59:51   作者:寄一片海給你  
這篇文章主要為大家詳細(xì)介紹了C++的動(dòng)態(tài)內(nèi)存管理,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助

前言

想必大家對(duì)c語(yǔ)言的動(dòng)態(tài)內(nèi)存分配并不陌生,忘了的小伙伴也可以看看我的這篇文章C語(yǔ)言動(dòng)態(tài)內(nèi)存分配

c語(yǔ)言的動(dòng)態(tài)內(nèi)存分配由于有些地方用起來(lái)比較麻煩同時(shí)檢查錯(cuò)誤的機(jī)制不適合c++,因此c++引入new/delete操作符進(jìn)行內(nèi)存管理,下面我們來(lái)深入探討c++為什么要引入new/delete

在這里插入圖片描述

用法上

對(duì)內(nèi)置類型

new/delete同樣是在堆區(qū)申請(qǐng)釋放空間
new和delete申請(qǐng)釋放單個(gè)元素的空間,new[]和delete[]申請(qǐng)釋放一塊連續(xù)的空間,new與delete匹配,new[]與delete[]匹配。new后面直接跟類型,不需要強(qiáng)制類型轉(zhuǎn)換。簡(jiǎn)單來(lái)說(shuō)new/delete用于單個(gè)對(duì)象,new[]/delete[]用于多個(gè)對(duì)象。
對(duì)內(nèi)置類型沒有什么區(qū)別,new操作符和malloc函數(shù)一樣,不會(huì)對(duì)空間初始化,唯一的不同在后面的底層原理會(huì)介紹

#include <iostream>
using namespace std;
int main()
{
	int* p1 = (int*)malloc(sizeof(int));
	int* p2 = (int*)malloc(sizeof(int) * 10);
	//new后面跟動(dòng)態(tài)申請(qǐng)的類型
	int* p3 = new int;//動(dòng)態(tài)申請(qǐng)一個(gè)int型空間
	int* pp3 = new int(1);//動(dòng)態(tài)申請(qǐng)一個(gè)int型空間并初始化為1
	//動(dòng)態(tài)申請(qǐng)10個(gè)int型空間并初始化
	int* p4 = new int[10]{ 1,2,3 };
	free(p1);
	free(p2);
	delete p3;
	delete pp3;
	delete[] p4;
}

對(duì)自定義類型

對(duì)自定義類型那就有很大的區(qū)別了,new一個(gè)自定義對(duì)象,在申請(qǐng)空間后還會(huì)調(diào)用構(gòu)造函數(shù)初始化,delete對(duì)象會(huì)先調(diào)用析構(gòu)函數(shù)清理對(duì)象中的資源,然后釋放空間

#include <iostream>
using namespace std;
class Test
{
public:
	Test(int data = 10)
		: _data(data)
	{
		cout << "Test():" << endl;
	}
	~Test()
	{
		cout << "~Test():" << endl;
	}
private:
	int _data;
};
int main()
{	
	// 申請(qǐng)單個(gè)Test類型的空間
	Test* p1 = (Test*)malloc(sizeof(Test));
	free(p1);
	// 申請(qǐng)10個(gè)Test類型的空間
	Test* p2 = (Test*)malloc(sizeof(Test) * 10);
	free(p2);
	// 申請(qǐng)單個(gè)Test類型的對(duì)象
	Test* p3 = new Test;	//申請(qǐng)空間并調(diào)用構(gòu)造函數(shù)初始化
	delete p3;
	// 申請(qǐng)10個(gè)Test類型的對(duì)象
	Test* p4 = new Test[10];//申請(qǐng)空間并調(diào)用構(gòu)造函數(shù)初始化
	delete[] p4;
	return 0;
}

在這里插入圖片描述

簡(jiǎn)單解釋一下:對(duì)于delete操作符先調(diào)用析構(gòu)函數(shù)清理對(duì)象的資源,再釋放空間,這里為了演示我只寫了棧的構(gòu)造函數(shù)和析構(gòu)函數(shù)

#include <iostream>
using namespace std;
class Stack
{
public:
	Stack(int capacity = 4)
		:_capacity(capacity)
	{
		int* _p = new int[capacity];
		_top = 0;
		_capacity = capacity;
		cout << "Stack()" << endl;
	}
	~Stack()
	{
		delete[] _p;
		_top = _capacity = 0;
		cout << "~Stack()" << endl;
	}
private:
	int* _p;
	int _top;
	int _capacity;
};
int main()
{	
	Stack* s1=new Stack;
	delete s1;
	return 0;
}

delete先調(diào)用構(gòu)造函數(shù)清理對(duì)象維護(hù)的堆區(qū)B的資源,然后再釋放堆區(qū)A的空間

在這里插入圖片描述

new/delete底層原理

通過匯編代碼我們發(fā)現(xiàn),在底層:new會(huì)調(diào)用operator new函數(shù)和Stack構(gòu)造函數(shù),而delete也會(huì)調(diào)用析構(gòu)函數(shù)和operator delete函數(shù),那么operator new和operator delete是什么函數(shù)呢?

在這里插入圖片描述

在這里插入圖片描述

operator new和operator delete是系統(tǒng)提供的兩個(gè)全局函數(shù),通過operator new申請(qǐng)空間,operator delete釋放空間,實(shí)際上operator new也是調(diào)用malloc申請(qǐng)空間的,operator delete也是調(diào)用free釋放空間的,那么為什么不直接用malloc和free呢?

c語(yǔ)言中malloc申請(qǐng)空間失敗會(huì)返回NULL空指針,那我們檢查錯(cuò)誤的方式就是通過errno錯(cuò)誤碼,而面向?qū)ο蟮恼Z(yǔ)言,處理錯(cuò)誤的方式一般是拋異常,C++中也要求拋異常——try catch,

這里我簡(jiǎn)單提一下拋異常,當(dāng)我們new失敗后,如果不捕獲異常就會(huì)拋異常

int main()
{	//由于申請(qǐng)空間過大new失敗
	char* p = new char[1024u * 1024u * 1024u * 2 - 1];  
	printf("execute\n");
	return 0;
}

在這里插入圖片描述

那我們捕獲異常,new失敗后編譯器就會(huì)提示申請(qǐng)失敗原因

#include <iostream>
using namespace std;
int main()
{	
	try    //檢測(cè)異常
	{
		char* p = new char[1024u * 1024u * 1024u * 2 - 1];  //new失敗,直接跳到catch,不執(zhí)行下面語(yǔ)句
		printf("execute\n");
	}
	catch (const exception& e)	//捕獲并處理異常
	{
		cout << e.what() << endl;
	}
	return 0;
}

在這里插入圖片描述

所以這就是為什么不直接使用malloc的原因,因?yàn)閙alloc申請(qǐng)空間失敗不會(huì)拋異常

而operator delete也是在free函數(shù)的基礎(chǔ)上增加了一些檢查機(jī)制

operator new/operator delete用法與malloc/free函數(shù)類似,在定位new中會(huì)介紹

C++也有operator new[]和operator delete[],僅僅是為了和new[]和delete[]配對(duì)

在這里插入圖片描述

在這里插入圖片描述

重載類的專屬operator new和 operator delete

operator new和operator delete是可以自己定義的,一般用于在STL中的內(nèi)存池中申請(qǐng)空間,沒學(xué)過內(nèi)存池的小伙伴可以簡(jiǎn)單了解一下。

有個(gè)住在山上的少年,他每次洗澡,做飯,洗衣服需要用水的時(shí)候都需要跑到山下的河邊接水,然后再回到山上,每天重復(fù)多次。那么可不可以在家附近建一個(gè)小水池,一次性將水池裝滿水,每次用水的時(shí)候就直接從水池打水就行了,不需要每次都跑到山下。這也就是內(nèi)存池出現(xiàn)的原理,這里山下的小河可以看作操作系統(tǒng)的堆區(qū),而每次打水可以看作是從堆區(qū)申請(qǐng)空間,水池可以看作內(nèi)存池,這樣我們直接從堆區(qū)申請(qǐng)一大塊空間放在內(nèi)存池,每次需要申請(qǐng)時(shí),直接到內(nèi)存池申請(qǐng)空間就行了,不需要每次都從堆區(qū)申請(qǐng)空間。

在這里插入圖片描述

這里以雙鏈表為例,如果沒有內(nèi)存池,每次創(chuàng)建新的節(jié)點(diǎn)時(shí)都需要從堆區(qū)申請(qǐng)空間,頻繁的找操作系統(tǒng)申請(qǐng)會(huì)大大降低效率

我們直接在類里面定義operator new和operator delete,與系統(tǒng)提供的兩個(gè)全局的函數(shù)構(gòu)成重載,到時(shí)候new和delete就會(huì)調(diào)用我們定義的operator new和delete而不會(huì)調(diào)用那兩個(gè)全局的函數(shù)

struct ListNode
{
	ListNode* _next;
	ListNode* _prev;
	int _data;
	ListNode(int val = 0)
		:_next(nullptr)
		,_prev(nullptr)
		,_data(val)
	{}
	void* operator new(size_t n)
	{
		void* p = nullptr;
		p = allocator<ListNode>().allocate(1);	//allocator是STL中的內(nèi)存池
		cout << "memory pool allocate" << endl;
		return p;
	}
	void operator delete(void* p)
	{
		allocator<ListNode>().deallocate((ListNode*)p, 1);
		cout << "memory pool deallocate" << endl;
	}
};
class List
{
public:
	List()
	{
		_head = new ListNode;
		_head->_next = _head;
		_head->_prev = _head;
	}
	void ListPush(int val)
	{
		ListNode* newnode = new ListNode;
		newnode->_data = val;
		ListNode* tail = _head->_prev;
		tail->_next = newnode;
		newnode->_prev = tail;
		newnode->_next = _head;
		_head->_prev = newnode;
	}
	~List()
	{
		ListNode* cur = _head->_next;
		while (cur != _head)
		{
			ListNode* next = cur->_next;
			delete cur;
			cur = next;
		}
		delete _head;
		_head = nullptr;
	}
private:
	ListNode* _head;
};
int main()
{
	List l;
	l.ListPush(1);
	l.ListPush(2);
	l.ListPush(3);
	l.ListPush(4);
	return 0;
}

在這里插入圖片描述

定位new

定位new表達(dá)式在實(shí)際中一般是配合內(nèi)存池使用。因?yàn)閮?nèi)存池分配出的內(nèi)存沒有初始化

#include <iostream>

using namespace std;

class Test
{
public:
	Test(int data = 10)
		: _data(data)
	{
		cout << "Test():" << endl;
	}
	~Test()
	{
		cout << "~Test():" << endl;
	}

private:
	int _data;
};
//構(gòu)造函數(shù)是不支持直接手動(dòng)調(diào)用的,所以引入定位new,對(duì)已經(jīng)存在的對(duì)象調(diào)用構(gòu)造函數(shù)初始化,常用于內(nèi)存池中的對(duì)象
int main()
{	
//調(diào)用operator new申請(qǐng)空間,operator new和malloc函數(shù)用法類似
	Test* p = (Test*)operator new(sizeof(Test));
	//調(diào)用構(gòu)造函數(shù)初始化
	new(p)Test(1);	//定位new,new后面接要初始化對(duì)象的地址+類型,(1)表示初始化為1
	//先調(diào)用析構(gòu)函數(shù),析構(gòu)函數(shù)可以直接調(diào)用
	p->~Test();
	//在調(diào)用operator delete釋放空間
	operator delete (p);
	

	return 0;
}

new/delete與malloc/free區(qū)別總結(jié)

共同點(diǎn):都從堆上申請(qǐng)空間,并需要手動(dòng)釋放空間,因?yàn)閚ew/delete可以說(shuō)是對(duì)malloc/free的升級(jí),申請(qǐng)/釋放空間還是會(huì)調(diào)用malloc/free

不同點(diǎn):

1、new/delete是操作符,而malloc/free是函數(shù)

2、首先是用法上的不同,malloc返回類型是void*,需要強(qiáng)轉(zhuǎn),而new不需要強(qiáng)轉(zhuǎn)了,new后面直接跟類型,new也不需要計(jì)算空間大小,申請(qǐng)N個(gè)加[N],new/delete配對(duì),mew[]/delete[]配對(duì)

3、malloc失敗返回NULL指針,new失敗如果不catch捕獲就會(huì)拋異常

4、對(duì)內(nèi)置類型,new和malloc一樣也不會(huì)初始化,但對(duì)自定義類型,new會(huì)先申請(qǐng)空間再調(diào)用構(gòu)造函數(shù)初始化,delete會(huì)先調(diào)用析構(gòu)函數(shù)清理對(duì)象中的資源再釋放空間

內(nèi)存泄漏

動(dòng)態(tài)申請(qǐng)的內(nèi)存空間不使用了,又沒有主動(dòng)釋放,就存在內(nèi)存泄漏,當(dāng)然不是所有的內(nèi)存泄漏都有危害。

1.出現(xiàn)內(nèi)存泄漏的進(jìn)程正常結(jié)束,會(huì)將內(nèi)存還給操作系統(tǒng),不會(huì)有什么危害

2.但出現(xiàn)內(nèi)存泄漏的進(jìn)程非正常結(jié)束,比如僵尸進(jìn)程,或是長(zhǎng)期運(yùn)行的程序,比如服務(wù)器程序,出現(xiàn)內(nèi)存泄漏,那么危害就很大了,系統(tǒng)會(huì)越來(lái)越慢,甚至是卡死宕機(jī)

所以我們?cè)趎ew申請(qǐng)空間時(shí),一定要記得delete釋放空間

了解到這里我們就明白了c++為什么要引入new/delete了

1.對(duì)自定義類型,對(duì)象動(dòng)態(tài)申請(qǐng)空間時(shí),new/delete會(huì)自動(dòng)調(diào)用構(gòu)造函數(shù)/析構(gòu)函數(shù)

2.new失敗后拋異常,符合面向?qū)ο笳Z(yǔ)言對(duì)出錯(cuò)的處理機(jī)制

3.我們可以定義類專屬的operator new和operator delete,這樣可以從內(nèi)存池申請(qǐng)空間,避免頻繁從堆區(qū)申請(qǐng)空間

總結(jié)

本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容! 

相關(guān)文章

最新評(píng)論