最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入理解C++中的new和delete并實現(xiàn)對象池

 更新時間:2021年09月14日 15:27:52   作者:-林澤宇  
這篇文章主要介紹了C++中的new和delete并實現(xiàn)對象池,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

深入理解new和delete

new和delete稱作運算符

在這里插入圖片描述

我們轉(zhuǎn)反匯編看看

在這里插入圖片描述

這2個運算符本質(zhì)也是相應(yīng)的運算符的重載的調(diào)用

malloc和new的區(qū)別?

1.malloc按字節(jié)開辟內(nèi)存的;new開辟內(nèi)存時需要指定類型 new int[10]
所以malloc開辟內(nèi)存返回的都是void*
而new相當(dāng)于運算符的重載函數(shù) operator new ->返回值自動轉(zhuǎn)成指定的類指針 int*
2.malloc只負(fù)責(zé)開辟空間,new不僅僅有malloc的功能,可以進行數(shù)據(jù)的初始化

new int(20);//初始化20  
new int[20]();//開辟數(shù)組是不支持初始化值的,但是支持寫個空括號,表示給每個元素初始化為0 ,相當(dāng)于每個元素調(diào)用int()成為0

3.malloc開辟內(nèi)存失敗返回nullptr指針;new拋出的是bad_alloc類型的異常
(也就是說,new運算符開辟內(nèi)存失敗,要把它的代碼擴在try catch里面,是不能通過返回值和空指針比較的。)

try//可能發(fā)生錯誤的代碼放在try里面 
	{
		int *p = new int;
		delete []p;

		int *q = new int[10];
		delete q;
	}
	catch (const bad_alloc &err)//捕獲相應(yīng)類型的異常 
	{
		cerr << err.what() << endl;//打印錯誤 
	}

在這里插入圖片描述

free和delete的區(qū)別?

delete p: 調(diào)用析構(gòu)函數(shù),然后再free( p),相當(dāng)于包含了free
如果delete的是普通的指針,那么delete (int*)p和free( p)是沒有區(qū)別的
因為對于整型指針來說,沒有析構(gòu)函數(shù),只剩下內(nèi)存的釋放

new -> 對operator new重載函數(shù)的調(diào)用 
delete -> 對operator delete重載函數(shù)的調(diào)用 

把new和delete的重載函數(shù)定義在全局的地方,這樣我們整個項目工程中只有涉及到new和delete的地方都會調(diào)用到我們?nèi)种貙懙膎ew,delete的重載函數(shù)。

//先調(diào)用operator new開辟內(nèi)存空間、然后調(diào)用對象的構(gòu)造函數(shù)(初始化)
void* operator new(size_t size)
{
	void *p = malloc(size);
	if (p == nullptr)
		throw bad_alloc();
	cout << "operator new addr:" << p << endl;
	return p;
}
//delete p; 先調(diào)用p指向?qū)ο蟮奈鰳?gòu)函數(shù)、再調(diào)用operator delete釋放內(nèi)存空間
void operator delete(void *ptr)
{
	cout << "operator delete addr:" << ptr << endl;
	free(ptr);
}

在這里插入圖片描述
在這里插入圖片描述

new和delete從內(nèi)存管理的角度上來說和malloc和free沒有什么區(qū)別
除非就是內(nèi)存開辟失敗,返回不一樣

void* operator new[](size_t size)
{
	void *p = malloc(size);
	if (p == nullptr)
		throw bad_alloc();
	cout << "operator new[] addr:" << p << endl;
	return p;
}
void operator delete[](void *ptr)
{
	cout << "operator delete[] addr:" << ptr << endl;
	free(ptr);
}

在這里插入圖片描述
在這里插入圖片描述

C++中,如何設(shè)計一個程序檢測內(nèi)存泄漏問題?
內(nèi)存泄漏就是new操作沒有對應(yīng)的delete,我們可以在全局重寫上面這些函數(shù),在new操作里面用映射表記錄都有哪些內(nèi)存被開辟過,delete的時候把相應(yīng)的內(nèi)存資源刪除掉,new和delete都有對應(yīng)關(guān)系
如果整個系統(tǒng)運行完了,我們發(fā)現(xiàn),映射表記錄的一些內(nèi)存還沒有被釋放,就存在內(nèi)存泄漏了! 我們用new和delete接管整個應(yīng)用的所有內(nèi)存管理 ,對內(nèi)存的開辟和釋放都記錄
也可以通過編譯器既定的宏和API接口,把函數(shù)調(diào)用堆棧打印出來,到底在哪個源代碼的哪一頁的哪一行做了new操作沒有delete

