天天看點

不帶引用計數的智能指針auto_ptrauto_ptr注意事項總結scoped_ptr(獨占空間—>禁止拷貝、禁止指派)unique_ptr

文章目錄

  • auto_ptr
  • auto_ptr注意事項總結
  • scoped_ptr(獨占空間--->禁止拷貝、禁止指派)
  • unique_ptr
    • 初始化

C++庫中提供的不帶引用計數的智能指針主要包括: auto_ptr,scoped_ptr,unique_ptr

auto_ptr

auto_ptr是C++98标準化引入的

源碼

template<class _Ty>
	class auto_ptr
		{	// wrap an object pointer to ensure destruction
public:
	typedef auto_ptr<_Ty> _Myt;
	typedef _Ty element_type;

   //構造函數的explicit關鍵詞有效阻止從一個"裸"指針隐式轉換成auto_ptr類型。
	explicit auto_ptr(_Ty *_Ptr = 0) _THROW0()//_THROW0()是禁止抛出異常
		: _Myptr(_Ptr)
		{	// construct from object pointer
		}



	/*auto_ptr的拷貝構造函數,
	_Right.release()函數中,把_Right的_Myptr
	賦為nullptr,讓目前auto_ptr持有本來_Right指向的資源的位址,保證一塊空間隻有一個指針指向
	*/
	auto_ptr(_Myt& _Right) _THROW0()
		: _Myptr(_Right.release())
		{	// construct by assuming pointer from _Right auto_ptr
		}

       _Ty *release() _THROW0()
		{	// return wrapped pointer and give up ownership
		_Ty *_Tmp = _Myptr;
		_Myptr = 0;//清理自己的空間
		return (_Tmp);//将儲存的空間位址傳回
		}
     

    
    //帶有隐式類型的構造函數
	auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0()
		{	// construct by assuming pointer from _Right auto_ptr_ref
		_Ty *_Ptr = _Right._Ref;
		_Right._Ref = 0;	// release old
		_Myptr = _Ptr;	// reset this
		}

	template<class _Other>
		operator auto_ptr<_Other>() _THROW0()//進行類型轉換的一個函數
		{	// convert to compatible auto_ptr
		return (auto_ptr<_Other>(*this));
		}

	template<class _Other>
		operator auto_ptr_ref<_Other>() _THROW0()
		{	// convert to compatible auto_ptr_ref
		_Other *_Cvtptr = _Myptr;	// test implicit conversion
		auto_ptr_ref<_Other> _Ans(_Cvtptr);
		_Myptr = 0;	// pass ownership to auto_ptr_ref
		return (_Ans);
		}


    /* 将一個auto_ptr<_Other>的對象轉換為一個auto_ptr<_Ty>的對象  */
	template<class _Other>
		_Myt& operator=(auto_ptr<_Other>& _Right) _THROW0()
		{	// assign compatible _Right (assume pointer)
		reset(_Right.release());
		return (*this);
		}

	template<class _Other>
		auto_ptr(auto_ptr<_Other>& _Right) _THROW0()
		: _Myptr(_Right.release())
		{	// construct by assuming pointer from _Right
		}

	//拷貝構造函數
	_Myt& operator=(_Myt& _Right) _THROW0()
		{	// assign compatible _Right (assume pointer)
		reset(_Right.release());
		return (*this);
		}

	_Myt& operator=(auto_ptr_ref<_Ty> _Right) _THROW0()
		{	// assign compatible _Right._Ref (assume pointer)
		_Ty *_Ptr = _Right._Ref;
		_Right._Ref = 0;	// release old
		reset(_Ptr);	// set new   讓該對象指向的指針指向_ptr指向的空間
		return (*this);
		}

	~auto_ptr() _NOEXCEPT
		{	// destroy the object
		delete _Myptr;
		}



//重載以下運算符是為了使得智能指針可以像一般指針一樣使用。
	_Ty& operator*() const _THROW0()
		{	// return designated value
 #if _ITERATOR_DEBUG_LEVEL == 2
		if (_Myptr == 0)
			_DEBUG_ERROR("auto_ptr not dereferencable");
 #endif /* _ITERATOR_DEBUG_LEVEL == 2 */

		return (*get());
		}

	_Ty *operator->() const _THROW0()
		{	// return pointer to class object
 #if _ITERATOR_DEBUG_LEVEL == 2
		if (_Myptr == 0)
			_DEBUG_ERROR("auto_ptr not dereferencable");
 #endif /* _ITERATOR_DEBUG_LEVEL == 2 */

		return (get());
		}

	_Ty *get() const _THROW0()
		{	// return wrapped pointer
		return (_Myptr);
		}

	

	void reset(_Ty *_Ptr = 0)
		{	// destroy designated object and store new pointer
		if (_Ptr != _Myptr)
			delete _Myptr;//釋放掉該對象的原空間
		_Myptr = _Ptr;    //讓該對象指向新的空間
		}

private:
	_Ty *_Myptr;	// the wrapped object pointer,智能指針内封裝的一個原生态指針
	};

           

