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

C++ 智能指針的魅力你都了解嗎

 更新時間:2021年06月04日 09:42:19   作者:WhiteShirtI  
智能指針使用和普通指針類似。解引用一個智能指針返回它指向的對象。如果在一個條件判斷中使用智能指針,效果就是檢測它是否為空,本文給大家介紹C++ 智能指針的相關知識,感興趣的朋友一起看看吧

前情提要

我們知道除了靜態(tài)內存和棧內存外,每個程序還有一個內存池,這部分內存被稱為自由空間或者堆。程序用堆來存儲動態(tài)分配的對象即那些在程序運行時分配的對象,當動態(tài)對象不再使用時,我們的代碼必須顯式的銷毀它們。

在C++中,動態(tài)內存的管理是用一對運算符完成的:new和delete,ne:在動態(tài)內存中為對象分配一塊空間并返回一個指向該對象的指針,delete指向一個動態(tài)獨享的指針,銷毀對象,并釋放與之關聯(lián)的內存。

動態(tài)內存管理經常會出現(xiàn)兩種問題:一種是忘記釋放內存,會造成內存泄漏;一種是尚有指針引用內存的情況下就釋放了它,就會產生引用非法內存的指針。

為了更加容易(更加安全)的使用動態(tài)內存,引入了智能指針的概念。智能指針的行為類似常規(guī)指針,重要的區(qū)別是它負責自動釋放所指向的對象

智能指針的原理

RAII:利用對象生命周期來控制程序資源。主要是通過對象的構造函數(shù)來獲得資源的管理權,然后再通過析構函數(shù)來釋放所管理的資源。其原理就是把管理一份資源的責任托管給了一個對象

//RAII
template<class T>
class SmartPtr
{
public:
	//構造函數(shù)獲取資源管理權
	SmartPtr(T* ptr)
		:_ptr(ptr)
	{}
	//通過析構函數(shù)釋放資源
	~SmartPtr()
	{
		if (_ptr)
			delete _ptr;
	}
private:
	T* _ptr;
};
class A
{
private:
	int _a = 10;
};

void test()
{
	//錯誤寫法
	int* ptr = new int[10];
	SmartPtr<int> sp(ptr);
	//立即初始化--申請資源時就立即綁定資源
	SmartPtr<int> sp2(new int);
	SmartPtr<A> sp3(new A);
}

這并不是一個智能指針對象,智能指針應該要滿足以下條件

  • 實現(xiàn)RAII思想
  • 使用方式和指針一樣,例如需要支持*解引用和->操作

應在類中添加以下操作的重載

T* operator->()
	{
		return _ptr;
	}
	T& operator*()
	{
		return *_ptr;
	}

智能指針和普通的指針的區(qū)別之一是智能指針不需要手動釋放空間

void test()
{
	//智能指針--編譯器調用析構自動釋放資源--不存在內存泄漏
	SmartPtr<A> sp(new A);
	(*sp)._a = 10;
	sp->_a = 100;

	//普通指針--手動釋放內存
	int* p = new int;
	A* pa = new A;
	*p = 1;
	pa->_a = 10;
	//return  //提前結束普通指針就會導致內存泄漏
	delete p;
	delete pa;
}

C++標準庫中的智能指針的使用

庫中的智能指針分為 auto_ptrunique_ptr、 share_ptr
他們都需要引入頭文件#include <memory>才能使用

auto_ptr

