shared_ptrインテリジェントポインタ欠陥の解決方法

29336 ワード

前回のブログ「C+:スマートポインタ」では、C++のスマートポインタについて詳しく説明しましたが、shared_ptrスマートポインタには3つの欠陥があります.
  • スレッドは安全ではありません.
  • 管理資源能力単一:mallocから出た資源を管理できず、ファイルポインタを管理できない.
  • は、循環参照の問題を生じる可能性がある.

  • 3つ目の欠陥(ループリファレンス)の解決方法は、【C++:スマートポインタ】というブログで解決されています.このブログは主に第1と第2の欠陥の解決方法を紹介します.
    ディレクトリ:1.欠陥1:スレッドが安全ではない2.欠陥二:管理資源能力単一
    欠陥のあるコード:
    template<class T>
    class SharedPtr
    {
    private:
    	T* _ptr;
    	int* _count;
    private:
    	void Release()
    	{
    		if (_ptr && ((*_count--) == 0))
    		{
    			delete _ptr;
    			delete _count;
    		}
    	}
    	void AddCount(SharedPtr<T>& sp)
    	{
    		_ptr = sp._ptr;
    		_count = sp._count;
    		if (_ptr)
    			(*_count)++;
    	}
    public:
    	SharedPtr(T* ptr = nullptr)
    		:_ptr(ptr)
    		, _count(nullptr)
    	{
    		if (_ptr)
    			_count = new int(1);
    	}
    	SharedPtr(SharedPtr<T>& sp)
    	{
    		AddCount(sp);
    	}
    	SharedPtr<T>& operator=(SharedPtr<T>& sp)
    	{
    		if (this != &sp)
    		//  :if(_ptr != sp._ptr)
    		{
    			Release();
    			AddCount(sp);
    		}
    		return *this;
    	}
    	~SharedPtr()
    	{
    		Release();
    	}
    	T& operator*()
    	{
    		return *_ptr;
    	}
    	T* operator->()
    	{
    		return _ptr;
    	}
    	int Use_count()
    	{
    		return *_count;
    	}
    };
    
    

    欠陥1:スレッドが安全でない
    shared_ptrのスレッドセキュリティは2つの側面に分けられます.
  • shared_ptrスマートポインタオブジェクトでの参照カウントは複数のスマートポインタオブジェクトが共有しており、2つのスレッドでスマートポインタの参照カウントが同時に++または--操作され、ロックをかける場合、カウントが混乱し、リソースの漏洩やプログラムの崩壊の問題を引き起こす可能性があり、++および--操作自体も原子ではない.
  • インテリジェントポインタ管理のオブジェクトはスタックに保存され、2つのスレッドが同時にアクセスし、スレッドのセキュリティの問題も発生します.

  • したがって、スレッドの安全を保証するためには、臨界資源にアクセスする場所(臨界領域)に適切な場所に鍵をかける必要がある.アクセスが完了したらロックを解除します.
    次のコードは、スレッドセキュリティバージョンのコードです.
    template<class T>
    class SharedPtr
    {
    private:
    	T* _ptr;
    	int* _count;
    	mutex m_mtx;
    private:
    	void Release()
    	{
    		m_mtx.lock();
    		if (_ptr && ((*_count--) == 0))
    		{
    			delete _ptr;
    			delete _count;
    		}
    		m_mtx.unlock();
    	}
    	void AddCount(SharedPtr<T>& sp)
    	{
    		_ptr = sp._ptr;
    		_count = sp._count;
    		m_mtx.lock();
    		if (_ptr)
    			(*_count)++;
    		m_mtx.unlock();
    	}
    public:
    	SharedPtr(T* ptr = nullptr)
    		:_ptr(ptr)
    		, _count(nullptr)
    	{
    		m_mtx.lock();
    		if (_ptr)
    			_count = new int(1);
    		m_mtx.unlock();
    	}
    	SharedPtr(SharedPtr<T>& sp)
    	{
    		AddCount(sp);
    	}
    	SharedPtr<T>& operator=(SharedPtr<T>& sp)
    	{
    		if (this != &sp)
    		//  :if(_ptr != sp._ptr)
    		{
    			Release();
    			AddCount(sp);
    		}
    		return *this;
    	}
    	~SharedPtr()
    	{
    		Release();
    	}
    	T& operator*()
    	{
    		return *_ptr;
    	}
    	T* operator->()
    	{
    		return _ptr;
    	}
    	int Use_count()
    	{
    		return *_count;
    	}
    };
    

    ここではロックを使用しているので、デッドロック問題の発生も予防しなければなりません.
    欠陥2:new以外の申請の対象を処理できない
    前回のブログでは【C++:スマートポインタ】shared_ptrはnewで申請されたオブジェクトリソースしか処理できないので、使用者がshared_を使用するとptr管理がnewで申請されたオブジェクトでない場合、プログラムがリソースを解放するまで実行されるとエラーが発生します.
    この問題を解決するためにC++はshared_ptrは特殊な削除器を設計した.
    この特殊な削除器は本質的に模倣関数であり、模倣関数の特徴を利用してshared_ptrインテリジェントポインタはリソース能力の単一の欠陥を管理し、管理リソースを解放するタスクをカスタマイズされた削除器に渡して完了します.
    次に、削除器を追加したコードを示します.
    //-------------------------     ---------------------------------
    		template<class T>//-------  new     
    		class DFDel
    		{
    			public:
    				void operator()(T*& ptr)
    				{
    					delete ptr;
    					ptr = nullptr;
    				}
    		}
    		template<class T>//-------  malloc     
    		class Free
    		{
    			public:
    				void operator()(T*& ptr)
    				{
    					free(ptr);
    					ptr = nullptr;
    				}
    		}
    		template<class T>//-------------------------  FIFE     
    		class Close
    		{
    			public:
    				void operator()(FIFE*& ptr)
    				{
    					fclose(ptr);
    					ptr = nullptr;
    				}
    		}
    //--------------------------------------------------------
    //          
    template<class T,class DF = DFDel<T>>
    class SharedPtr
    {
    private:
    	T* _ptr;
    	int* _count;
    private:
    	void Release()
    	{
    		if (_ptr && ((*_count--) == 0))
    		{
    			//   :
    			DF()(_ptr);
    			//DF:        ;DF():        
    		}
    	}
    	void AddCount(SharedPtr<T>& sp)
    	{
    		_ptr = sp._ptr;
    		_count = sp._count;
    		if (_ptr)
    			(*_count)++;
    	}
    public:
    	SharedPtr(T* ptr = nullptr)
    		:_ptr(ptr)
    		, _count(nullptr)
    	{
    		if (_ptr)
    			_count = new int(1);
    	}
    	SharedPtr(SharedPtr<T>& sp)
    	{
    		AddCount(sp);
    	}
    	SharedPtr<T>& operator=(SharedPtr<T>& sp)
    	{
    		if (this != &sp)
    		//  :if(_ptr != sp._ptr)
    		{
    			Release();
    			AddCount(sp);
    		}
    		return *this;
    	}
    	~SharedPtr()
    	{
    		Release();
    	}
    	T& operator*()
    	{
    		return *_ptr;
    	}
    	T* operator->()
    	{
    		return _ptr;
    	}
    	int Use_count()
    	{
    		return *_count;
    	}
    };