单例模式Singleton Pattern

单例模式Singleton Pattern

单例模式是只能有一个实例化的对象的类。这个类就要禁止别人new出来,或者通过直接定义。通过设置类的构造函数为私有来实现,既然构造函数是私有了,那么它就只能被类内部的成员函数调用,所以我们设置一个公有函数供外部调用,然后这个函数返回一个对象。为了保证多次调用为同一个对象,可以把类内部要返回的对象设置为静态变量

优点

(1)由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显
(2)减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
(3)避免对资源的多重占用。如避免对同一个资源文件的同时写操作
(4)单例模式可以在系统设置全局的访问点,优化和共享资源访问

缺点

单例模式一般没有接口,扩展困难。不利于测试

使用场景

(1)在整个项目中需要一个共享访问点或共享数据
(2)创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源
(3)需要定义大量的静态常量和静态方法的环境

实现

懒汉实现与饿汉实现

释放

如果单例模式中申请了实例对象,可以直接调用析构函数释放。如果单例模式的类中申请了其他资源,就无法释放,导致内存泄漏。static CSingleton *p;此时全局数据区中,存储的并不是一个实例对象,而是一个实例对象的指针,即一个地址变量而已,实例对象呢在堆区,因为是通过new得来的
(1)手动释放,添加一个公有的释放函数,可以在主函数中调用
(2)定义一个内部垃圾回收类,并且在Singleton中定义一个此类的静态成员。程序结束时,系统会自动析构此静态成员,此时,在此类的析构函数中析构Singleton实例,就可以实现实例的自动释放。防止被外部随意调用,应该设计成私有

懒汉实现

因为这样的方式只有在调用CSingleton::getInstance()的时候才会返回一个实例化的对象。以时间换空间。多线程情况下,如果一个函数中不同状态有不同操作,就要考虑线程同步的问题了,需要修改一下getInstance中的实现

class CSingleton {
private:
    CSingleton() {
        pthread_mutex_init(&mtx, 0);//锁的初始化
    }
    static CSingleton *p;//静态实例对象指针
public:
    static pthread_mutex_t mtx;//互斥锁
    //1.经典法加锁
    static CSingleton* getInstance() {
        if (p == NULL){
            pthread_mutex_lock(&mtx);
            if (p == NULL)
                p = new CSingleton();
            pthread_mutex_unlock(&mtx);
        }
        return p;
    }
    //2.内部静态变量法加锁
    CSingleton* getInitance()
    {
        pthread_mutex_lock(&mtx);
        static CSingleton obj;
        pthread_mutex_unlock(&mtx);
        return &obj;
    }
    //1.手动释放
public:
    static void releaseInstance()
    {
        delete p;
    }
    //2.内部垃圾回收类 
private:
    class CGarbo
    {
    public:
        ~CGarbo() {
            if (CSingleton::p)
                delete CSingleton::p;
        }
    };
    static CGarbo Garbo;//定义一个静态成员,在程序结束时,系统会调用它的析构函数
};
pthread_mutex_t CSingleton::mtx;
CSingleton* CSingleton::p = NULL;
CSingleton::CGarbo CSingleton::Garbo; // 类的静态成员需要类外部初始化,否则程序运行连GC的构造都不会进入
void* fun1(void *)
{
    while (1) {
        CSingleton *pt = CSingleton::getInstance();
        cout << "fun1: pt_addr = " << pt << endl;
        Sleep(1000);
    }
}
void* fun2(void *)
{
    while (1) {
        CSingleton *pt = CSingleton::getInstance();
        cout << "fun2: pt_addr = " << pt << endl;
        Sleep(1000);
    }
}
void callSingleton()
{
    pthread_mutex_init(&CSingleton::mtx, 0);
    pthread_t pt_1;
    pthread_t pt_2;
    int ret = pthread_create(&pt_1, 0, &fun1, 0);
    if (ret != 0)
        printf("error\n");
    ret = pthread_create(&pt_2, 0, &fun2, 0);
    if (ret != 0)
        printf("error\n");
    pthread_join(pt_1, 0);
    pthread_join(pt_2, 0);
    pthread_exit(0);
}

饿汉实现

类中的静态变量在外部声明的时候就可以new一个对象出来,因为p是CSingleton的成员,它是可以调用构造函数。锁也不用加了,因为我们调用CSingleton::getInstance()之前这个类就已经被实例化了,属于线程安全。我们调用这个函数的目地只是为了得到这个对象的地址。以空间换时间

class CSingleton
{
private:
    CSingleton() { }
    static CSingleton *p;
public:
    static CSingleton* getInstance() {
        return p;
    }
};
CSingleton* CSingleton::p = new CSingleton();

你可能感兴趣的:(单例模式Singleton Pattern)