@XiangZhou
2015-11-14T10:38:16.000000Z
字数 3086
阅读 2081
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 fun
AListener fun
BListener fun
BListener fun
AListener fun
guardpointer guard fail!
BListener fun
BListener fun
guardpointer guard fail!
guardpointer guard fail!
guardpointer guard fail!
BListener fun
guardpointer guard fail!
guardpointer guard fail!
guardpointer guard fail!
对上层的野指针就行了防护,使用Guard的唯一一个需要特别注意的就是一定要值传递,不然你会挂的很惨。