《我的泛型编程观》之boost.scoped_ptr、scoped_array源码分析

 http://hi.baidu.com/_%E2d_%B7%B3_%DE%B2%C2%D2/blog/item/1d7739d92055bb2610df9b5c.html

 

 

《我的泛型编程观》之boost.scoped_ptr、scoped_array源码分析
2009年12月06日 星期日 下午 06:57

      boost.scoped_ptr已经被tr2建议作为C++标准库的一部分,它的兄弟shared_ptr已经通过tr1即将成为C++标准库的一部分了。boost.scoped_ptr干着和std::auto_ptr几乎一样的事情,但是它们却又本质上的差别。std::auto_ptr具有权限转移的特点,也就是说当你使用一个auto_ptr来构造另外一个auto_ptr的时候,对资源的控制权即发生了转移,这可以通过auto_ptr的源码来看,因为“源码之下,了无秘密”嘛:
template<class _Other>
   auto_ptr<_Ty>& 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
   }
auto_ptr<_Ty>& operator=(auto_ptr<_Ty>& _Right) _THROW0()
   { // assign compatible _Right (assume pointer)
   reset(_Right.release());
   return (*this);
   }

release的定义如下:
_Ty *release() _THROW0()
   { // return wrapped pointer and give up ownership
   _Ty *_Tmp = _Myptr;
   _Myptr = 0;
   return (_Tmp);
   }

       正是由于这个原因使得std::auto_ptr变得不伦不类,使得它的应用领域变得狭隘了很多。boost.scoped_ptr起着类似的作用,只不过你根本无法用“纯天然”的方法像auto_ptr一样夺走它的资源控制权,因为对于scoped_ptr来说,拷贝构造函数和赋值运算符重载都是私有的,况且还没有实现。

template<class T> class scoped_ptr // noncopyable
{
private:
     T * px;
     scoped_ptr(scoped_ptr const &);
     scoped_ptr & operator=(scoped_ptr const &);
     typedef scoped_ptr<T> this_type;
     void operator==( scoped_ptr const& ) const;
     void operator!=( scoped_ptr const& ) const;

在最新的1.39版本中,甚至判等和不等已经被禁止了。

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
     explicit scoped_ptr( std::auto_ptr<T> p ): px( p.release() ) // never throws
     {
#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 );
     }
     void reset(T * p = 0) // never throws
     {
         BOOST_ASSERT( p == 0 || p != px ); // catch self-reset errors
         this_type(p).swap(*this);
     }
     T & operator*() const // never throws
     {
         BOOST_ASSERT( px != 0 );
         return *px;
     }
     T * operator->() const // never throws
     {
         BOOST_ASSERT( px != 0 );
         return px;
     }
     T * get() const // never throws
     {
         return px;
     }
// implicit conversion to "bool"
#include <boost/smart_ptr/detail/operator_bool.hpp>
     void swap(scoped_ptr & b) // never throws
     {
         T * tmp = b.px;
         b.px = px;
         px = tmp;
     }
};
template<class T> inline void swap(scoped_ptr<T> & a, scoped_ptr<T> & b) // never throws
{
     a.swap(b);
}

这些就是它剩下的内容,可见它就是一个RAII的实践,自动帮我们管理了动态分配的内存这么一个作用。
scoped_ptr的实现其实蛮简单的,唯一值得一提的删除部分的那一句话。
         boost::checked_delete( px );
通过查看定义和实现我们可以知道它的实现如下:
template<class T> inline void checked_delete(T * x)
{
     // intentionally complex - simplification causes regressions
     typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
     (void) sizeof(type_must_be_complete);
     delete x;
}

这样做有什么意义呢?实际上它起着一个静态断言的作用,可以防止delete一个具有非平凡析构函数的类指针。
如果你觉得这句话很拗口的话,可以编译一下下面这个程序:
#include <iostream>
using namespace std;
class CTest;
void deletesometh( CTest * p )
{
delete p;
}

int main()
{
deletesometh( (CTest*)0 );
return 0;
}

看看它的给的警告:
warning C4150: 删除指向不完整“CTest”类型的指针;没有调用析构函数
1>         e:\documents\visual studio 2008\projects\boost\main.cpp(5) : 参见“CTest”的声明

MSDN的解释如下:
错误消息删除指向不完整“type”类型的指针;没有调用析构函数

调用 delete 运算符来删除已声明但未定义的类型,这样编译器不能发现析构函数。
下面的示例生成 C4150:
// C4150.cpp// compile with: /W2class   IncClass;void NoDestruct( IncClass* pIncClass ){   delete pIncClass;} // C4150, define class to resolveint main(){}
好了,checked_delete的作用就是把这个警告无运行时成本的转换成错误,以防止潜在的错误。因为对于库开发者来说,你永远都不知道用户最终会用什么来具现化你的模板。
编译下面的代码你会得到一系列的错误信息:
#include <iostream>
#include <boost/checked_delete.hpp>
using namespace std;
class CTest;
void deletesometh( CTest * p )
{
// delete p;
boost::checked_delete( p );
}

int main()
{
deletesometh( (CTest*)0 );
return 0;
}

比如说这样的错误信息:
error C2027: 使用了未定义类型“CTest”
1>         e:\documents\visual studio 2008\projects\boost\main.cpp(5) : 参见“CTest”的声明
1>         e:\documents\visual studio 2008\projects\boost\main.cpp(10): 参见对正在编译的函数 模板 实例化“void boost::checked_delete<CTest>(T *)”的引用
1>         with
1>         [
1>             T=CTest
1>         ]
1>d:\boost\include\boost\checked_delete.hpp(32) : error C2118: 负下标
1>d:\boost\include\boost\checked_delete.hpp(34) : warning C4150: 删除指向不完整“CTest”类型的指针;没有调用析构函数
1>         e:\documents\visual studio 2008\projects\boost\main.cpp(5) : 参见“CTest”的声明

在Effective STL中Meyer曾经告诫过我们不要使用std::auto_ptr来作为容器的元素,其原因就是其资源控制权的转移特性,然而对于scoped_ptr则不会有这样的问题,因为你使用scoped_ptr来作为容器的元素实际上意义并不大,而且你都没有办法直接替换移动元素等等,比如下面的代码会导致编译错误:
boost::scoped_ptr< string > sp1( new string("haha") );
boost::scoped_ptr< string > sp2( new string("hehe") );
vector< boost::scoped_ptr< string> > vec;
vec.push_back( sp1 );

如果你非要使用某种智能指针来作为容器元素的话,请选择shared_ptr。

对于scoped_array来说,大多数时候我们都不需要选择它,因为vector是一个更好的选择。scoped_array的好处是不会有多余的开销,其实现和scoped_ptr极其类似,区别在于卸载部分和提供operator []等的支持等等。

在他们的实作中有这样的一段处理指令:
#include <boost/smart_ptr/detail/operator_bool.hpp>
这主要是提供智能指针向bool值隐式转换的跨平台支持。

 

你可能感兴趣的:(《我的泛型编程观》之boost.scoped_ptr、scoped_array源码分析)