不带引用计数的智能指针


C++库中提供的不带引用计数的智能指针主要包括: auto_ptrscoped_ptrunique_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_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智能指针

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值