new和delete能混用嗎?

C++為什么區(qū)分單個元素和數(shù)組的內(nèi)存分配和釋放呢?
下面這樣操作是否可以???

在這里插入圖片描述

其實現(xiàn)在對于整型來說,沒有所謂的構(gòu)造函數(shù)和析構(gòu)函數(shù)可言,所以這樣的代碼就只剩下malloc和free的功能,所以底層調(diào)用的就是malloc和free

在這里插入圖片描述

所以,它們現(xiàn)在混用是沒有問題的!?。?/p>

那什么時候我們才需要考慮這些問題呢?

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

在這里插入圖片描述

在這里插入圖片描述

在這里面,我們能不能混用呢?

在這里插入圖片描述
在這里插入圖片描述

出現(xiàn)錯誤了。
此時new和delete不能進行混用了!

在這里插入圖片描述

在這里插入圖片描述
在這里插入圖片描述

在這里,new和delete可以混用嗎?

在這里插入圖片描述
在這里插入圖片描述

運行出錯了。

我們最好是這樣配對使用:

new delete
new[] delete[]

對于普通的編譯器內(nèi)置類型
new/delete[]
new[]/delete
這樣混用是可以的!
因為只涉及內(nèi)存的開辟和釋放,底層調(diào)用的就是malloc和free

但是,如果是對象,就不能混用了。

在這里插入圖片描述

一個Test對象是4個字節(jié)。
每一個Test對象有1個整型的成員變量。

在這里插入圖片描述

new的時候,分配了5個Test對象,但是不只是開辟了20個字節(jié)哦!
delete[]p2的時候先調(diào)用Test對象的析構(gòu)函數(shù),析構(gòu)函數(shù)有this指針,this指針區(qū)分析構(gòu)的對象,this指針把正確的對象的地址傳到析構(gòu)函數(shù)?,F(xiàn)在加了[]表示有好幾個對象,有一個數(shù)組,里面的每個對象都要析構(gòu),但是它是怎么知道是有5個對象呢???
所以,實際上,new Test[5]是開辟了如圖式的內(nèi)存:
多開辟了4個字節(jié),存儲對象的個數(shù)。
用戶在寫new Test[5]時,這個5是要被記錄下來的。
而且,new操作完了之后,給以后返回的p2指針指向的地址是0x104這個地址!即數(shù)組首元素的地址。并不是真真正正底層開辟的0x100這個地址,因為那個是不需要讓用戶知道的,用戶只需要知道這個指針指向的是第一個元素對象的地址。

在這里插入圖片描述

當(dāng)我們?nèi)elete[]p2的時候,它一看這個[]就知道釋放的是一個對象數(shù)組,那么就要從p2(0x104)上移4個字節(jié),去取對象的個數(shù),知道是5個對象了(一個對象是4字節(jié)),然后把ox104下的內(nèi)存平均分成5份,每一份內(nèi)存的起始地址就是對象的起始地址,然后傳給對象的析構(gòu)函數(shù),就可以進行對象的析構(gòu)了。然后進行內(nèi)存的釋放,operator delete(p2-4),從0x100開始釋放?。?!

在這里插入圖片描述
在這里插入圖片描述

這個代碼錯誤在:實際上開辟的內(nèi)存空間大小是20+4=24字節(jié),開辟內(nèi)存是從0028開辟的,因為它有析構(gòu)函數(shù),所以在底層給數(shù)組開辟內(nèi)存時多開辟了4個字節(jié)來存儲開辟的對象的個數(shù),但是用戶返回的是02c,比028剛好多了4個字節(jié),也就是給用戶返回的是真真正正對象的起始地址。
delete p2;它就認(rèn)為p2只是指向1個對象,因為沒有使用delete[],所以它就只是把Test[0]這個對象析構(gòu)了而已,然后直接free(p2),從第一個對象的地址(02c)開始free,而底層內(nèi)存是從028開始開辟的。

我們換成delete[]p2,來運行看看

在這里插入圖片描述

從指針-4開始free釋放內(nèi)存的操作

在這里插入圖片描述

這個代碼的出錯在:只是new出來1個對象,在0x104開辟的,p1也是指向了0x104,但是在delete[]的時候,認(rèn)為是指向的是對象數(shù)組,因為還有析構(gòu)函數(shù),于是它就從0x104上移4個字節(jié)去取開辟對象的個數(shù),

在這里插入圖片描述