從auto_ptr的源碼可以看到,隻有最後一個auto_ptr智能指針持有資源,原來的auto_ptr都被賦nullptr了

是以如果我們還繼續使用被賦nullptr的智能指針機會發生嚴重的錯誤。

通路 NULL 指針的行為會産生不可預料的後果。但是在 Linux 系統中後果是确定的:通路空指針會産生 Segmentation fault 的錯誤

int main()
{
  auto_ptr<int>  ptr(new int);
  auto_ptr<int>  ptr1(ptr);
   
  *ptr=10;
  return 0;
}
           
不帶引用計數的智能指針auto_ptrauto_ptr注意事項總結scoped_ptr(獨占空間—&gt;禁止拷貝、禁止指派)unique_ptr

注意:auto_ptr 拷貝構造函數是會修改引用參數的,是以不能使用在STL中,STL容器要求拷貝構造函數中對源對象保持不變,auto_ptr會将源對象的指針擁有轉移到自身,導緻源對象為空(不再指向被建立的對象)

int main()
{
	vector<auto_ptr<int>> vec;
	vec.push_back(auto_ptr<int>(new int(10)));
	vec.push_back(auto_ptr<int>(new int(20)));
	vec.push_back(auto_ptr<int>(new int(30)));
	// 這裡可以列印出10
	cout << *vec[0] << endl;
	vector<auto_ptr<int>> vec1 = vec;
	/* 這裡由于上面做了vector容器的拷貝,相當于容器中
	的每一個元素都進行了拷貝構造,原來vec中的智能指針
	全部為nullptr了,再次通路就成通路空指針了,程式崩潰
	*/
	cout << *vec[0] << endl;
	return 0;
}
           

是以不要在容器中使用auto_ptr,C++建議最好不要使用auto_ptr

auto_ptr注意事項總結

1、auto_ptr不能共享所有權

2、auto_ptr不能指向數組

3、auto_ptr不能作為容器的成員

4、不能通過複制操作來初始化auto_ptr,這是因為auto_ptr的構造函數被定義了explicit,将指針作為參數,是以不能自動将指針轉換為智能指針對象:

auto_ptr<int> p(new int(10)); //OK
auto_ptr<int>p = new int(10);//Error
           

scoped_ptr(獨占空間—>禁止拷貝、禁止指派)

template<class T> class scoped_ptr // noncopyable
{
private:
    T * px;
	

	/*将拷貝構造函數和指派函數私有化,這樣scoped_ptr的智能指針
	對象就不支援這兩種操作,從根本上杜絕淺拷貝的發生*/
	r(scoped_ptr const &);
    scoped_ptr & operator=(scoped_ptr const &);
 
    typedef scoped_ptr<T> this_type;
		
	/*
	私有化邏輯比較運算符重載函數,不支援scoped_ptr的智能指針
	對象的比較操作
	*/
    void operator==( scoped_ptr const& ) const;
    void operator!=( scoped_ptr const& ) const;
 
public:
    typedef T element_type;
    explicit scoped_ptr( T * p = 0 ): px( p ) // never throws
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_scalar_constructor_hook( px );
#endif
    }
 
#ifndef BOOST_NO_AUTO_PTR
	/*支援從auto_ptr構造一個scoped_ptr智能指針對象,
	但是auto_ptr因為調用release()函數,導緻其内部指
	針為nullptr*/
    explicit scoped_ptr( std::auto_ptr<T> p ) BOOST_NOEXCEPT : px( p.release() )
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_scalar_constructor_hook( px );
#endif
    }
 
#endif
	/*析構函數,釋放智能指針持有的資源*/
    ~scoped_ptr() // never throws
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_scalar_destructor_hook( px );
#endif
        boost::checked_delete( px );
    }
};
           

由源碼可知該智能指針由于私有化了拷貝構造函數和operator=指派函數,是以從根本上杜絕了智能指針淺拷貝的發生,是以scoped_ptr也是不能用在容器當中的,如果容器互相進行拷貝或者指派,就會引起scoped_ptr對象的拷貝構造和指派,這是不允許的,代碼會提示編譯錯誤。

