全局变量的问题:
- 变量名冲突:这个问题会使项目管理成本大幅增加。项目经理必须小心地维护变量命名规则,所有工程师在开发代码时,每遇到一个全局变量,都必须仔细分辨该变量究竟属于哪个模块、哪个程序
- 耦合度难题:全局变量实际上大大增加了函数和模块之间的耦合度。用通俗的话说,需要访问某个特定全局变量的多个函数被变量牢牢的“粘结”在一起,成为拆不散的一团乱麻。
- 单个实例问题:全局变量不能阻止程序员定义一个类的多个对象。如果没有其他技术手段的帮助,保证一个类只有单个实例就全靠程序员的自觉了。在一个多人参与、并行开发的大型项目中,这也会增加很多管理上的负担。
- 初始化顺序:全局变量不可能保证相互之间遵循特定的初始化顺序,这完全有编译器决定。对于简单变量来说,这没什么麻烦的,但是对于类的对象实例,构造函数被调用的顺序有时就显得非常重要。
- 多线程访问:当多个并发的线程都需要访问某些全局变量时,我们必须使用各种同步机制,小心地保护这些全局变量,以免陷入并发冲突的泥潭
单件模式
保证一个类只有一个对象实例,并提供一个访问该对象实例的全局访问点。
这句话的意思是说,如果我们想实现一个全局范围可见的对象以替代麻烦缠身的的全局变量,那么,最好的做法是将数据封装在一个特殊的类中,这个类严格管理数据的创建过程以保证数据的唯一性,同时不允许随意创建该类的对象实例(这可以通过将类的构造函数声明为private或protect类型来实现)。虽然不能通过类的构造函数获得对象实例,但还是可以从该类的静态成员函数得到该类唯一的对象实例的指针或引用。
一个简单实现
.h
class CSingleton
{
public:
static CSingleton *GetSingleton();
protected:
CSingleton();
private:
static CSingleton * m_pSingleton;
};
.cpp
CSingleton::CSingleton()
{
}
CSingleton * CSingleton::m_pSingleton = nullptr;
CSingleton * CSingleton::GetSingleton()
{
//GetSingleton第一次被调用时,CSingleton唯一的对象实例被创建出来。
//此后,GetSingleton函数每次被调用时,代码都会返回这唯一实例的指针,
//而不会创建新的对象。这一方法保证了实例对象的唯一性。
if (!m_pSingleton)
{
m_pSingleton = new CSingleton();
}
return m_pSingleton;
}
单件类的内存释放
-
在这里,有必要澄清一个概念:并不是所有没有释放的内存都对程序有害。《Effective C++》一书中提到:对一个程序来说,如果某些内存的分配数量有一个最大值,程序运行中所分配的内存总量总不会超出该最大值,这些内存就可以被视为内存池,不释放内存池的空间,并不会对整个系统造成伤害,因为它不会因无限增长而耗尽系统内存;如果在程序运行过程中,某些动态分配的内存空间会无限增长,就算客户代码非常小心,也无法避免内存耗尽,这些内存被称为“内存泄露”。相比之下,内存池并没有太大的危害,而内存泄露则必须严格防范。
-
但是在某些特定的场合下,我们还是需要认真释放单件类的对象实例。典型的情况是,如果单件类的构造函数除了分配内存意外,还要分配某些系统资源,而这些资源也不会随着进程的结束而自动释放(例如在单件类初始化时打开了一个外设,或在数据库中创建了一张临时表),那么,在不进行完全清理的前提下,程序的反复运行就有可能造成系统资源的耗尽或后续访问时的冲突。
我们可以利用C语言运行库提供的atexit()函数,把静态释放成员函数设置为程序结束时的响应函数。这样,程序结束时,运行库就会依次调用所有的释放函数了。实现如下:
.h
class CSingleton
{
public:
static CSingleton *GetSingleton();
static void DeleteSingleton();
protected:
~CSingleton(){};
CSingleton();
private:
static CSingleton * m_pSingleton;
};
.cpp
CSingleton::CSingleton()
{
}
void CSingleton::DeleteSingleton()
{
if (m_pSingleton)
{
delete m_pSingleton;
}
}
CSingleton * CSingleton::m_pSingleton = nullptr;
CSingleton * CSingleton::GetSingleton()
{
if (!m_pSingleton)
{
m_pSingleton = new CSingleton();
atexit(CSingleton::DeleteSingleton);
}
return m_pSingleton;
}
参考资料:道法自然-面向对象实践指南