现在的位置: 首页 > 综合 > 正文

Singleton(一)

2018年03月30日 ⁄ 综合 ⁄ 共 3123字 ⁄ 字号 评论关闭

1 用以支持 Singleton 的一些 C++ 几基本手法

// Header file Singleton.h
class Singleton
{
public:
	static Singleton* Instance()
	{
		if (!pInstance_)
			pInstance_ = new Singleton;
		return pInstance_;
	}
private:
	Singleton();
	Singleton(const Singleton&);
	static Singleton* pInstance_;
};
// Implementation file Singleton.cpp
Singleton* Singleton::pInstance_ = 0;

问题:如果客户端(调用端)使用 delete 删除了 Singleton 怎么办?

// Header file Singleton.h
class Singleton
{
public:
        // 传回引用而非指针避免客户端 delete Singleton 对象,这样比较安全些
	static Singleton& Instance()
	{
		if (!pInstance_)
			pInstance_ = new Singleton;
		return *pInstance_;
	}
private:
	Singleton();
	Singleton(const Singleton&);
	static Singleton* pInstance_;
};
// Implementation file Singleton.cpp
Singleton* Singleton::pInstance_ = 0;

2 另一种方式构造 Singleton 

// Header file Singleton.h
class Singleton
{
public:
	static Singleton* Instance()
	{
		return &instance_;
	}
        int Dosomething()
        {
        // ....
        }
private:
	//....
	static Singleton instance_;
};
// Implementation file Singleton.cpp
Singleton Singleton::instance_;

问题:Singleton::instance_静态对象的初始化是否优先于使用者对象的调用?

// SomeFile.cpp
#include "singleton.h"
int global = Singleton::Instance()->Dosomething(); // 这时,Singleton::instance_对象已经初始化了吗?

面对不同编译单元(c++源文件)中动态初始化对象,C++并未定义其间的初始化顺序。

由于无法确保编译器一定先将instance_初始化,所以全局变量 global 的初值设定中对 Singleton::Instance的调用有可能传回一个尚未构造的对象。这意味着无法保证任何外部对象所使用的 instance_ 是一个已被正确初始化的对象。

3 保证 “Singleton的唯一性“

class Singleton
{
public:
	static Singleton& Instance();// unique point of access
        //....
private:
	Singleton();
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
	~Singleton();</strong>
};

4 Singleton 应该在什么时候摧毁自身实体?

Singleton 未被删除,也不会造成内存泄露,因为singleton并非累积性数据,到进程结束仍然保存着一份实体。

然而,泄露是存在的,而且更隐蔽更有害,那就是资源泄露。这是因为 Singleton 构造函数可以索求广泛的资源:网络连接、OS互斥体和进程通讯方法中的各种handles等等。

避免资源泄露的唯一正确方法是在程序关闭时期删除 Singleton 对象。问题在于我们必须谨慎选择删除时机,确保 Singleton 对象被摧毁后不会再有任何人取用它。

// Meyers Singleton (由 Scott Meyers 最先提出)
class Singleton
{
public:
	static Singleton& Instance() )
	{
		static Singleton obj; // 使用局部的静态变量,函数内的 static 对象在该函数第一次执行时被初始化(即执行期才初始化的 static 变量)
		return obj;
	}
private:
	Singleton();
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
	~Singleton();
};

 ”执行期才初始化“的 static 变量 和 ”通过编译器常量加以初始化“ 的基本型 static 变量的区别?

int Fun()
{
    static int x = 100; // 这是“通过编译期常量加以初始化”
    return ++x;
}

这种情况下 x 的初始化会在程序中的任何一行被执行之前完成,而且通常是在程序装在期间。 Fun() 第一次被调用前, x 早就被设为100了。如果初始值不是一个编译期常量,抑或静态变量是个拥有构造函数的对象,那么变量的初始化将发生于执行期,也就是跟第一次行经其定义式之时。

此外,编译器会产生一些代码,使得初始化之后,执行期相关机制会登记需被析构的变量(拥有构造函数的静态对象和全局对象)。这些代码的 C++ 伪代码看起来像下面这样:

static Singleton& Instance() 
{
        // Functions generated by the compiler
        extern void __ConstructSingleton(void* memory);
        extern void __DestroySingleton();
        // variable是generated by the compiler
        static bool __initialized = false;
        // Buffer that holds the singleton
        // (We assume it is properly aligned)
        static char __buffer[sizeof(Singleton)];
        if (!__initialized)
        {
                // First call, construct object
  		// Will invoke Singleton::Singleton
 		__ConstructSingleton(__buffer);
		// register destruction
 		atexit(__DestroySingleton);
		__initialized = true;
         }
	return *reinterpret_cast<Singleton*>(__Object);
}

atexit 是由标准C 程序库提供,让你得以注册一些在程序结束之际自动被调用的的函数,且被调用次序为后进先出(LIFO。C++对象以LIFO方式进行,县产生的对象后摧毁你。当然,以 new 和 delete 管理的对象不遵守这一规则)。

编译器自动产生出 __DestroySingleton 函数(它被执行后会摧毁  __buffer 内存内的 Singleton 对象)并将其地址传给 atexit()。
atexit 函数的声明:int atexit(void (*pFun)());

atexit 如何运作? 每次被调用,它的参数会被压入 C runtime library 所维护的一个私有 stack 内。程序结束之际,执行期相关机制便会调用那些经由 atexit() 登记的函数。

Meyer singleton 提供的是"程序结束之际摧毁 Singleton"最简单的方式。

抱歉!评论已关闭.