溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

C++11智能指針的具體使用方法

發(fā)布時(shí)間:2021-08-24 15:15:08 來(lái)源:億速云 閱讀:280 作者:chen 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“C++11智能指針的具體使用方法”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

目錄
  • 智能指針的原理

    • RAII

    • 智能指針的原理

    • auto_ptr

      • 1.auto_ptr的使用及問題

    • unique_ptr

      • shared_ptr

        • shared_ptr的循環(huán)引用

        智能指針的原理

        RAII

        RAII(Resource Acquisition Is Initialization)是一種利用對(duì)象生命周期來(lái)控制程序資源(如內(nèi)存、文件句柄、網(wǎng)絡(luò)連接、互斥量等等)的簡(jiǎn)單技術(shù)。

        在對(duì)象構(gòu)造時(shí)獲取資源,接著控制對(duì)資源的訪問使之在對(duì)象的生命周期內(nèi)始終保持有效,最后在對(duì)象析構(gòu)的時(shí)候釋放資源。 借此,我們實(shí)際上把管理一份資源的責(zé)任托管給了一個(gè)對(duì)象。這種做法有兩大好處:

        • 不需要顯式地釋放資源。

        • 采用這種方式,對(duì)象所需的資源在其生命期內(nèi)始終保持有效。

        我們使用RAII的思想設(shè)計(jì)SmartPtr類:

        template <class T>
        class SmartPtr
        {
        public:
        	SmartPtr(T* ptr)
        		:_ptr(ptr)
        	{}
        
            ~SmartPtr()
        	{
        		if (_ptr)
        		{
        			delete _ptr;
        			_ptr = nullptr;
        		}
        	}
        
        private:
        	T* _ptr;
        };

        智能指針的原理

        上述的SmartPtr還不能將其稱為智能指針,因?yàn)樗€不具有指針的行為。指針可以解引用,也可以通過->去訪問所指空間中的內(nèi)容 ,因此:SmartPtr模板類中還得需要將* 、->重載下,才可讓其像指針一樣去使用。

        template <class T>
        class SmartPtr
        {
        public:
        	SmartPtr(T* ptr)
        		:_ptr(ptr)
        	{}
        
        	T& operator*()
        	{
        		return *_ptr;
        	}
        
        	T* operator->()
        	{
        		return _ptr;
        	}
        
            ~SmartPtr()
        	{
        		if (_ptr)
        		{
        			delete _ptr;
        			_ptr = nullptr;
        		}
        	}
        
        private:
        	T* _ptr;
        };

        智能指針使用:

        C++11智能指針的具體使用方法

        總結(jié)智能指針的原理:

        • RAII特性

        • 重載operator*和opertaor->,具有像指針一樣的行為。

        auto_ptr

        1.auto_ptr的使用及問題

        auto_ptr的頭文件#include<memory>

        auto_ptr的使用:

        C++11智能指針的具體使用方法

        C++11智能指針的具體使用方法

        為什么此時(shí)訪問sp的成員時(shí)會(huì)報(bào)錯(cuò)呢?我們來(lái)看看它們的地址。

        C++11智能指針的具體使用方法

        我們發(fā)現(xiàn)在拷貝構(gòu)造之后,sp管理的地址為空,而sp1管理的地址是之前sp所管理的地址,管理權(quán)發(fā)生了轉(zhuǎn)移。那么上面所說(shuō)的報(bào)錯(cuò)也很容易想通,因?yàn)閟p管理的地址為空,不能進(jìn)行訪問。

        auto_ptr的問題:當(dāng)對(duì)象拷貝或者賦值后,管理權(quán)進(jìn)行轉(zhuǎn)移,造成前面的對(duì)象懸空。auto_ptr問題是非常明顯的,所以實(shí)際中很多公司明確規(guī)定了不能使用auto_ptr。

        auto_ptr的實(shí)現(xiàn)原理:管理權(quán)轉(zhuǎn)移的思想,下面簡(jiǎn)化模擬實(shí)現(xiàn)了一份AutoPtr來(lái)了解它的原理:

        template<class T>
        class AutoPtr
        {
        public:
        	AutoPtr(T* ptr)
        		:_ptr(ptr)
        	{}
        
        	//拷貝:管理權(quán)轉(zhuǎn)移
        	AutoPtr(AutoPtr<T> &sp)
        		:_ptr(sp._ptr)
        	{
        		sp._ptr = nullptr;
        	}
        
        	//賦值:管理權(quán)轉(zhuǎn)移
        	AutoPtr& operator=(AutoPtr<T> &sp)
        	{
        		if (this != &sp)
        		{
        			if (_ptr)
        				delete _ptr;
        			_ptr = sp._ptr;
        			sp._ptr = nullptr;
        		}
        		return *this;
        	}
        
        	~AutoPtr()
        	{
        		if (_ptr)
        		{
        			delete _ptr;
        			_ptr = nullptr;
        		}
        	}
        
        	T* operator->()
        	{
        		return _ptr;
        	}
        
        	T& operator*()
        	{
        		return *_ptr;
        	}
        
        private:
        	T* _ptr;
        };

        unique_ptr

        為了解決拷貝或者賦值時(shí)管理權(quán)轉(zhuǎn)移的問題,出現(xiàn)了unique_ptr。

        unique_ptr解決問題的方式非常粗暴:防拷貝,也就是不讓賦值和拷貝

        unique_ptr的使用:

        C++11智能指針的具體使用方法

        unique_ptr的實(shí)現(xiàn)原理:簡(jiǎn)單粗暴的防拷貝,下面簡(jiǎn)化模擬實(shí)現(xiàn)了一份UniquePtr來(lái)了解它的原理:

        template<class T>
        class UniquePtr
        {
        public:
        
        	UniquePtr(T* ptr)
        		:_ptr(ptr)
        	{}
        
        	// C++11防拷貝的方式:delete
        	UniquePtr(const UniquePtr<T> &) = delete;
        
        	UniquePtr& operator=(const UniquePtr<T>&) = delete;
        
        	T* operator->()
        	{
        		return _ptr;
        	}
        
        	T& operator*()
        	{
        		return *_ptr;
        	}
        
        	~UniquePtr()
        	{
        		if (_ptr)
        		{
        			delete _ptr;
        			_ptr = nullptr;
        		}
        	}
        
        private:
        
        	//C++98防拷貝的方式:只聲明不實(shí)現(xiàn)+聲明成私有
        	//UniquePtr(UniquePtr<T> const &);
        	//UniquePtr& operator=(UniquePtr<T> const &);
        
        	T* _ptr;
        };

        shared_ptr

        c++11中提供更靠譜的并且支持拷貝的shared_ptr

        shared_ptr的使用

        C++11智能指針的具體使用方法

        shared_ptr中拷貝與賦值都是沒有問題的。

        shared_ptr的原理

        • shared_ptr的原理:是通過引用計(jì)數(shù)的方式來(lái)實(shí)現(xiàn)多個(gè)shared_ptr對(duì)象之間共享資源。shared_ptr在其內(nèi)部,給每個(gè)資源都維護(hù)了著一份計(jì)數(shù),用來(lái)記錄該份資源被幾個(gè)對(duì)象共享。

        • 對(duì)象被銷毀時(shí)(也就是析構(gòu)函數(shù)調(diào)用),就說(shuō)明自己不使用該資源了,對(duì)象的引用計(jì)數(shù)減一。

        • 如果引用計(jì)數(shù)是0,就說(shuō)明自己是最后一個(gè)使用該資源的對(duì)象,必須釋放該資源;

        • 如果不是0,就說(shuō)明除了自己還有其他對(duì)象在使用該份資源,不能釋放該資源,否則其他對(duì)象就成野指針了。

        shared_ptr中成員函數(shù):use_count(對(duì)象數(shù)據(jù)的引用計(jì)數(shù))

        示例:

        C++11智能指針的具體使用方法

        示例詳解:

        C++11智能指針的具體使用方法

        利用引用計(jì)數(shù)簡(jiǎn)單的實(shí)現(xiàn)SharedPtr,了解原理:

        template<class T>
        class SharedPtr
        {
        public:
        	SharedPtr(T* ptr)
        		:_ptr(ptr)
        		,_count(new int(1))
        	{}
        
        	SharedPtr(const SharedPtr<T> &sp)
        		:_ptr(sp._ptr)
        		,_count(sp._count)
        	{
        		//計(jì)數(shù)器累加
        		++(*_count);
        	}
        
        	SharedPtr& operator=(const SharedPtr<T> &sp)
        	{
        		//判斷管理的是否是同一份資源
        		if (_ptr != sp._ptr)
        		{
        			//計(jì)數(shù)-1,判斷之前管理的資源是否需要釋放
        			if ((--(*_count)) == 0)
        			{
        				delete _ptr;
        				delete _count;
        			}
        			
        			_ptr = sp._ptr;
        			_count = sp._count;
        
        			//計(jì)數(shù)器累加
        			++(*_count);
        		}
        		return *this;
        	}
        
        	T* operator->()
        	{
        		return _ptr;
        	}
        	
        	T& operator*()
        	{
        		return *_ptr;
        	}
        
        	~SharedPtr()
        	{
        		if (--(*_count) == 0)
        		{
        			delete _ptr;
        			delete _count;
        			_ptr = nullptr;
        			_count = nullptr;
        		}
        	}
        
        private:
        	T* _ptr;
        	int* _count;//給每份資源開辟一個(gè)計(jì)數(shù)器
        };

        但是還存在一個(gè)線程安全的問題:

        1. 智能指針對(duì)象中引用計(jì)數(shù)是多個(gè)智能指針對(duì)象共享的,兩個(gè)線程中智能指針的引用計(jì)數(shù)同時(shí)++或–,這個(gè)操作不是原子的,引用計(jì)數(shù)原來(lái)是1,++了兩次,可能還是2。這樣引用計(jì)數(shù)就錯(cuò)亂了。會(huì)導(dǎo)致資源未釋放或者程序崩潰的問題。所以只能指針中引用計(jì)數(shù)++、- -是需要加鎖的,也就是說(shuō)引用計(jì)數(shù)的操作是線程安全的。

        2. 智能指針管理的對(duì)象存放在堆上,兩個(gè)線程中同時(shí)去訪問,會(huì)導(dǎo)致線程安全問題。

        這里我們通過加鎖來(lái)解決線程安全問題:

        template<class T>
        class SharedPtr
        {
        public:
        	SharedPtr(T* ptr)
        		:_ptr(ptr)
        		,_count(new int(1))
        		,_mutex(new mutex)
        	{}
        
        	SharedPtr(const SharedPtr<T> &sp)
        		:_ptr(sp._ptr)
        		,_count(sp._count)
        		,_mutex(sp._mutex)
        	{
        		//計(jì)數(shù)器累加
        		AddCount();
        	}
        
        	SharedPtr& operator=(const SharedPtr<T> &sp)
        	{
        		//判斷管理的是否是同一份資源
        		if (_ptr != sp._ptr)
        		{
        			//計(jì)數(shù)-1,判斷之前管理的資源是否需要釋放
        			if (SubCount() == 0)
        			{
        				delete _ptr;
        				delete _count;
        				delete _mutex;
        			}
        			
        			_ptr = sp._ptr;
        			_count = sp._count;
        			_mutex = sp._mutex;
        
        				//計(jì)數(shù)器累加
        			AddCount();
        		}
        		return *this;
        	}
        
        	//線程安全的累加器
        	int AddCount()
        	{
        		//加鎖
        		_mutex->lock();
        		++(*_count);
        		_mutex->unlock();
        		return *_count;
        	}
        
        	int SubCount()
        	{
        		_mutex->lock();
        		--(*_count);
        		_mutex->unlock();
        		return *_count;
        	}
        
        	T* operator->()
        	{
        		return _ptr;
        	}
        	
        	T& operator*()
        	{
        		return *_ptr;
        	}
        
        	~SharedPtr()
        	{
        		if (SubCount() == 0)
        		{
        			delete _ptr;
        			delete _count;
        			delete _mutex;
        			_ptr = nullptr;
        			_count = nullptr;
        			_mutex = nullptr;
        		}
        	}
        	
        private:
        	T* _ptr;
        	int* _count;//給每份資源開辟一個(gè)計(jì)數(shù)器
        	mutex* _mutex; //每一份資源有一個(gè)獨(dú)立的鎖
        };

        shared_ptr的循環(huán)引用

        循環(huán)引用的場(chǎng)景:

        struct ListNode
        {
        	int _data; 
            shared_ptr<ListNode> _prev;
        	shared_ptr<ListNode> _next;
        	~ListNode() { cout << "~ListNode()" << endl; }
        };
        
        void test()
        {
        	shared_ptr<ListNode> node1(new ListNode);
        	shared_ptr<ListNode> node2(new ListNode);
        
        	node1->_next = node2;
        	node2->_prev = node1;
        }

        C++11智能指針的具體使用方法

        node1和node2兩個(gè)智能指針對(duì)象指向兩個(gè)節(jié)點(diǎn),兩個(gè)節(jié)點(diǎn)的引用計(jì)數(shù)都是1。node1->next指向node2,node2->prev指向node1,兩個(gè)節(jié)點(diǎn)的引用計(jì)數(shù)都變成2。程序運(yùn)行完之后,析構(gòu)node1和node2,node1和node2所指向的節(jié)點(diǎn)引用計(jì)數(shù)分別減1,但是node1->next指向下面節(jié)點(diǎn),node2->prev指向上面節(jié)點(diǎn),此時(shí),兩個(gè)節(jié)點(diǎn)的引用計(jì)數(shù)都為1,所以兩個(gè)節(jié)點(diǎn)不能析構(gòu)。

        引用計(jì)數(shù)為0時(shí),如果要析構(gòu)node1節(jié)點(diǎn),就先要去析構(gòu)node1中的自定義結(jié)構(gòu),然后再析構(gòu)node1。也就是說(shuō)node1->next析構(gòu)了,node2就釋放了;node2->prev析構(gòu)了,node1就釋放了。但是_next屬于node的成員,node1釋放了,_next才會(huì)析構(gòu),而node1由_prev管理,_prev屬于node2成員,所以這就叫循環(huán)引用,誰(shuí)也不會(huì)釋放。

        解決方案:
        在引用計(jì)數(shù)的場(chǎng)景下,把節(jié)點(diǎn)中的_prev和_next改成weak_ptr就可以了
        原理就是,node1->_next = node2和node2->_prev = node1時(shí)weak_ptr的_next和_prev不會(huì)增加node1和node2的引用計(jì)數(shù)。

        weak_ptr最大作用就是解決shared_ptr的循環(huán)引用

        struct ListNode
        {
        	int _data; 
            weak_ptr<ListNode> _prev;
        	weak_ptr<ListNode> _next;
        	~ListNode() { cout << "~ListNode()" << endl; }
        };
        void test()
        {
        	shared_ptr<ListNode> node1(new ListNode);
        	shared_ptr<ListNode> node2(new ListNode);
        
        	node1->_next = node2;
        	node2->_prev = node1;
        }

        注意:
        weak_ptr不能單獨(dú)使用,可以用shared_ptr創(chuàng)建

        	//weak_ptr錯(cuò)誤使用
        	weak_ptr<ListNode> node1(new ListNode);
        
        	//weak_ptr正確使用
        	shared_ptr<ListNode> node2(new ListNode);
        	weak_ptr<ListNode> node3(node2);

        “C++11智能指針的具體使用方法”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

        向AI問一下細(xì)節(jié)

        免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

        c++
        AI