auto_ptr和scoped_ptr這一點上的差別:auto_ptr可以任意轉移資源的所有權,而scoped_ptr不會轉移所有權(因為拷貝構造和指派被禁止了)。

unique_ptr

初始化

與shared_ptr不同,unique_ptr沒有定義類似make_shared的操作,是以隻可以使用new來配置設定記憶體,并且由于unique_ptr不可拷貝和指派,初始化unique_ptr必須使用直接初始化的方式。

部分源碼:

template<class _Ty,
	class _Dx>	// = default_delete<_Ty>
	class unique_ptr
		: public _Unique_ptr_base<_Ty, _Dx>
	{	// non-copyable pointer to an object
public:
	typedef _Unique_ptr_base<_Ty, _Dx> _Mybase;
	typedef typename _Mybase::pointer pointer;
	typedef _Ty element_type;
	typedef _Dx deleter_type;

	/*右值引用的拷貝構造函數*/
	unique_ptr(unique_ptr&& _Right) noexcept
		: _Mybase(_Right.release(),
			_STD forward<_Dx>(_Right.get_deleter()))
		{	// construct by moving _Right
		}
	
	/*右值引用的operator=指派重載函數*/
	unique_ptr& operator=(unique_ptr&& _Right) noexcept
		{	// assign by moving _Right
		if (this != _STD addressof(_Right))
			{	// different, do the move
			reset(_Right.release());
			this->get_deleter() = _STD forward<_Dx>(_Right.get_deleter());
			}
		return (*this);
		}

	/*
	交換兩個unique_ptr智能指針對象的底層指針
	和删除器
	*/
	void swap(unique_ptr& _Right) noexcept
		{	// swap elements
		_Swap_adl(this->_Myptr(), _Right._Myptr());
		_Swap_adl(this->get_deleter(), _Right.get_deleter());
		}

	/*通過自定義删除器釋放資源*/
	~unique_ptr() noexcept
		{	// destroy the object
		if (get() != pointer())
			{
			this->get_deleter()(get());
			}
		}
	
	/*unique_ptr提供->運算符的重載函數*/
	_NODISCARD pointer operator->() const noexcept
		{	// return pointer to class object
		return (this->_Myptr());
		}

	/*傳回智能指針對象底層管理的指針*/
	_NODISCARD pointer get() const noexcept
		{	// return pointer to object
		return (this->_Myptr());
		}

	/*提供bool類型的重載,使unique_ptr對象可以
	直接使用在邏輯語句當中,比如if,for,while等*/
	explicit operator bool() const noexcept
		{	// test for non-null pointer
		return (get() != pointer());
		}
    
    /*功能和auto_ptr的release函數功能相同,最終也是隻有一個unique_ptr指針指向資源*/
	pointer release() noexcept
		{	// yield ownership of pointer
		pointer _Ans = get();
		this->_Myptr() = pointer();
		return (_Ans);
		}

	/*把unique_ptr原來的舊資源釋放,重置新的資源_Ptr*/
	void reset(pointer _Ptr = pointer()) noexcept
		{	// establish new pointer
		pointer _Old = get();
		this->_Myptr() = _Ptr;
		if (_Old != pointer())
			{
			this->get_deleter()(_Old);
			}
		}
	/*
	删除了unique_ptr的拷貝構造和operator=指派函數,
	是以不能做unique_ptr智能指針對象的拷貝構造和
	指派,防止淺拷貝的發生
	*/
	unique_ptr(const unique_ptr&) = delete;
	unique_ptr& operator=(const unique_ptr&) = delete;
	};

           

使用

unique_ptr<int> up2 = new int();   //error! 構造函數是explicit
unique_ptr<int> ptr(new int(10));  //ok,直接初始化
unique_ptr<int> ptr1(ptr);//error  不支援拷貝構造
unique_ptr<int>  ptr2=ptr;//error   不支援指派重載

           

雖然unique_ptr删除了拷貝構造函數和指派重載函數,但卻提供了帶右值引用參數的拷貝構造和指派,也就是說,unique_ptr智能指針可以通過右值引用進行拷貝構造和指派操作,或者在産生unique_ptr臨時對象的地方。

unique_ptr<int> ptr(new int);
unique_ptr<int> ptr2 = std::move(ptr); // 使用了右值引用的拷貝構造,其中move()函數ptr提升為右值
ptr2 = std::move(ptr); // 使用了右值引用的operator=指派重載函數
           

unique_ptr最終也是隻能有一個該智能指針引用資源,是以建議在使用不帶引用計數的智能指針時,可以優先選擇unique_ptr智能指針

繼續閱讀