单件模式在设计模式中是比较基本的模式之一,它是用来构造在整个应用中只有唯一实例的对象。今天在CODE的过程中采用单件模式实现了一个类,大至如下:
class Device: public BaseDevice
{
private:
int m_port;
...
private:
Device();
Device& operator=(const Device&);
static Device* _instance;
...
public:
DWORD Initialize();
~Device();
public:
static Device* Instance();
};
Device *Device::_instance = NULL;
Device::Device()
{ ... }
Device::~Device()
{ }
DWORD Device::Initialize()
{ ... }
Device* Device::Instance()
{
if(NULL==_instance)
{
_instance=new Device();
}
return _instance;
}
这样一个简单的单件实例类就实现好了,那么它的实例在应用过程中就是唯一的了吗?当然是的。单件模式要实现目标就是这样。但我在编程过程中遇到了一个问题——上面的代码存在着BUG:当对单件对象delete后然后再获取实例并调用其的方法时出现访问地址错误?
Device*dv=Device::Instance();
if(dv)
dv->Initialize();
...
delete dv;
dv=NULL;
...
dv=Device::Instance();
if(dv)
dv->Initialize(); //在这里调用出错
如果只看这段代码很难察觉到错误,因为这些都是正常的。DEBUG了半天才找到了错误的根源——错误还是出在单件类的设计和实现上。为了使对象的实例唯一,我们使类的构造函数私有,公开类静态成员函数获得实例,对象的实例保存在类的静态成员变量中。客户端每次调用Instance返回的是这个静态成员变量。那为什么在delete之后重新获得实例却出现访问地址错误呢,明明看到第二个dv指针不是NULL值啊?问题就出在析构函数那:没有对静态变量_instance=NULL。因为在执行delete操作时,其实对象的实例已经得到释放了,只是这时的_instance指针没有赋NULL值,依然指向最初创建对象的那个地址!这就是所谓的“野指针”吧。
Device::~Device()
{
Device::_instance=NULL;
}
更进一步的想,在单件模式的设计应该也分几种情况。像上面那种方式,实例是唯一,但可以被应用端随时释放并再创建,另一种我想是把析构函数也私有化,这样应用端就不能释放,但是怎么创建呢?这就需要配合其它的设计模式吧,比如抽象工厂,专门负责对象的创建和释放。