這就出現(xiàn)了問題了。
關(guān)鍵是它free的時候,執(zhí)行的是free(0x104-4)
但是new的時候并不是從0x100開始開辟內(nèi)存的。

自定義的類類型,有析構(gòu)函數(shù),為了調(diào)用正確的析構(gòu)函數(shù),那么開辟對象數(shù)組的時候,會多開辟4個字節(jié),記錄對象的個數(shù)

對象池代碼應(yīng)用

對象池的實現(xiàn)是靜態(tài)鏈表,在堆上開辟的。

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

#include <iostream>
using namespace std;

template<typename T>
class Queue
{
public:
	Queue()//構(gòu)造函數(shù) 0構(gòu)造(默認(rèn)構(gòu)造) 
	{
		_front = _rear = new QueueItem();
	}
	~Queue()//析構(gòu)函數(shù) 
	{
		QueueItem *cur = _front;//指向頭結(jié)點 
		while (cur != nullptr)
		{
			_front = _front->_next;
			delete cur;
			cur = _front;
		}
	}
	void push(const T &val)//入隊操作
	{
		QueueItem *item = new QueueItem(val);//malloc
		_rear->_next = item;
		_rear = item;
	}
	void pop()//出隊操作 隊頭出 頭刪法 
	{
		if (empty())
			return;
		QueueItem *first = _front->_next;
		_front->_next = first->_next;
		if (_front->_next == nullptr)//隊列原本只有1個有效元素節(jié)點 
		{
			_rear = _front;
		}
		delete first;//free
	}
	T front()const//獲取首元素的值 
	{
		return _front->_next->_data;
	}
	bool empty()const { return _front == _rear; }//判空  鏈?zhǔn)疥犃?
private:
	//產(chǎn)生一個QueueItem的對象池(10000個QueueItem節(jié)點)
	struct QueueItem//節(jié)點類型,鏈?zhǔn)疥犃?,帶頭節(jié)點的單鏈表 
	{
		QueueItem(T data = T()) :_data(data), _next(nullptr) {}//構(gòu)造函數(shù)
		 
		//給QueueItem提供自定義內(nèi)存管理
		void* operator new(size_t size)
		{
			if (_itemPool == nullptr)//如果對象池滿了,對象池的指針就指向空了,然后現(xiàn)在進入,再開辟一個對象池 
			{
				_itemPool = (QueueItem*)new char[POOL_ITEM_SIZE*sizeof(QueueItem)];//開辟池 
				QueueItem *p = _itemPool;
				for (; p < _itemPool + POOL_ITEM_SIZE - 1; ++p)//連在一個鏈表上 
				{
					p->_next = p + 1;//因為節(jié)點內(nèi)存是連續(xù)開辟的 可以用p+1 
				}
				p->_next = nullptr;
			}

			QueueItem *p = _itemPool;
			_itemPool = _itemPool->_next;
			return p;
		}
		void operator delete(void *ptr)
		{
			QueueItem *p = (QueueItem*)ptr;
			p->_next = _itemPool;
			_itemPool = p;//往頭前放,然后連起來 
		}
		T _data;//數(shù)據(jù)域 
		QueueItem *_next;//指向下一個節(jié)點的指針域 
		static QueueItem *_itemPool;//指向?qū)ο蟪氐钠鹗嫉刂?,因為所有?QueueItem都放在一個對象池里面 
		static const int POOL_ITEM_SIZE = 100000;//開辟的對象池的節(jié)點的個數(shù),靜態(tài)常量可以直接在類體初始化 
	};

	QueueItem *_front;//指向頭節(jié)點
	QueueItem *_rear;//指向隊尾 即鏈表的最后一個元素 
};

template<typename T>//在類外定義靜態(tài)成員變量 
typename Queue<T>::QueueItem *Queue<T>::QueueItem::_itemPool = nullptr;
//typename告訴編譯器后邊的嵌套類作用域下的名字是類型,放心使用吧 

int main()
{
	Queue<int> que;
	for (int i = 0; i < 1000000; ++i)
	{
		que.push(i);//QueueItem(i)
		que.pop();//QueueItem
	}
	cout << que.empty() << endl;

	return 0;
}

可以把指針改為智能指針,出作用域,對象池自動釋放

