2024/10/25大约 2 分钟
C++单例模式
众所周知,根据创建时机单例模式分为懒汉式和饿汉式,根据线程是否安全分为线程安全和线程不安全。
最近看Cherno的一个讲解视频,对于单例模式有了一个新的认识。Cherno认为,给单例提供一个随时随地的访问接口不利于游戏引擎这种底层框架的设计,会打乱程序的树状架构。
阶段一
对于下面这个单例实现方式,根据静态对象的构造时机以及C++11后局部静态变量在第一次被访问时初始化是线程安全的特性,很容易得到这是饿汉式线程安全的一种实现。但这种方式不能直观地显式创建和销毁单例对象,尤其是当应用程序有多个单例,而且单例间存在依赖关系,需要特定的创建和销毁顺序。
class Singleton
{
public:
    //单例对象创建在全局数据区
	static Singleton GetInstance()
    {
        static Singleton instance;
        return instance
    }
    //单例对象创建在堆,此处引用的静态指针位置在全局数据区
	static Singleton* GetInstance()
    {
        static Singleton* instance = new Singleton();
        return instance
    }
};阶段二
下面这个单例实现方式,将对象的创建和销毁的控制权交给了使用者,更容易掌控单例对象的创建和销毁时机。
class Singleton
{
public:
	static Singleton* GetInstance()
    {
        return instance
    }
    static void Init()
    {
        s_instance = new Singleton();
    }
    static void Shutdown()
    {
        delete s_instance;
        s_instance = nullptr;
    }
private:
    static Singleton* instance;
};
static Singleton* instance = nullptr;此外,Cherno建议将instance指针放在类外,如下:
//Singleton.h
class Singleton
{
public:
	static Singleton* GetInstance();
    static void Init();
    static void Shutdown();
};
//Singleton.cpp
static Singleton* s_instance = nullptr;
void Singleton::Init()
{
	s_instance = new Singleton();
}
void Singleton::Shutdown()
{
	delete s_instance;
	s_instance = nullptr;
}
Singleton* Singleton::GetInstance()
{
	return s_instance;
}此时单例对象的指针处于文件作用域,无法被外部访问,完全被保护起来,但感觉和类内也差不多? 我个人觉得还是放在类内比较好,可以防止命名空间污染,总不能都叫s_instance吧?虽然可以套上不同命名空间,但感觉还是不合适。
如果想让s_instance可以在其他地方访问,将其声明为非静态,然后在访问的地方extern即可:
//Singleton.cpp
Singleton* s_instance = nullptr;
//Main.cpp
extern Singleton* instance;