auto_ptr是一種存在缺陷的智能指針(禁用

#include <memory>
using namespace std;
void test()
{
	auto_ptr<int> ap(new int);
	auto_ptr<int> ap2(new int(2));
	*ap = 10;
	*ap2 = 20;
}

auto_ptr指針進行賦值操作時會將資源進行轉移,目的是為了防止多個智能指針指向同一塊內存資源。但是這種設計顯然不符合我們的需求

在這里插入圖片描述

我們來簡單模擬實現(xiàn)auto_ptr,看他底層是如何進行資源權的轉移的

//實現(xiàn)auto_ptr
template<class T>
class Auto_ptr
{
public:
	Auto_ptr(T* ptr)
		:_ptr(ptr)
	{}
	~Auto_ptr()
	{
		if (_ptr)
			delete _ptr;
	}

	T* operator->()
	{
		return _ptr;
	}
	T& operator*()
	{
		return *_ptr;
	}

	Auto_ptr(Auto_ptr<T>& ap)
		:_ptr(ap._ptr)
	{
		//資源管理權轉移 
		ap._ptr = nullptr;
	}

	Auto_ptr<T>& operator=(Auto_ptr<T>& ap)
	{
		if (this != &ap)
		{
			if (_ptr)
				delete _ptr;
			//資源管理權轉移 
			_ptr = ap._ptr;
			ap._ptr = nullptr;
		}
		return *this;
	}
private:
	T* _ptr;
};

unique_ptr

unique_ptr智能指針是通過防拷貝來解決資源管理權限轉移問題----將unique_ptr賦值運算符函數(shù)和拷貝構造函數(shù)設置為刪除函數(shù)

void test()
{
	unique_ptr<int> up(new int(10));
	unique_ptr<int> up2(up);//error
	unique_ptr<int> up3(new int(20));
	up = up3; //error
}

報錯原因:拷貝構造和賦值重載函數(shù)都是已經刪除的函數(shù)

在這里插入圖片描述

底層實現(xiàn):

template<class T>
class Unique_ptr
{
public:
	Unique_ptr(T* ptr)
		:_ptr(ptr)
	{}

	Unique_ptr(const Unique_ptr<T>& up) = delete;
	Unique_ptr<T>& operator=(const Unique_ptr<T>& up) = delete;

	~Unique_ptr()
	{
		if (_ptr)
		{
			delete _ptr;
			_ptr = nullptr;
		}
	}
private:
	T* _ptr;
};

shared_ptr

shared_ptr是C++11中新提供的一種智能指針,不僅解決了資源管理權限轉移問題,還提供靠譜的拷貝功能

class A
{
public:
	int _a = 10;
	~A()
	{
		cout << "~A()" << endl;
	}
};

void test()
{
	shared_ptr<A> sp(new A);
	shared_ptr<A> sp2(new A);
	shared_ptr<A> sp3(sp2);//ok
	sp3 = sp;//ok
	sp->_a = 100;
	sp2->_a = 1000;
	sp3->_a = 10000;
	cout << sp->_a << endl;
	cout << sp2->_a << endl;
	cout << sp3->_a << endl;
}

運行結果:

在這里插入圖片描述

我們發(fā)現(xiàn)申請多少資源就會釋放多少資源,此時的sp和sp3共享一份資源,修改sp3也就相等于修改了sp。所以最終都會打印10000。那共享了一份資源,是如何實現(xiàn)資源只釋放一次呢?----引用計數(shù)

我們可以通過shared_ptr提供的接口use_count()來查看,當前有多少個智能指針來管理同一份資源

void test()
{
	shared_ptr<A> sp(new A);
	cout << sp.use_count() << endl;//1
	shared_ptr<A> sp2(sp);
	cout << sp.use_count() << endl;//2
	cout << sp2.use_count() << endl;//2
	shared_ptr<A> sp3(new A);
	cout << sp.use_count() << endl;//2
	cout << sp2.use_count() << endl;//2
	cout << sp3.use_count() << endl;//1
	sp3 = sp;
	sp3 = sp2;
	cout << sp.use_count() << endl;//2
	cout << sp2.use_count() << endl;//2
	cout << sp3.use_count() << endl;//2
}

運行截圖:之所以中間會有調析構函數(shù),是因為當sp3指向sp時,sp3的引用計數(shù)為0,則會調用析構函數(shù)來釋放資源。此時sp創(chuàng)建的資源就有3個指智能指針來管理

在這里插入圖片描述

圖解

在這里插入圖片描述

在實現(xiàn)時,我們應該確保一個資源只對應一個計數(shù)器,而不是每個智能指針都有各自的計數(shù)器。所以我們可以將資源和計數(shù)器綁定在一起,此時指向同一份資源的智能指針,訪問的也都是同一個計數(shù)器
成員變量:成員變量應該有兩個變量,分別是資源指針的變量_ptr和計數(shù)器變量_countPtr,他們都是一個指針類型的變量
拷貝構造函數(shù):在拷貝構造函數(shù)中,應該將當前對象的指針指向要拷貝的對象的資源,而且還要拷貝它的計數(shù)器,最后還需要將計數(shù)器進行++
賦值運算符重載:我們不能判斷兩個對象是否相等,而應該為只要兩個對象的資源不同,那么才需要進行賦值。在賦值中,先讓當前對象的計數(shù)器進行–,如果為0則表示當前對象的資源是只被當前對象管理,則需要釋放資源。然后再將當前對象指針修改為要拷貝的對象的資源,并且拷貝它的計數(shù)器。最后還要對計數(shù)器進行++操作
析構函數(shù):判斷當前對象的資源的計數(shù)器,先進行–操作,在判斷計數(shù)器是否為0,如果為0才會將資源釋放,不為0則什么都不做

template<class T>
class Shared_ptr
{
public:
	Shared_ptr(T* ptr)
		:_ptr(ptr)
		, _countPtr(new size_t(1))//初始化為1
	{}
	Shared_ptr(const Shared_ptr<T>& sp)
		:_ptr(sp._ptr)
		, _countPtr(sp._countPtr)
	{
		//計數(shù)器累加
		++(*_countPtr);
	}
	Shared_ptr<T> operator=(const Shared_ptr<T>& sp)
	{
		if (_ptr != sp._ptr)
		{
			//本身計數(shù)器自減
			//計數(shù)器為0,則當前對象需要釋放資源
			if (--(*_countPtr) == 0)
			{
				delete _ptr;
				delete _countPtr;
			}

			_ptr = sp._ptr;
			_countPtr = sp._countPtr;

			++(*_countPtr);
		}
		return *this;
	}

	~Shared_ptr()
	{
		//計數(shù)器自減
		if (--(*_countPtr) == 0)
		{
			delete _ptr;
			delete _countPtr;
			_ptr = nullptr;
			_countPtr = nullptr;
		}
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
private:
	T* _ptr;
	size_t* _countPtr;//計數(shù)器指針
};

我們實現(xiàn)的shared_ptr智能指針在多線程的場景下其實是不存在線程安全問題的----引用計數(shù)器指針是一個共享變量,多個線程進行修改時會導致計數(shù)器混亂。導致資源提前被釋放或者會產生內存泄漏問題
我們來看看一下代碼, 如果是安全的,那么最后析構函數(shù)應該只被調用一次

void fun(const Shared_ptr<A>& sp, int n)
{
	for (int i = 0; i < n; ++i)
		Shared_ptr<A> copy(sp);//創(chuàng)建copy智能指針
}

void test()
{
	Shared_ptr<A> sp(new A);
	int n = 100000;
	thread t1(fun, ref(sp), n);
	thread t2(fun, ref(sp), n);
	t1.join();
	t2.join();
}

運行結果1:我們發(fā)現(xiàn)并沒有調用對象的析構函數(shù),說明此時產生了內存泄漏的問題

在這里插入圖片描述

運行結果2:調用兩次析構函數(shù),也就說明一份資源被釋放兩次。

在這里插入圖片描述

我們可以在類中提供獲取計數(shù)器的值的接口

size_t getCount()
	{
		return *_countPtr;
	}

然后再代碼中運行并獲取計數(shù)器的值,發(fā)現(xiàn)計數(shù)器的值并沒有為0,所以就不會調用調用析構函數(shù)

在這里插入圖片描述

所以我們可以在修改計數(shù)器的地方進行加鎖保護。而這個鎖不能為全局變量的鎖,資源之間不能被有影響,否則當一個資源進行加鎖修改時,另一個資源會被收到影響,此時會影響代碼的執(zhí)行效率。應當給每一個計數(shù)器提供單獨的鎖
這里++操作和–操作都進行封裝

template<class T>
class Shared_ptr
{
public:
	Shared_ptr(T* ptr)
		:_ptr(ptr)
		, _countPtr(new size_t(1))//初始化為1
		, _mtx(new mutex)
	{}
	Shared_ptr(const Shared_ptr<T>& sp)
		:_ptr(sp._ptr)
		, _countPtr(sp._countPtr)
		,_mtx(sp._mtx)
	{
		//計數(shù)器累加
		//++(*_countPtr);
		addCount();
	}
	Shared_ptr<T> operator=(const Shared_ptr<T>& sp)
	{
		if (_ptr != sp._ptr)
		{
			//本身計數(shù)器自減
			//計數(shù)器為0,則當前對象需要釋放資源
			//if (--(*_countPtr) == 0)
			if (subCount() == 0)
			{
				delete _ptr;
				delete _countPtr;
				delete _mtx;
			}

			_ptr = sp._ptr;
			_countPtr = sp._countPtr;

			addCount();
		}
		return *this;
	}

	~Shared_ptr()
	{
		//計數(shù)器自減
		if (subCount() == 0)
		{
			delete _ptr;
			delete _countPtr;
			delete _mtx;
			_ptr = nullptr;
			_countPtr = nullptr;
			_mtx = nullptr;
		}
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}

	size_t getCount()
	{
		return *_countPtr;
	}

	size_t addCount()
	{
		_mtx->lock();
		++(*_countPtr);
		_mtx->unlock();
		return *_countPtr;
	}

	size_t subCount()
	{
		_mtx->lock();
		--(*_countPtr);
		_mtx->unlock();
		return *_countPtr;
	}
private:
	T* _ptr;
	size_t* _countPtr;//計數(shù)器指針
	mutex* _mtx;
};

運行結果:我們發(fā)現(xiàn)多線程場景下,也是都可以正常釋放的

在這里插入圖片描述

循環(huán)引用問題

shared_ptr其實也存在一些小問題,也就是循環(huán)引用問題
我們先來看看以下代碼

struct ListNode
{
	shared_ptr<ListNode> _next;
	shared_ptr<ListNode> _prev;
	int _data;

	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};

void test()
{
	shared_ptr<ListNode> n1(new ListNode);
	shared_ptr<ListNode> n2(new ListNode);

	cout << n1.use_count() << endl;
	cout << n2.use_count() << endl;

	n1->_next = n2;
	n2->_prev = n1;

	cout << n1.use_count() << endl;
	cout << n2.use_count() << endl;
}

運行結果:我們發(fā)現(xiàn)并沒有釋放資源,計數(shù)器也在自增

在這里插入圖片描述

圖解:

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

在C++11中,專門為了解決這個問題,又引入了一種新的智能指針waek_ptr,這種指針稱為弱指針。在賦值或者拷貝的時候,計數(shù)器并不會進行++。析構時也并不會進行真正資源的釋放。waek_ptr不能單獨使用,其最大的作用就是解決shared_ptr循環(huán)引用的問題。

struct ListNode
{
	weak_ptr<ListNode> _next;
	weak_ptr<ListNode> _prev;
	int _data;

	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};

void test()
{
	shared_ptr<ListNode> n1(new ListNode);
	shared_ptr<ListNode> n2(new ListNode);

	cout << n1.use_count() << endl;
	cout << n2.use_count() << endl;

	n1->_next = n2;
	n2->_prev = n1;

	cout << n1.use_count() << endl;
	cout << n2.use_count() << endl;
}

運行結果:

在這里插入圖片描述

在我們自己實現(xiàn)的shared_ptr中,我們在釋放資源時都單單以delete來釋放,而在我們申請空間方式中并非就只用new來申請空間們也有可能是用malloc來申請,則此時就應該要用free來釋放。所以我們還要給智能指針添加一個刪除器

void test()
{
	Shared_ptr<A> sp(new A[100]);//調用析構會報錯
}

刪除器主要可以通過仿函數(shù)來實現(xiàn)

template<class T>
struct DeleteDel
{
	void operator()(T* ptr)
	{
		delete ptr;
	}
};

template<class T>
struct FreeDel
{
	void operator()(T* ptr)
	{
		free(ptr);
	}
};

template<class T>
struct DeleteArrDel
{
	void operator()(T* ptr)
	{
		delete[] ptr;
	}
};
template<class T, class Del = DeleteDel<T>>
class Shared_ptr
{
public:
	Shared_ptr(T* ptr)
		:_ptr(ptr)
		, _countPtr(new size_t(1))//初始化為1
		, _mtx(new mutex)
	{}
	Shared_ptr(const Shared_ptr<T>& sp)
		:_ptr(sp._ptr)
		, _countPtr(sp._countPtr)
		,_mtx(sp._mtx)
	{
		//計數(shù)器累加
		//++(*_countPtr);
		addCount();
	}
	Shared_ptr<T> operator=(const Shared_ptr<T>& sp)
	{
		if (_ptr != sp._ptr)
		{
			//本身計數(shù)器自減
			//計數(shù)器為0,則當前對象需要釋放資源
			//if (--(*_countPtr) == 0)
			if (subCount() == 0)
			{
				//delete _ptr;
				//通過刪除器來釋放空間
				_del(_ptr);
				delete _countPtr;
				delete _mtx;
			}

			_ptr = sp._ptr;
			_countPtr = sp._countPtr;

			addCount();
		}
		return *this;
	}

	~Shared_ptr()
	{
		//計數(shù)器自減
		if (subCount() == 0)
		{
			//delete _ptr;
			//通過刪除器來釋放空間
			_del(_ptr);
			delete _countPtr;
			delete _mtx;
			_ptr = nullptr;
			_countPtr = nullptr;
			_mtx = nullptr;
		}
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}

	size_t getCount()
	{
		return *_countPtr;
	}

	size_t addCount()
	{
		_mtx->lock();
		++(*_countPtr);
		_mtx->unlock();
		return *_countPtr;
	}

	size_t subCount()
	{
		_mtx->lock();
		--(*_countPtr);
		_mtx->unlock();
		return *_countPtr;
	}
private:
	T* _ptr;
	size_t* _countPtr;//計數(shù)器指針
	mutex* _mtx;
	Del _del;
};

在這里插入圖片描述

以上就是C++ 一篇文章讓你知道智能指針的魅力的詳細內容,更多關于C++智能指針的資料請關注腳本之家其它相關文章!

相關文章

  • C++實現(xiàn)棧的操作(push和pop)

    C++實現(xiàn)棧的操作(push和pop)

    這篇文章主要介紹了C++實現(xiàn)棧的操作(push和pop),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • 利用Matlab制作抖音同款含褶皺面料圖

    利用Matlab制作抖音同款含褶皺面料圖

    這篇文章主要介紹了如何利用Matlab制作抖音的同款含褶皺面料圖,文中的示例代碼講解詳細,對我們學習Matlab有一定幫助,需要的可以參考一下
    2022-03-03
  • C語言使用stdlib.h庫函數(shù)的二分查找和快速排序的實現(xiàn)代碼

    C語言使用stdlib.h庫函數(shù)的二分查找和快速排序的實現(xiàn)代碼

    以下是對C語言使用stdlib.h庫函數(shù)的二分查找和快速排序的實現(xiàn)代碼進行了詳細的介紹,需要的朋友可以過來參考下。希望對大家有所幫助
    2013-10-10
  • C語言求解無向圖頂點之間的所有最短路徑

    C語言求解無向圖頂點之間的所有最短路徑

    這篇文章主要為大家詳細介紹了C語言求解無向圖頂點之間的所有最短路徑,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • C++第三方日志庫log4cplus的安裝與使用配置教程

    C++第三方日志庫log4cplus的安裝與使用配置教程

    log4cplus是C++編寫的開源的日志系統(tǒng),log4cplus具有線程安全、靈活、以及多粒度控制的特點,本文給大家介紹C++第三方日志庫log4cplus的安裝與使用教程,感興趣的朋友一起看看吧
    2022-02-02
  • C++實現(xiàn)順序排序算法簡單示例代碼

    C++實現(xiàn)順序排序算法簡單示例代碼

    這篇文章主要介紹了C++實現(xiàn)順序排序算法簡單示例代碼,對于學過C++的朋友一定不會陌生,現(xiàn)在重溫一下這個算法,需要的朋友可以參考下
    2014-08-08
  • c++讀取sqlserver示例分享

    c++讀取sqlserver示例分享

    這篇文章主要介紹了c++讀取sqlserver的示例,需要的朋友可以參考下
    2014-02-02
  • 詳解C++中移動語義的概念與使用

    詳解C++中移動語義的概念與使用

    本篇文章主要為大家詳細介紹了C++中移動語義的相關知識,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考,一起跟隨小編過來看看吧
    2023-06-06
  • 淺析C語言中堆和棧的區(qū)別

    淺析C語言中堆和棧的區(qū)別

    堆和棧都是一種數(shù)據項按序排列的數(shù)據結構。在C語言中是非常重要的知識點,接下來通過本文給大家介紹C語言中堆和棧的區(qū)別,感興趣的朋友一起看下吧
    2016-06-06
  • C++快速排序及優(yōu)化方案詳解

    C++快速排序及優(yōu)化方案詳解

    這篇文章主要介紹了C++快速排序及優(yōu)化方案詳解,快速排序是一種常用的排序算法,它通過選擇一個基準元素,將數(shù)組分成兩個子數(shù)組,其中一個子數(shù)組的所有元素都小于基準元素,另一個子數(shù)組的所有元素都大于基準元素,需要的朋友可以參考下
    2023-10-10

最新評論

普宁市| 荔浦县| 揭阳市| 确山县| 新乐市| 内江市| 运城市| 商河县| 临沧市| 江孜县| 永和县| 合水县| 西藏| 深州市| 朝阳县| 仙居县| 昆山市| 宿松县| 东阳市| 永嘉县| 麦盖提县| 甘孜| 娱乐| 手游| 泊头市| 涞源县| 澜沧| 文昌市| 五大连池市| 南京市| 邛崃市| 淮阳县| 库尔勒市| 宁德市| 交城县| 哈尔滨市| 曲靖市| 贵德县| 建瓯市| 仁寿县| 昆明市|