单例模式(C++实现)

2019-08-20  本文已影响0人  anycedo

一、什么是单例模式

1.1单例模式的基本概念

    单例模式属于创建型模式,这种模式涉及到一种单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一对象的方式。
    单例类的特征:1.单例类最多只能有一个实例;2.单例类必须自己创建自己唯一的实例;3.单例类必须给所有其他的对象提供这一实例。

1.2使用单例模式的好处

    使用单例类是为了保证某一个类仅有一个实例,并提供一个访问它的全局访问点。
    单例类主要解决了一个全局使用的类的频繁的创建与销毁。
    所以单例模式有以下几个优点: 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例;2、避免对资源的多重占用。
    那单例模式有一个不好的地方就是,单例类没有接口,不能继承。

1.3单例模式的使用场景

    当我们想在程序中控制实例数目,节约系统资源的时候,可以考虑使用单例类。
    >>1.Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
    >>2.一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。

二、单例模式的C++实现

2.1懒汉式

    什么叫懒汉式的单例类呢?顾名思义,懒汉很懒,只有他饿了的时候才会去找吃的,也就是说只有在需要的时候才会去实例化。一次都没有需要的时候,不会去构造这个对象,这个程序中也就没有这个对象的实例,只有需要了一次了,实例化了一次,这个对象才会一直存在。

//Singleton.cpp
#include<iostream>
#include<mutex>
using namespace std;
//mutex singletonLock;
class Singleton {
public:
    static Singleton* getInstance() {
        static Singleton* singeltonInstance;//函数内的静态变量,声明加定义,已分配空间。
        cout << "getInstance in singleton." << endl;
//线程安全
        //if(singeltonInstance == NULL){
        //  singletonLock.lock();//线程安全
            if (singeltonInstance == NULL)
              singeltonInstance = new Singleton();
       //   singletonLock.unlock();
       //}
        return singeltonInstance;
    };
private:
    //static Singleton* singeltonInstance;//若在此处定义,类内的静态成员变量,要记得在类外部的cpp文件的全局进行初始化。(仅仅是声明,并没定义,所以没有分配空间)
//Static members obey the usual class member access rules (clause 11). When used in the declaration of a class member, the static specifier shall only be used in the member declarations that appear within the member-specification of the class declaration. [Note: it cannot be specified in memberdeclarations that appear in namespace scope. ] 
    Singleton() {
        cout << "Construct in singleton." << endl;
    }
    Singleton(const Singleton&);//拷贝构造函数和赋值运算符也是私有的,以禁止拷贝和赋值;
    Singleton& operator=(const Singleton&);
};

    
    懒汉式的单例类在多线程的使用中存在线程安全问题。如果有两个线程同时获取单例类的实例,又都发现实例不存在,因此都会进行实例化,会产生两个实例都要赋给singeltonInstance,会造成严重的错误,要解决这个问题就要加锁。上面程序注释掉的为线程安全的部分,即为懒汉式单例模式线程安全问题的一个解决方案。

2.2饿汉式

    饿汉式的单例类,会在全局作用域进行单例类的实例化,并以此来初始化。

//Singleton.cpp
#include<iostream>
using namespace std;
class Singleton;
class Singleton {
private:
    Singleton() {};//构造函数私有化
    Singleton(Singleton &s) {}; //拷贝构造私有化
    Singleton& operator=(const Singleton& s) {};
    static Singleton* singletonInstance;
public:
    static Singleton* getInstance() {
        return singletonInstance;
    }
};
//......
Singleton* Singleton::singletonInstance = new Singleton();//全局实例化

2.3 单例模式的GC

    1.单例模式中实例化的对象是new出来的,需要对它负责,也就是需要delete;
    2.delete一个对象时,会调用它的析构函数;
    3.如果在析构函数里使用delete进行内存管理,由于对象是new出来的,根本进不去析构函数,无法自动的销毁;
    4.如果在析构函数里使用delete进行内存管理,就一定会造成一个析构的循环的(见2)。

    那么应该怎样在C++的程序中显式的进行单例对象的内存管理呢?一个比较好的方法是定义一个内部垃圾回收类,并且在Singleton中定义一个此类的静态成员。程序结束时,系统会自动析构此静态成员,此时,在此类的析构函数中析构Singleton实例,就可以实现singletonInstance的自动释放。

//Singleton.cpp
#include<iostream>
using namespace std;
class Singleton;
class Singleton {
private:
    Singleton() { cout << "Construct singleton." << endl; };//构造函数私有化
    Singleton(Singleton &s) {}; //拷贝构造私有化
    Singleton& operator=(const Singleton& s) {};
    static Singleton* singletonInstance;

    class SingletonCG {//单例的回收
    public:
        ~SingletonCG(){
            if (singletonInstance != NULL)
                delete singletonInstance;
            singletonInstance = NULL;
            cout << "Singleton delete." << endl;
            system("pause");
        }
    };
    static SingletonCG cg;
public:
    static Singleton* getInstance() {
        cout << "getInstance." << endl;
        return singletonInstance;
    }
    
};
//......
Singleton* Singleton::singletonInstance = new Singleton();//全局实例化
Singleton::SingletonCG Singleton::cg;////类的静态成员需要类外部初始化,这一点很重要,否则程序运行连GC的构造都不会进入,何谈自动析构

三、使用单例模式的注意事项

四、参考资料

[1]单例模式——菜鸟教程
[2]单例模式——C++实现
[3]单例模式——C++的GC

上一篇 下一篇

猜你喜欢

热点阅读