• c++模式之单例模式详解


    1.概念

    单例模式是指在整个系统生命周期内,保证一个类只能产生一个实例,确保该类的唯一性.
    使用单例两个原因:
    1.节省资源。一个类只有一个实例,不存在多份实例,节省资源。
    2.方便控制。在一些操作公共资源的场景时,避免了多个对象引起的复杂操作
    单例类的特点

    • 构造函数和析构函数为私有类型,目的是禁止外部构造和析构。
    • 拷贝构造函数和赋值构造函数是私有类型,目的是禁止外部拷贝和赋值,确保实例的唯一性。
    • 类中有一个获取实例的静态方法,可以全局访问。

    2.懒汉模式示例(缺点)

    getInstance函数使用了懒汉式单例模式的实现方式。它首先检查静态成员变量instance是否为空,如果为空则创建一个新的实例,否则直接返回已有的实例。这种实现方式在单线程环境下是有效的,但在多线程环境下可能会导致线程安全问题。
    在多线程环境下,多个线程可能会同时检查到instance为空,然后同时创建多个实例,违背了单例模式的初衷。为了解决这个问题,我们需要在创建实例时添加同步机制,以确保只有一个线程能够创建实例。

    #include 
    #include 
    #include 
    #include 
    using namespace std;
    
    void sleep(int time) { clock_t head = clock(); while (clock() - head <= time) {} }
    
    class Singleton {
    private:
        static Singleton* instance; // 静态成员变量,用于保存单例实例
        Singleton() {} // 私有构造函数,防止外部实例化
    
    public:
        static Singleton* getInstance() {
            if (instance == nullptr) {
                instance = new Singleton();
            }
            return instance;
        }
    
        void someFunction() {
            // 单例的其他成员函数
            cout << "HELLO WORLD" << endl;
        }
    };
    
    Singleton* Singleton::instance = nullptr; // 初始化静态成员变量
    
    int main() {
        Singleton* obj1 = Singleton::getInstance();
        obj1->someFunction();
    
        Singleton* obj2 = Singleton::getInstance();
        obj2->someFunction();
    
        // obj1和obj2是同一个实例
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    3.懒汉模式线程安全

    添加了一个静态成员变量mutex作为互斥锁,用于线程同步。在getInstance函数中,我们首先进行一次非线程安全的检查,如果instance为空,才会获取互斥锁并再次检查instance是否为空。这样可以确保只有一个线程能够创建实例。
    使用了std::lock_guard来自动管理锁的加锁和解锁,以避免手动处理锁的释放。这样可以确保在任何情况下,无论是正常返回还是发生异常,都会自动释放锁。
    这个改进后的示例提供了一种线程安全的懒汉式单例模式实现方式,可以在多线程环境下正常工作

    #include 
    #include 
    using namespace std;
    
    class Singleton {
    private:
        static Singleton* instance; // 静态成员变量,用于保存单例实例
        static std::mutex mutex; // 互斥锁,用于线程同步
        Singleton() {} // 私有构造函数,防止外部实例化
    
    public:
        static Singleton* getInstance() {
            if (instance == nullptr) {
                lock_guard<std::mutex> lock(mutex); // 加锁
                if (instance == nullptr) {
                    instance = new Singleton();
                }
            }
            return instance;
        }
    
        void someFunction() {
            // 单例的其他成员函数
            cout << "mutex hello world" << endl;
            
        }
    };
    
    Singleton* Singleton::instance = nullptr; // 初始化静态成员变量
    std::mutex Singleton::mutex; // 初始化互斥锁
    
    int main() {
        Singleton* obj1 = Singleton::getInstance();
        obj1->someFunction();
    
        Singleton* obj2 = Singleton::getInstance();
        obj2->someFunction();
    
        // obj1和obj2是同一个实例
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    4.饿汉式创建单例

    它是一种在程序启动时就创建实例的单例模式。下面是一个简单的示例来说明如何实现C++的饿汉式单例模式。

    Singleton类使用饿汉式的方式创建实例。在静态成员变量instance的定义处,我们直接使用new操作符创建了一个Singleton的实例,并将其赋值给instance。这样,在程序启动时,实例就会被创建并初始化。
    在getInstance函数中,我们直接返回已经创建好的实例,而无需再进行实例化。
    在main函数中,我们通过调用Singleton::getInstance()来获取单例实例,并调用其成员函数。由于使用了饿汉式创建实例,obj1和obj2实际上是同一个实例。
    饿汉式在程序启动时就创建了实例,因此会占用一定的内存空间。此外,如果实例的创建过程较为复杂或耗时,可能会影响程序的启动速度。因此,在选择单例模式的实现方式时,需要根据具体的需求和场景来决定使用懒汉式还是饿汉式。

    #include 
    using namespace std;
    
    class Singleton {
    private:
        static Singleton* instance; // 静态成员变量,用于保存单例实例
        Singleton() {} // 私有构造函数,防止外部实例化
    
    public:
        static Singleton* getInstance() {
            return instance;
        }
    
        void someFunction() {
            cout << "Hungry Han style instance" << endl;
        }
    };
    
    Singleton* Singleton::instance = new Singleton(); // 在静态成员变量初始化时创建实例
    
    int main() {
        Singleton* obj1 = Singleton::getInstance();
        obj1->someFunction();
    
        Singleton* obj2 = Singleton::getInstance();
        obj2->someFunction();
    
        // obj1和obj2是同一个实例
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    5.饿汉模式线程示例

    #include 
    #include 
    using namespace std;
    
    class ThreadSingleton {
    private:
        static ThreadSingleton* instance; // 静态成员变量,用于保存单例实例
        ThreadSingleton() {} // 私有构造函数,防止外部实例化
    
    public:
        static ThreadSingleton* getInstance() {
            return instance;
        }
    
        void calculateSquareArea(double side) {
            double area = side * side;
            std::cout << "The area of the square is: " << area << std::endl;
        }
    };
    
    ThreadSingleton* ThreadSingleton::instance = new ThreadSingleton(); // 在静态成员变量初始化时创建实例
    
    int main() {
        std::thread t1([&]() {
            ThreadSingleton* obj1 = ThreadSingleton::getInstance();
            obj1->calculateSquareArea(5);
            });
    
        std::thread t2([&]() {
            ThreadSingleton* obj2 = ThreadSingleton::getInstance();
            obj2->calculateSquareArea(8);
            });
    
        t1.join();
        t2.join();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
  • 相关阅读:
    Exch:POP3 和 IMAP4 操作指南
    基于rt thread smart构建EtherCAT主站
    计蒜客T1375 百钱买百鸡(四)
    Echarts的配置修改
    都 2022 年了,你真的会用 Python 的 pip 吗?
    时空数据挖掘五(城市计算)
    优秀的Elasticsearch Java 客户端-Jest
    ZPL II 语言编程基础
    解析五育融合之下的steam教育模式
    动态规划- 背包问题总结(一)
  • 原文地址:https://blog.csdn.net/qq_44913716/article/details/134515709