单 例 模 式

什么是单例模式?

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。这种模式在需要控制资源的访问、管理全局状态或避免创建多个实例的场景中非常有用。

注意:

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。

单例模式的适用场景

  1. 资源管理:例如,线程池、数据库连接池等资源需要全局唯一的实例来进行管理和控制。
  2. 全局状态:需要一个全局唯一的对象来保存状态,如配置文件读取器、日志记录器等。
  3. 避免多次实例化:某些对象的实例化过程耗时耗资源,需要保证只创建一次。

单例模式同时解决了两个问题, 所以违反了单一职责原则

  1. 保证一个类只有一个实例。 为什么会有人想要控制一个类所拥有的实例数量? 最常见的原因是控制某些共享资源 (例如数据库或文件) 的访问权限。

    它的运作方式是这样的: 如果你创建了一个对象, 同时过一会儿后你决定再创建一个新对象, 此时你会获得之前已创建的对象, 而不是一个新对象。

    注意, 普通构造函数无法实现上述行为, 因为构造函数的设计决定了它必须总是返回一个新对象。

客户端甚至可能没有意识到它们一直都在使用同一个对象。

        2 . 为该实例提供一个全局访问节点。 还记得你 (好吧, 其实是我自己) 用过的那些存储重要对象的全局变量吗? 它们在使用上十分方便, 但同时也非常不安全, 因为任何代码都有可能覆盖掉那些变量的内容, 从而引发程序崩溃。

和全局变量一样, 单例模式也允许在程序的任何地方访问特定对象。 但是它可以保护该实例不被其他代码覆盖。

还有一点: 你不会希望解决同一个问题的代码分散在程序各处的。 因此更好的方式是将其放在同一个类中, 特别是当其他代码已经依赖这个类时更应该如此。

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

模式结构:

单例模式的核心要素

  1. 私有化构造函数:防止其他代码使用new关键字直接实例化对象。
  2. 静态成员变量:持有类的唯一实例。
  3. 公有的静态方法:提供全局访问点来获取该实例。

C++实现单例模式

在C++中实现单例模式时,需要考虑线程安全性、内存管理和效率等问题。以下是单例模式的几种常见实现方式。

1. 饿汉式单例

饿汉式单例在类加载时就初始化单例对象。它的实现简单且线程安全,但如果单例对象的初始化依赖于其他资源或配置文件,这种方式可能不太适合。

class Singleton {
private:
    static Singleton* instance;
    Singleton() {} // 私有构造函数
public:
    static Singleton* getInstance() {
        return instance;
    }
};

// 静态成员变量初始化
Singleton* Singleton::instance = new Singleton();

int main() {
    Singleton* s1 = Singleton::getInstance();
    Singleton* s2 = Singleton::getInstance();

    // 验证s1和s2是同一个实例
    if (s1 == s2) {
        std::cout << "s1和s2是同一个实例" << std::endl;
    } else {
        std::cout << "s1和s2不是同一个实例" << std::endl;
    }

    return 0;
}

2. 懒汉式单例(线程不安全)

懒汉式单例在第一次调用getInstance方法时初始化实例。这种方式延迟了实例的创建,但在多线程环境下不安全。

class Singleton {
private:
    static Singleton* instance;
    Singleton() {} // 私有构造函数
public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};

// 静态成员变量初始化
Singleton* Singleton::instance = nullptr;

int main() {
    Singleton* s1 = Singleton::getInstance();
    Singleton* s2 = Singleton::getInstance();

    // 验证s1和s2是同一个实例
    if (s1 == s2) {
        std::cout << "s1和s2是同一个实例" << std::endl;
    } else {
        std::cout << "s1和s2不是同一个实例" << std::endl;
    }

    return 0;
}
3. 线程安全的懒汉式单例

为了解决懒汉式单例在多线程环境中的问题,可以使用互斥锁来确保线程安全。

#include <mutex>

class Singleton {
private:
    static Singleton* instance;
    static std::mutex mtx;
    Singleton() {} // 私有构造函数
public:
    static Singleton* getInstance() {
        std::lock_guard<std::mutex> lock(mtx);
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};

// 静态成员变量初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;

int main() {
    Singleton* s1 = Singleton::getInstance();
    Singleton* s2 = Singleton::getInstance();

    // 验证s1和s2是同一个实例
    if (s1 == s2) {
        std::cout << "s1和s2是同一个实例" << std::endl;
    } else {
        std::cout << "s1和s2不是同一个实例" << std::endl;
    }

    return 0;
}
4. 使用C++11的线程安全单例

C++11引入了std::call_oncestd::once_flag,可以简化线程安全的单例模式实现。

#include <mutex>

class Singleton {
private:
    static Singleton* instance;
    static std::once_flag initInstanceFlag;
    Singleton() {} // 私有构造函数
public:
    static Singleton* getInstance() {
        std::call_once(initInstanceFlag, []() {
            instance = new Singleton();
        });
        return instance;
    }
};

// 静态成员变量初始化
Singleton* Singleton::instance = nullptr;
std::once_flag Singleton::initInstanceFlag;

int main() {
    Singleton* s1 = Singleton::getInstance();
    Singleton* s2 = Singleton::getInstance();

    // 验证s1和s2是同一个实例
    if (s1 == s2) {
        std::cout << "s1和s2是同一个实例" << std::endl;
    } else {
        std::cout << "s1和s2不是同一个实例" << std::endl;
    }

    return 0;
}

单例模式的优缺点

优点
  1. 唯一实例:确保系统中只有一个实例,节省资源。
  2. 全局访问:提供一个全局访问点,方便访问实例。
  3. 延迟初始化:某些实现方式支持延迟初始化,优化性能。
缺点
  1. 全局状态:可能引入全局状态,增加系统复杂性。
  2. 并发问题:在多线程环境下实现单例模式需要考虑线程安全性。
  3. 难以测试:单例模式可能导致代码难以测试,因为它隐藏了类的依赖关系。

总结

单例模式是一种非常常用的设计模式,适用于需要全局唯一实例的场景。在C++中,可以通过多种方式实现单例模式,包括饿汉式单例、懒汉式单例和线程安全的单例实现。每种实现方式都有其优缺点,开发者需要根据具体需求选择合适的实现方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值