到此這篇關(guān)于深入理解C++中的new和delete并實現(xiàn)對象池的文章就介紹到這了,更多相關(guān)C++對象池內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++右值引用與移動構(gòu)造函數(shù)基礎(chǔ)與應(yīng)用詳解

    C++右值引用與移動構(gòu)造函數(shù)基礎(chǔ)與應(yīng)用詳解

    左值和右值都是針對表達式,左值是指表達式結(jié)束后依然存在的持久對象,右值是指表達式結(jié)束時就不再存在的臨時對象,下面這篇文章主要給大家介紹了關(guān)于C++11右值引用和移動語義的相關(guān)資料,需要的朋友可以參考下
    2023-02-02
  • C++傳值、傳址、傳引用的區(qū)別和選擇小結(jié)

    C++傳值、傳址、傳引用的區(qū)別和選擇小結(jié)

    參數(shù)傳遞有傳值、傳址和傳引用這三種,本文主要介紹了C++傳值、傳址、傳引用的區(qū)別和選擇小結(jié),具有一定的參考價值,感興趣的可以了解一下
    2024-07-07
  • c語言函數(shù)如何求兩個數(shù)的最大值

    c語言函數(shù)如何求兩個數(shù)的最大值

    這篇文章主要介紹了c語言函數(shù)如何求兩個數(shù)的最大值問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • 簡單聊聊C++中回調(diào)函數(shù)的實現(xiàn)

    簡單聊聊C++中回調(diào)函數(shù)的實現(xiàn)

    回調(diào)函數(shù)就是一個通過函數(shù)指針調(diào)用的函數(shù),如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個函數(shù),當(dāng)這個指針被用來調(diào)用其所指向的函數(shù)時,我們就說這是回調(diào)函數(shù),下面這篇文章主要給大家介紹了關(guān)于C++中回調(diào)函數(shù)實現(xiàn)的相關(guān)資料,需要的朋友可以參考下
    2022-01-01
  • 在Qt中使用OpenGL繪制三角形指南

    在Qt中使用OpenGL繪制三角形指南

    在高性能渲染場景中,CPU資源常被過度消耗,導(dǎo)致界面卡頓,而OpenGL作為業(yè)界標(biāo)準(zhǔn)的圖形API,能通過GPU硬件加速顯著降低CPU負(fù)載,本文將以繪制三角形為例,教你如何通過Qt的QOpenGLWidget和QOpenGLFunctions實現(xiàn)跨平臺GPU渲染,感興趣的朋友一起看看吧
    2025-04-04
  • C++俄羅斯方塊游戲 無需圖形庫的俄羅斯方塊

    C++俄羅斯方塊游戲 無需圖形庫的俄羅斯方塊

    這篇文章主要為大家詳細(xì)介紹了無需圖形庫的C++俄羅斯方塊游戲,重溫經(jīng)典游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-06-06
  • C語言實現(xiàn)五子棋小游戲

    C語言實現(xiàn)五子棋小游戲

    五子棋游戲是一款很經(jīng)典的智力游戲,只有學(xué)過編程語言的人,把五子棋的編程原理弄懂了,就能用自己熟悉的語言實現(xiàn)出來,在這里給大家分享,c語言五子棋源碼,僅供大家參考借鑒。
    2016-03-03
  • c語言的指針數(shù)組詳解

    c語言的指針數(shù)組詳解

    這篇文章主要為大家介紹了c語言的指針數(shù)組,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-12-12
  • C++詳細(xì)實現(xiàn)紅黑樹流程詳解

    C++詳細(xì)實現(xiàn)紅黑樹流程詳解

    今天我要跟大家介紹二叉搜索樹中的另一顆樹——紅黑樹,它主要是通過控制顏色來控制自身的平衡,但它的平衡沒有AVL樹的平衡那么嚴(yán)格
    2022-06-06
  • 淺談帶緩沖I/O 和不帶緩沖I/O的區(qū)別與聯(lián)系

    淺談帶緩沖I/O 和不帶緩沖I/O的區(qū)別與聯(lián)系

    下面小編就為大家?guī)硪黄獪\談帶緩沖I/O 和不帶緩沖I/O的區(qū)別與聯(lián)系。小編覺得挺不錯的現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-01-01

最新評論

徐汇区| 高青县| 宁明县| 慈溪市| 共和县| 平安县| 夏河县| 南岸区| 衡阳县| 靖西县| 伊通| 宣汉县| 大冶市| 乌兰察布市| 利辛县| 泊头市| 绥中县| 天峻县| 临朐县| 财经| 黄龙县| 南召县| 文安县| 驻马店市| 洮南市| 平江县| 建昌县| 老河口市| 巴南区| 弥渡县| 兴和县| 邹平县| 寿阳县| 延寿县| 青冈县| 南城县| 从化市| 从江县| 鲁甸县| 平遥县| 彭阳县|