循环容器和RAII

来源:互联网 发布:支付宝绑定淘宝账户 编辑:程序博客网 时间:2024/06/01 18:05

        一.什么是RAII手法?RAII(Resource Acquisition Is Initialization),也称为资源获取就是初始化,C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。简单的说,RAII的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。

       二.说到RAII不得不提到的一个武器就是智能指针,比较完善的使用引用计数的可以用于STL标准容器的智能指针有boost的智能指针boost::shared_ptrboost::weak_ptr,现在标准的C++0x也兼容了智能指针tr1::shared_ptrtr1::weak_ptrauto_ptr是不能用于STL标准容器的,所以我的经验是如果用智能指针实现RAII手段最好不要用auto_ptr,智能指针的主要做用既程序员不需要手动去释放new出来的类,当智能指针退出它的作用域的时候会自己去析构,至于智能指针具体的细节我在这里不做赘述,很多资料有详细的介绍。

   三.在有些情况下我们可能会用到循环容器,既这样一个容器。它里面可以存放n个指定的元素,你不需要手动去删除多余的元素,当元素个数超过n的时候会删除最先插入的多余的节点。顺序是先进先出,boost库中有提供现成的循环容器,标准的STL中没有现成的循环容器,不过你可以自己去实现。下面是我实现的一个循环容器的代码:

#include <iostream>#include <deque>using namespace std;template <typename T>class Circular{public:    Circular(int iSize)        :iSize_(iSize)        ,iNow_(0)    {         list_.clear();    }    ~Circular()    {         list_.clear();         iNow_ = 0;    }    void push(T node)    {        list_.push_back(node);        ++iNow_;        if(iNow_>iSize_)        {            list_.pop_front();            --iNow_;        }    }    void pop(void)    {        if(!list_.empty())        {            list_.pop_front();            --iNow_;        }    }    bool empty(void)    {        return list_.empty();    }    bool full(void)    {        return (iNow_==iSize_);    }    const T& front(void)    {        return list_.front();    }    const T& back(void)    {        return list_.back();    }    const T& at(const int& iNode)    {        return list_.at(iNode);    }    void clear(void)    {        list_.clear();    }    const int& size(void)    {        return static_cast<const int>(list_.size());    }    const int& capacity(void)    {        return iSize_;    }    typename deque<T>::iterator find(T& node)    {        return list_.find(node);    }    typename deque<T>::iterator begin(void)    {        return list_.begin();    }    typename deque<T>::iterator end(void)    {        return list_.end();    }private:    const int iSize_;    int iNow_;    deque<T> list_;};
    四.循环容器,智能指针,RAII这三者在一起我们能做什么呢?好吧,那我们用一个具体的实例来看看吧,说不定你会惊叹它的强大。现在我们来实现这样一个情景,我们现在实现了一个服务器,服务器要求在30秒内没有收到一个客户端连接的数据就自动断开这个连接。如果是你,你会怎么做。如果是我,我会这样做。
    (1)、定义一个客户端socket类:

class client{   public:   client(int sockTag)       :sockfd(sockTag)   {     }client()   :sockfd(0){}~client(void){   if(sockfd > 0)   {       shutdown(sockfd);//关闭客户端连接    }}int GetSock(void){     Return sockfd;}private:    int  sockfd;//客户端连接套接字}

    (2)、定义一个以client的智能指针为结点的循环容器:

Circular<std::tr1::shared_ptr<client> > m_Wheel_(30);void AddClock(){std::tr1::shared_ptr<client> pCLient(new client());m_Wheel_.puhs(pClinet);}void AddClock(int sockTag){bool bFind_ = false;std::tr1::shared_ptr<client> pClient;if(sockTag>0){    for(int i=0;i<m_Wheel_.size();i++){     pClient = m_Wheel_.at(i);     if(pClient->GetSock() == SockTag){    bFind_ = true;    break;}}if(bFind_ ){    m_Wheel_.push(pClient);}else{    std::tr1::shared_ptr<client> pTemp(new client(SockTag));    if(pTemp)    {        m_Wheel_.push(pTemp);}}}  }

    (3)、每当有接收到数据时执行如下操作:

void OnRecevied(void *pData,const int nData, int SockTag){     ………     AddClocK(SockTag);}

     (4)、开启一个1秒的定时器:

Timer  m_timerClient;m_TimerClient.start(1*1000);void TimerCallBack(void){//定时器的回调函数     AddClock();}

    OK!大功告成。如果不明所以的同志我可以粗略的讲一下流程,首先我们用RAII的手法封装socket让它在clientsockfd)析构的时候断开连接,我们在收到某个socket的数据之后把client结点放在m_Wheel_容器中,这个client结点在什么时候析构呢?当它从m_Wheel_容器中被删除的时候。我们在程序中开了一个定时器,它的任务就是每隔一秒钟都会去往m_Wheel_中增加一个无用的client()结点。假如在第一次收到数据之后的30秒钟之内没有再收到该socket的数据,那么定时器再这段时间里会插入30个无效的client()结点,而之前的client(SockTag)结点就会被挤出循环容器,从而导致client(SockTag)的析构,关闭socket连接。然而一旦在这个时间段内有新的数据到来那么先查找m_Wheel_中有没有已经包含了该socket的client(SockTag)结点如果有的话,把这个结点再插入到m_Wheel_的末尾,这样使得智能指针的引用计数加一,就算是第一个clientSockTag)结点被挤出了容器,client(SockTag)的引用计数减一(不为0)仍然不会析构。

    怎么样,这样的设计是不是很酷,逻辑上十分简单,也不用太多的代码量,更不用担心内存的泄露。关键是不用程序员自己去管理socket的生命周期,循环容器自己就管理好了,如果想要延长心跳的判别时间只需要增加循环容器的size就可以了。简单粗暴。

0 0
原创粉丝点击