循环容器和RAII

        一.什么是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 让它在 client sockfd )析构的时候断开连接,我们在收到某个 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_ 的末尾,这样使得智能指针的引用计数加一,就算是第一个 client SockTag )结点被挤出了容器, client(SockTag) 的引用计数减一(不为 0) 仍然不会析构。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值