@XiangZhou
2015-11-14T02:38:16.000000Z
字数 3086
阅读 2293
c++ 野指针防护
一直从事C++底层库的开发,这里以监听模式来示例野指针的防护。底层通知上层,一种方式是,底层提供一个监听接口类,上层实现,然后注册下来,一般是有注册就有反注册,可以把下层安全压在上层使用者,在释放这个监听接口类之前进行反注册,这个就太不明智,那么我们就需要基于框架设计能防护野指针破坏,这里我们提供一个Guard机制。
`Guard`翻译过来的意思就是`警卫`,顾名思义就是用来防护的。先看其实现:
class Guard{public:Guard(){m_guard = new GuardHelper();m_host = true;}~Guard(){if (m_host) {*(m_guard->m_vaild) = false;}--(*m_guard->m_refCounter);if (*(m_guard->m_refCounter) == 0) {delete m_guard;}}Guard(const Guard& val){m_guard = val.m_guard;++(*m_guard->m_refCounter);m_host = false;}Guard& operator =(const Guard& val){m_guard = val.m_guard;++(*m_guard->m_refCounter);m_host = false;return *this;}operator bool(){return *m_guard->m_vaild;}protected:struct GuardHelper{GuardHelper(){m_refCounter = new int;m_vaild = new bool;*m_refCounter = 1;*m_vaild = true;}~GuardHelper(){delete m_refCounter;delete m_vaild;}int* m_refCounter;bool* m_vaild;};private:GuardHelper *m_guard;bool m_host;};
一个Guard对象正常的通过其构造函数实例化的时候,那我们就认为这个Guard是一个主Guard,m_host设置为true,其实例化一个GuardHelper对象,这个对象有一个引用计数的标识和标识这个主Guard是否有效的标识位。我们再来分析其析构函数,如果是主Guard,其在析构的时候,会将Guard有效标识为设置为false,当引用计数为0时,释放m_guard实例。这里介绍最关键的副Guard概念,借由主Guard构建的Guard都是副Guard,在复执构造函数和赋值中m_host都是false。为了方便使用使Guard可以转化为bool。
其实说到这,我们怎么来防护野指针的思路也就有了,在类型中实例化一个主Guard,在其他需要保存这个指针的地方,保存一份其主Gurad的副Guard,那么在这个指针析构的时候,主Guard也就析构了,那么其他的副Gurad的m_vaild值就为false,那么我们在使用这个指针时就可以知道这个指针已经是野指针了。
那么主Guard该放在那里了,放在库框架的基类再合适不过,这个就可以在整个框架中的指针就行防护。
使用Guard的时候一般都是这样,保存指针和其副Guard。
std::pair<XXX*, Guard>
这种结构过于丑陋,这里我们提供一个包裹类,将其作成一个Guard指针,和平常的指针一样使用。
template <typename T>class GuardPointer{public:GuardPointer(T *p): m_p(p), m_guard(p->getObjectGuard()){}T* operator ->(){return m_p;}T& operator *(){return *m_p;}operator bool(){return m_guard;}private:T *m_p;Guard m_guard;};
使用示例:
#include <iostream>#include "guard.h"#include <list>using namespace std;class Object{public:virtual ~Object(){}Guard getObjectGuard(){return m_guard;}private:Guard m_guard;};class Listener : public Object{public:~Listener(){}virtual void fun() = 0;};class AListener : public Listener{public:~AListener(){}virtual void fun(){std::cout << "AListener fun" << std::endl;}};class BListener : public Listener{public:~BListener(){}virtual void fun(){std::cout << "BListener fun" << std::endl;}};class Notify{public:void doAll(){for (auto ite = m_listeners.begin(); ite != m_listeners.end(); ++ite){if ((*ite)) {(*ite)->fun();}else {std::cout << "guardpointer guard fail!" << std::endl;}}}void registerListener(Listener *listener){m_listeners.push_back(GuardPointer<Listener>(listener));}private:std::list<GuardPointer<Listener>> m_listeners;};int main(){Notify notify;Listener *p1 = new AListener();Listener *p2 = new AListener();Listener *p3 = new BListener();Listener *p4 = new BListener();notify.registerListener(p1);notify.registerListener(p2);notify.registerListener(p3);notify.registerListener(p4);notify.doAll();delete p2;notify.doAll();delete p1;delete p3;notify.doAll();delete p4;notify.doAll();return 0;}
运行结果:
AListener funAListener funBListener funBListener funAListener funguardpointer guard fail!BListener funBListener funguardpointer guard fail!guardpointer guard fail!guardpointer guard fail!BListener funguardpointer guard fail!guardpointer guard fail!guardpointer guard fail!
对上层的野指针就行了防护,使用Guard的唯一一个需要特别注意的就是一定要值传递,不然你会挂的很惨。