设计模式之——单例
单例是在程序设计中用得比较多的一种,其应用场景如下:
- 日志类。日志类往往对应实际的日志文件或者数据库,这种文件或数据库只存在一份,因此我们实例化的时候只需要实例化一次就够了,也就是说对于日志类,其实例化的对象在程序中有且只有一个。
- IO操作类,比如串口,SPI等设备。像这些外接设备在现实中也是只有一个,因此我们实例化也只需要实例化一次就够了。
单例的实现方式
饿汉式
对象在程序执行的时候就会被创建,在程序结束时自动释放。下面我们写一段比较简单的程序来说明这个问题。
#ifndef CSINGLETONEH_H
#define CSINGLETONEH_H
#include <iostream>
using namespace std;
/*
* C++单例的饿汉实现方式
*
*/
class CSingletonEH
{
public:
static CSingletonEH *getInstance();
private:
CSingletonEH(int arg);
~CSingletonEH();
private:
static CSingletonEH m_instance;
};
#endif // CSINGLETONEH_H
#include "CSingletonEH.h"
//定义静态的单例对象,程序执行时实例化,程序结束时释放(析构)
CSingletonEH CSingletonEH::m_instance(1);
CSingletonEH *CSingletonEH::getInstance()
{
return &m_instance;
}
CSingletonEH::CSingletonEH(int arg)
{
cout << "CSingletonEH object create!" << endl;
cout << "create arg:" << arg << endl;
}
CSingletonEH::~CSingletonEH()
{
cout << "CSingletonEH object destroy!" << endl;
}
上述程序中m_instance
在类内声明为static
的对象,在类外定义这个对象。毫无疑问,这个对象的生存周期和程序的生存周期一样,并且在程序执行时就被创建了。这里我们分析一下其优缺点。
优点:
- 对象的生命周期和程序一样,不用担心其内存释放和泄露问题。
- 线程安全(仅限于对象的创建,后续补充说明)
缺点: - 内存浪费,因为对象在程序执行时就会被创建,不管有没有用到该类
- 对象的创建不灵活,因为对象在定义时就被创建了,不能根据实际传入构造参数。
懒汉式
在第一次调用时创建对象,后续调用时直接返回已经创建的对象。
静态局部变量实现方式
这种实现方式是利用局部静态 只会被初始化一次。
#ifndef CSINGLETONLH_H
#define CSINGLETONLH_H
#include <pthread.h>
#include <iostream>
using namespace std;
/*
* C++单例的懒汉方式
* 对象在栈空间
*/
class CSingletonLH
{
public:
static CSingletonLH *getInstance(int val = 0);
private:
CSingletonLH(int val);
~CSingletonLH();
private:
};
#endif // CSINGLETONLH_H
#include "CSingletonLH.h"
//初始化静态锁
static pthread_mutex_t instanceMutex = PTHREAD_MUTEX_INITIALIZER;
CSingletonLH *CSingletonLH::getInstance(int val)
{
//防止产生多个对象,解决线程安全问题
pthread_mutex_lock(&instanceMutex);
static CSingletonLH singleton(val);
pthread_mutex_unlock(&instanceMutex);
return &singleton;
}
CSingletonLH::CSingletonLH(int val)
{
(void)val;//do something..
cout << "CSingletonLH Object Create!" << endl;
}
CSingletonLH::~CSingletonLH()
{
cout << "CSingletonLH Object Destroy!" << endl;
}
静态指针成员变量实现方式
需要在类内专门定义一个静态对象用于释放内存
#ifndef CSINGLETONLH_HEAP_H
#define CSINGLETONLH_HEAP_H
#include <pthread.h>
#include <iostream>
using namespace std;
/*
* C++单例的懒汉方式
* 对象存放在堆空间,并在类内声明一个static的对象用于释放内存
*/
class CSingletonLH_Heap
{
public:
static CSingletonLH_Heap *getInstance();
private:
CSingletonLH_Heap();
~CSingletonLH_Heap();
private:
static CSingletonLH_Heap *m_instance;
private:
class GarbageCollector {
public:
~GarbageCollector() {
if ( m_instance != NULL ) {
delete m_instance;
}
}
};
static GarbageCollector gc;
};
#endif // CSINGLETONLH_HEAP_H
#include "CSingletonLH_Heap.h"
CSingletonLH_Heap *CSingletonLH_Heap::m_instance = NULL;
//初始化静态锁
static pthread_mutex_t instanceMutex = PTHREAD_MUTEX_INITIALIZER;
//定义一个static GarbageCollector对象,在GarbageCollector的析构函数中释放内存
CSingletonLH_Heap::GarbageCollector CSingletonLH_Heap::gc;
CSingletonLH_Heap *CSingletonLH_Heap::getInstance()
{
pthread_mutex_lock(&instanceMutex);
if ( NULL == m_instance )
{
m_instance = new CSingletonLH_Heap();
}
pthread_mutex_unlock(&instanceMutex);
return m_instance;
}
CSingletonLH_Heap::CSingletonLH_Heap()
{
cout << "CSingletonLH_Heap Object Create!" << endl;
}
CSingletonLH_Heap::~CSingletonLH_Heap()
{
cout << "CSingletonLH_Heap Object Destroy!" << endl;
}
静态智能指针成员变量实现方式
通过智能指针自动释放内存
#ifndef CSINGLETONLH_HEAPSHAREPTR_H
#define CSINGLETONLH_HEAPSHAREPTR_H
#include <pthread.h>
#include <iostream>
#include <memory>
using namespace std;
class CSingletonLH_HeapSharePtr
{
public:
static shared_ptr<CSingletonLH_HeapSharePtr> getInstance();
~CSingletonLH_HeapSharePtr();
private:
CSingletonLH_HeapSharePtr();
CSingletonLH_HeapSharePtr(const CSingletonLH_HeapSharePtr &other);
CSingletonLH_HeapSharePtr &operator=(const CSingletonLH_HeapSharePtr &other);
private:
static shared_ptr<CSingletonLH_HeapSharePtr> m_instance;
};
#endif // CSINGLETONLH_HEAPSHAREPTR_H
#include "CSingletonLH_HeapSharePtr.h"
shared_ptr<CSingletonLH_HeapSharePtr> \
CSingletonLH_HeapSharePtr::m_instance(new CSingletonLH_HeapSharePtr());
shared_ptr<CSingletonLH_HeapSharePtr> CSingletonLH_HeapSharePtr::getInstance()
{
return m_instance;
}
CSingletonLH_HeapSharePtr::CSingletonLH_HeapSharePtr()
{
cout << "CSingletonLH_HeapSharePtr Object Create!" << endl;
}
CSingletonLH_HeapSharePtr::~CSingletonLH_HeapSharePtr()
{
cout << "CSingletonLH_HeapSharePtr Object Destroy!" << endl;
}
懒汉三种不同方式的比较
从上面的程序看来,在简单的日常使用中,利用静态局部变量来实现懒汉单例会比较简单,因此个人更偏向于该方式。当然,其他方式也有各自的应用场景,当我们有需要用到单例模式的时候,可以权衡一下实际的应用场景再决定用哪种实现方式。
单例的返回值类型
上述中,单例的返回值类型都是指针。其实我们也可以返回引用。对于两种类型的返回,有以下几点需要注意的。
- 当返回指针时,若设计的单例的生命周期跟程序一样,析构函数必须声明为
private
,防止用户提前释放。 - 当返回引用时,只能赋值给引用变量,因为其构造函数,拷贝构造都是
private
的。