【C++学习笔记】十二、单例模式详解
1 单例模式
在C++中,单例模式是一种常用的设计模式,用于确保一个类只有一个实例,并提供全局访问点。常见的C++单例模式包括以下几种:
- 饿汉式单例模式:在程序启动时就创建单例实例,以保证线程安全和性能。在该模式下,getInstance()方法直接返回已经初始化好的单例实例。
- 懒汉式单例模式:在第一次请求时才创建单例实例,以节省资源。在单线程环境下可以使用简单的实现方式,但在多线程环境下需要考虑线程安全问题。
- 双检锁/双重校验锁(Double-Checked Locking)单例模式:结合了懒汉式和饿汉式单例模式的优点,即在第一次请求时检查实例是否存在,如果不存在则加锁进行同步,在加锁之前再次检查实例是否存在,以避免不必要的加锁操作。
- 内部静态变量单例模式:定义一个私有的静态成员变量作为实例,同时将构造函数声明为私有,避免外部直接创建对象,当需要获取实例时,通过静态成员函数返回该静态变量的引用,以保证全局唯一性。
- 注册表单例模式:将每个单例实例注册到单例管理器中,通过名称或者索引来获取实例。当需要创建新的实例时,先在注册表中查找是否已经存在该实例,如果不存在则创建一个并添加到注册表中。
以上是常见的C++单例模式的几种实现方式。选择合适的单例模式取决于具体的应用场景和需求。
以下是每种不同的C++单例模式的示例代码:
2 饿汉式单例模式:
class Singleton {
private:static Singleton* instance; // 实例指针// 私有化构造函数和拷贝构造函数,以防止外部创建或复制实例Singleton() {}Singleton(const Singleton&) {}public:static Singleton* getInstance() {return instance;}void doSomething() { /* 单例类的具体实现 */ }
};Singleton* Singleton::instance = new Singleton(); // 静态初始化实例int main() {Singleton* singleton = Singleton::getInstance(); // 获取单例实例return 0;
}
在该示例代码中,Singleton类的实例被静态初始化为一个常量,这意味着它在程序启动时就已经存在,因此getInstance()方法可以直接返回该实例。这种方式可以保证线程安全和性能。
3 懒汉式单例模式:
class Singleton {
private:static Singleton* instance; // 实例指针// 私有化构造函数和拷贝构造函数,以防止外部创建或复制实例Singleton() {}Singleton(const Singleton&) {}public:static Singleton* getInstance() {if (instance == nullptr) { // 如果没有实例,则创建一个新实例instance = new Singleton();}return instance;}void doSomething() { /* 单例类的具体实现 */ }
};Singleton* Singleton::instance = nullptr;int main() {Singleton* singleton = Singleton::getInstance(); // 获取单例实例return 0;
}
在该示例代码中,Singleton类的实例在第一次请求时被创建,这样可以节省资源。由于没有考虑线程安全问题,因此在多线程环境下可能存在问题。
4 双检锁/双重校验锁(Double-Checked Locking)单例模式:
class Singleton {
private:static Singleton* instance; // 实例指针// 私有化构造函数和拷贝构造函数,以防止外部创建或复制实例Singleton() {}Singleton(const Singleton&) {}public:static Singleton* getInstance() {if (instance == nullptr) { // 如果没有实例,则加锁创建一个新实例std::lock_guard<std::mutex> lock(mutex); // 加锁if (instance == nullptr) { // 再次检查实例是否为空instance = new Singleton();}}return instance;}void doSomething() { /* 单例类的具体实现 */ }static std::mutex mutex; // 静态互斥量,用于线程同步
};Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex;int main() {Singleton* singleton = Singleton::getInstance(); // 获取单例实例return 0;
}
在该示例代码中,Singleton类使用了双检锁来确保只有一个实例,并使用了静态互斥量来保证线程安全。在第一次请求时,只有一个线程可以进入临界区并创建实例。
5 内部静态变量单例模式:
class Singleton {
private:// 私有化构造函数和拷贝构造函数,以防止外部创建或复制实例Singleton() {}Singleton(const Singleton&) {}public:static Singleton& getInstance() {static Singleton instance; // 静态局部变量,只会初始化一次return instance;}void doSomething() { /* 单例类的具体实现 */ }
};int main() {Singleton& singleton = Singleton::getInstance(); // 获取单例实例return 0;
}
在该示例代码中,Singleton类的实例被定义为静态局部变量,这意味着它只会在第一次使用时进行初始化,并且是线程安全的。
6 登记式单例模式
登记式单例模式是指在程序启动时将Singleton对象注册到一个全局的对象管理器中,通过该管理器获取Singleton 对象。具体实现如下:
class Singleton {
public:static Singleton& getInstance() {if (instance == nullptr) {instance = new Singleton();SingletonManager::getInstance().registerObject("Singleton", instance);}return *instance;}
private:static Singleton* instance;Singleton() {}~Singleton() {}
};Singleton* Singleton::instance = nullptr;// 对象管理器
class SingletonManager {
public:static SingletonManager& getInstance() {static SingletonManager instance;return instance;}void registerObject(const std::string& name, Singleton* object) {objects_[name] = object;}Singleton* getObject(const std::string& name) {if (objects_.find(name) != objects_.end()) {return objects_[name];} else {return nullptr;}}
private:std::unordered_map<std::string, Singleton*> objects_;
};
该方式的优点是可以动态添加或删除单例对象,缺点是相对于其他方式而言实现起来比较复杂。