循环容器和RAII
来源:互联网 发布:支付宝绑定淘宝账户 编辑:程序博客网 时间:2024/06/01 18:05
一.什么是RAII手法?RAII(Resource Acquisition Is Initialization),也称为“资源获取就是初始化,是C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。简单的说,RAII的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。
二.说到RAII不得不提到的一个武器就是智能指针,比较完善的使用引用计数的可以用于STL标准容器的智能指针有boost的智能指针boost::shared_ptr和boost::weak_ptr,现在标准的C++0x也兼容了智能指针tr1::shared_ptr,tr1::weak_ptr。auto_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就可以了。简单粗暴。
- 循环容器和RAII
- RAII和垃圾收集
- C++和RAII
- RAII和RTTI
- RAII
- RAII
- RAII
- RAII
- RAII
- RAII
- RAII
- RAII
- RAII
- RAII
- RAII
- RAII
- RAII和垃圾收集GC
- RAII和垃圾收集(上)
- Java线程(十四):Concurrent包中强大的并发集合类
- webapi
- android surfaceview 播放视频
- 第十五周项目——oj b
- 破解MacOffice软件方法
- 循环容器和RAII
- cocos2d-x 声音和音效
- struts2版本所需的基本jar包
- tableviewcell加载的几种方法
- 黑马程序员——OC语言的ARC机制
- 将web项目以war包形式部署到tomcat中的方法
- iOS 8后系统定位功能的改动
- 第十五周项目——oj c
- Sphinx搜索引擎(2)——CoreSeek