单例模式(正式)
当需要严格控制一个类在全局只有唯一一个实例,并且可以随时调用它的时候,就可以使用单例模式。
单例模式可以以实例化时间分为懒汉式和饿汉式、以C实现方式分为C++98的实现以及C++11的实现。
懒汉式
c++11 class Singleton { public: static Singleton* getInstance() { if(m_instance == nullptr) { m_instance = new Singleton(); } return m_instance; } private: Singleton(){} ~Singleton(){} static Signeton* m_instance; };
class Singleton { public: static Singleton& GetInstance() { static Singleton instance; return instance; } private: Singleton() = default; ~Singleton() {} };
也就是说, Singleton 通过将构造函数私有化实现了对实例化的控制,只有通过调用 getInstance() 才能生成并获得这唯一一个实例。
同时,这是一个懒汉式的实现,只有在调用 getInstance() 一次过后才有一个 Singleton 被实例化。
代码注解:
-
这段代码使用指针的目的是为了延迟单例类的实例化,并在需要时动态地创建和访问实例对象。
-
通过使用 new 运算符创建一个 Singleton 类的实例对象,并将其赋值给静态成员 m_instance,以确保只有一个实例对象被创建。需要注意的是,静态成员 m_instance 存储的是实例对象的地址,而不是对象本身。因此,在使用时需要通过解引用操作符 * 来获取实际的对象。
静态成员变量定义只能在源文件:
重复编译 减少初始化操作
静态成员:
-
如果把类的成员声明为静态的,就可以把它与类的对象独立开来(静态成员不属于对象)
-
用 static 关键字把类的成员变量声明为静态,表示它在程序中(不仅是对象)是共享的。
-
静态成员只有一份,在全局存储区
-
静态成员使用类名加范围解析运算符 :: 就可以访问,不需要创建对象。
饿汉式
// in 'Singleton.h' class Singleton { public: static Singleton& GetInstance { return instance; } private: Singleton() = default; ~Singleton() {} static Singleton instance; }; // in 'Singleton.cpp' Singleton Singleton::instance;
代码讲解
-
GetInstance() 方法返回一个 Singleton 对象的引用。
-
在 C++11 中,使用局部静态变量可以确保只有一个实例对象被创建。在这段代码中,instance 是一个局部静态变量,它在第一次调用 GetInstance() 方法时被创建,而在后续的调用中会直接返回已经存在的实例对象。
-
构造函数 Singleton() 和析构函数 ~Singleton() 都使用 default 关键字进行了默认定义,即使用编译器生成的默认实现。它们被声明为 private,防止在类外部直接创建和销毁对象。
-
通过这种实现方式,可以简洁地实现只有一个实例对象被创建,并且可以通过引用来访问该对象。
懒汉模式和恶汉模式是两种单例模式实现方式的区别:
懒汉模式:在首次使用时才创建实例对象,延迟实例化,可能存在线程安全问题。
恶汉模式:在类加载时就创建实例对象,立即实例化,不存在线程安全问题。
懒汉模式节省内存空间,但可能引入延迟和线程安全问题;恶汉模式简单直接,但可能浪费部分内存空间。选择取决于内存占用、延迟和线程安全需求。
竞争的场景:
两个线程在调用结构的过程中,如判断是否空指针的时候,一个在判断为空指针后准备创建对象,另一个在此时也判断为空的指针
//Singleton.h /* C++98 */ class Singleton11{ public: static Singleton98* getInstance() { if(m_instance == nullptr) { std::lock_guard<std::mutex> lock(m_mutex); if(m_instance == nullptr) { m_instance = new Singleton(); } } return m_instance; } private: //... } /* C++11 */ class Singleton11{ public: static Singleton11& getInstance() { std::call_once(m_flag, []() { m_instance.reset(new Singleton11); }); } private: //... static std::mutex m_mutex; } //Singleton.cpp /* C++98 */ Singleton98* Singleton98::m_instance = nullptr; std::mutex Singleton98::m_mutex; /* C++11 */ std::unique_ptr<Singleton11> Singleton11::m_instance; std::once_flag Singleton11::m_flag;
问题:
-
多线程需要考虑多次new的问题,需要使用线程同步如互斥锁
-
忘记delete!
用处
资源共享:当多个对象需要共享同一个资源时,可以使用单例模式来管理该资源。例如,数据库连接池、线程池等。
配置信息:单例模式可以用于管理全局的配置信息,确保在整个应用程序中只有一个配置对象,方便访问和修改配置。
日志记录器:在日志记录中,使用单例模式可以确保只有一个日志记录器实例,所有的日志信息都在同一个地方进行记录,并且可以方便地访问和控制日志记录器。
本文作者:Gal0721
本文链接:https://www.cnblogs.com/Gal0721/p/17726168.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步