smart_ptr之shared_ptr(二)

前面已经介绍了shared_ptr的基本使用,这篇文件主要介绍它的定制器和删除器的部分功能。

在一定程度上,删除器使用的概率会大很多。

在shared_ptr中有一个构造函数是这样的:

 template<class Y, class D> shared_ptr(Y * p, D d): px(p), pn(p, d)
    {
        boost::detail::sp_enable_shared_from_this( this, p, p );
    }

其中的D就是删除器,被传给了引用计数对象,然后引用计数对象在构造自己的时候,把删除器又传递给了具体的计数管理对象,最终在释放资源的时候使用。

对于删除器需要满足的要求是可以实现D(p)即可,可以是普通的函数(资源管理),可以是delete运算符,可以是函数对象,有一个参数是p都可以行。

举例代码如下:
(1)使用函数对象,实现文件资源句柄的关闭

class FileCloser 
{
    public:   
        void operator()(FILE* file) 
        {    
            std::cout << "The FileCloser has been called with a FILE*, "
                "which will now be closed.\n";    
            if (file!=0)       
                fclose(file);  
        }
};


int main() 
{    
     std::cout <<     "shared_ptr example with a custom deallocator.\n"; 
     {    
         FILE* f=fopen("test.txt","r");    
         if (f==0) 
         {      
             std::cout << "Unable to open file\n";      
             throw "Unable to open file";    
         }    
         boost::shared_ptr<FILE> my_shared_file(f, FileCloser()); 
         fseek(my_shared_file.get(),42,SEEK_SET);  
     }  
     std::cout << "By now, the FILE has been closed!\n";

    return 0;
}

在自己工程的目录下建立一个test.txt文件即可运行。

结果如下:


可以看到函数对象正确的调用,并且进行了相关的资源处理,对于数据库连接资源等都可以这样进行管理。

其实上面也可以不用使用自定义删除器,直接使用相关的fclose函数也是可以的,这个时候就是使用函数指针,执行相关的文件关闭操作。

int main() 
{    
     std::cout <<     "shared_ptr example with a custom deallocator.\n"; 
     {    
         FILE* f=fopen("test.txt","r");    
         if (f==0) 
         {      
             std::cout << "Unable to open file\n";      
             throw "Unable to open file";    
         }    
         boost::shared_ptr<FILE> my_shared_file(f, &fclose); 
         fseek(my_shared_file.get(),42,SEEK_SET);  
     }  
     std::cout << "By now, the FILE has been closed!\n";

    return 0;
}

传递一个函数指针进去即可。


关于分配器,因为在自定义上可能需要和stl的分配器模板适配,我自己学习的不是很好,故不在这里举例说明!

后期在补上。


补充知识:如何更安全的使用定制删除器。

编程的一个思想:当我们把基类的析构函数设定为protected的时候,delete操作符是不能直接作用于基类的指针,这样就可以保证在shared_ptr中利用基类类型保存派生内对象的时候,可以防止用户执行delete smaret.get()的操作。

那么如果想安全的使用删除器,我们也可以这么来做,声明析构函数为 protected (或 private) 并使用一个定制删除器来负责销毁对象。这个定制删除器必须是它要删除的类的友元,这样它才可以工作。封装这个删除器的好方法是把它实现为私有的嵌套类,如下例所示:

class A {  
    class deleter 
    {    
    public:      
        void operator()(A* p) 
        {        
            delete p;
        }  
    };  
    friend class deleter;
public:  
    virtual void sing() 
    {    
        std::cout << "Lalalalalalalalalalala"; 
    }  
    static boost::shared_ptr<A> createA() 
    {    
        boost::shared_ptr<A> p(new A(),A::deleter());    
        return p;  
    }
protected:  virtual ~A() {};
};
int main() 
{  
    boost::shared_ptr<A> p=A::createA();
    delete p.get(); //会报编译时错误,无法使用析构函数
    return 0;
}


上面就保证了,资源只能有自己的定制器删除,保证了安全性。


如何实现利用shared_ptr保存对象本身<实质就是保存this指针>

可能刚开始的初始实现如下:

class test

{

    public:

         shared_ptr<test> getThis()

         { return shared_ptr<this>(this);}

}


int main( void )

{

    shared_ptr<test> a( new test());

    shared_ptr<test> p = a->getThis();

    return 0;

}

上面的实现感觉上是正确的,实质在运行的时候会出现错误,因为this指针给两个智能指针进行了管理,一个是a,一个是p,那么指针就会被删除两次,因为出现了利用“裸指针”初始化两个智能指针的错误。

那么如何解决这个问题了,在boost库中就提供了该机制,利用enable_shared_from_this

class A1 : public boost::enable_shared_from_this<A1>
{
public:
    A1()
    { 
        std::cout << "A1" << std::endl;
    }

    ~A1() { std::cout << "~A1"<< std::endl; }
    boost::shared_ptr<A1> get()
    {
        return shared_from_this();
    }
};

int main() 
{  
    boost::shared_ptr<A1> p( new A1 ); // this 1
    boost::shared_ptr<A1> q = p->get(); // this 1
    std::cout << p.use_count() << std::endl;
    std::cout << q.use_count() << std::endl;
    return 0;
}


实现的基本原理是:利用了weak_ptr保存了这个this指针,然后再通过weak_ptr构造一个新的shared_ptr,而不是用“裸指针”,因为weak_ptr不会影响引用计数,这样所有的智能指针就维护同一个指针对象。

构造的过程如下:

第一部:构建智能指针

    template<class Y>
    explicit shared_ptr( Y * p ): px( p ), pn( p ) // Y must be complete
    {
        boost::detail::sp_enable_shared_from_this( this, p, p );
    }

二:调用boost::detail::sp_enable_shared_from_this的重载函数

template< class X, class Y, class T > inline void sp_enable_shared_from_this( boost::shared_ptr<X> const * ppx, Y const * py, boost::enable_shared_from_this< T > const * pe )
{
    if( pe != 0 )
    {
        pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) );
    }
}

三、调用boost::enable_shared_from_this的_internal_accept_owner( ppx, const_cast< Y* >( py ) )

实现代码:

 template<class X, class Y> void _internal_accept_owner( shared_ptr<X> const * ppx, Y * py ) const
    {
        if( weak_this_.expired() )
        {
            weak_this_ = shared_ptr<T>( *ppx, py );
        }
    }


mutable weak_ptr<T> weak_this_;

这样就实现了weak_ptr指针的生成,并且保存了相关的this指针。

四:调用 shared_from_this()得到了共享指针,从weak_ptr,而不是“裸指针”

shared_ptr<T> shared_from_this()
  {
        shared_ptr<T> p( weak_this_ );
        BOOST_ASSERT( p.get() == this );
        return p;
  }

总的来说:即使shared_ptr在构造自己的时候,就已经在适当的时候构建了相关的weak_ptr,然后再需要的时候返回了自己。所以这个方法是一个比较好的解决方法。

一定要记住:不能直接用this初始化shared_ptr哦,那样很容易出错了!

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值