C++单列模式

单例模式是一种最为常见的软件设计模式。单例模式要求:单例对象所在的类必须保证只能创建一个对象。单例模式在我们日常生活和软件开发中的应用比比皆是,比如:windows系统只有一个任务管理器,一个市只有一个市长。
    如何保证一个类最多只能创建一个对象呢?这个问题不能交由使用者去做处理,比如用全局变量。而应该由这个类的创建者在实现该类的时候考虑问题的解决。
    单例模式巧妙的使用C++成员权限,将构造函数和拷贝构造函数隐藏起来(private),从而有效限定使用中对对象的自由创建。然后开放一个(static)接口,通过静态方法创建对象,并在静态方法中限定对象的唯一创建。
    单例模式的创建方式一般有两种:懒汉式和饿汉式。
1.懒汉模式
懒汉:顾名思义,不到万不得已该类不会去实例化对象。将对象的示例推迟到需要该对象的时候。

// 单例模式之懒汉模式
class Singleton{
public:
		static Singleton* createSingleton(){	// static方法
			if( m_handler == nullptr ){
				m_handler = new Singleton();
			}
			return m_handler;
		}
private:
	Singleton();			//  私有化构造函数
	~Singleton();		//  私有化析构函数
	Singleton( const Singleton & );	//  私有化拷贝构造函数,防止通过拷贝构造复制对象
	static Singleton* m_handler;
};

Singleton* Singleton::m_handler = nullptr;

int main()
{
	Singleton *ptr1 = Singleton::createSingleton();
	Singleton *ptr2 = ptr1-> createSingleton();  // ptr1 和 ptr2 指向同一个对象
	return 0;
}

 

2.饿汉模式
饿汉:单例类在创建类的时候就创建了对象。

// 单例模式之饿汉模式
class Singleton{
public:
        static Singleton* getSingleton(){
            return m_handler;
        }
private:
    Singleton();            //  私有化构造函数
    ~Singleton();        //  私有化析构函数
    Singleton( const Singleton & );    //  私有化拷贝构造函数,防止通过拷贝构造复制对象
    static Singleton* m_handler;
};
Singleton* Singleton::m_handler = new Singleton;        // 类创建时,创建对象

int main()
{
    Singleton *ptr1 = Singleton::createSingleton();
    Singleton *ptr2 = ptr1-> createSingleton();  // ptr1 和 ptr2 指向同一个对象
    return 0;
}

3.单例模式中的线程安全
前面我们考虑了单例模式的懒汉式和饿汉式,但是我们只考虑了普通单线程情况。如果考虑到多线程情况,那么上面的懒汉模式则不是线程安全的。而饿汉模式因为在编译阶段已经创建了对象,所有它是线程安全的。
如何解决懒汉模式的线程不安全呢?通常情况我们可以通过互斥锁解决临界资源的访问问题。

// 单例模式之懒汉模式+线程安全
class Singleton{
public:
    static Singleton* createSingleton(){    // static方法
        if( m_handler == nullptr ){   // 解决访问效率问题
            pthread_mutex_lock( &m_lock );
            if( m_handler == nullptr ){
                m_handler = new Singleton();
            }
            pthread_mutex_unlock( &m_lock );
            return m_handler;
        }
    }
private:
    Singleton();            //  私有化构造函数
    ~Singleton();        //  私有化析构函数
    Singleton( const Singleton & );    //  私有化拷贝构造函数,防止通过拷贝构造复制对象
    static Singleton* m_handler;
    static pthread_mutex_t  m_lock;
};

Singleton* Singleton::m_handler = nullptr;
pthread_mutex_t  Singleton::m_lock = PTHREAD_MUTEX_INITIALIZER;


int main()
{
    Singleton *ptr1 = Singleton::createSingleton();
    Singleton *ptr2 = ptr1-> createSingleton();  // ptr1 和 ptr2 指向同一个对象
    return 0;
}

 

上面例程通过互斥锁,看似解决了多线程中的临界资源互斥问题。但是实际上并非如此。具体问题如下:
上面代码中:m_handler = new Singleton();  我们期望的执行顺序是:
(1)分配一段内存   (2)构造对象,放入内存   (3)m_handler存内存地址
但是实际执行可能是:
(1)分配一段内存   (2) m_handler存内存地址   (3)构造对象,放入内存
那么后面的情况可能导致,对象还没创建,但是已经被另外一个线程拿去使用了,这种情况可能导致严重错误。那么如何解决呢?大家可以思考一下。

文章来源:学到牛牛

posted @ 2022-05-17 09:35  学到牛牛  阅读(58)  评论(0)    收藏  举报