您的位置:首页 > 编程语言 > C语言/C++

设计模式学习--单例模式

2016-06-27 22:32 489 查看
在设计模式中,单例模式是最常见的一种模式。下面是本人学习单例模式的一些总结。用java和C++两种语言实现单例模式

定义

确保一个类只有一个实例,并提供一个全局访问点。

实现方法

如何在实际软件中,做到类只有一个实例呢。最简单的方法,就是使类的构造函数私有,这样类的使用者,就不能创建类的实例,然后,再提供统一的获取类实例的方法。

下面就给出Java和C++两种具体语言的实现。

JAVA实现方法

静态变量

Singleton最简单的方法,就是直接声明一个static变量,并且进行初始化,这样类在加载的时候,就会初始化实例。

public class Singleton
{
private volatile static Singleton singleton = new Singleton();
private Singleton()  {    }
public static Singleton getInstance()   {
return singleton;
}
}


双检锁

使用双检索的方式,必须添加volatile关键字,

因为JVM的重排序,会导致未被初始化的实例.

public class Singleton
{
private volatile static Singleton singleton = null;
private Singleton(){}

public static Singleton getInstance(){
if (singleton== null)  {
synchronized (Singleton.class) {
if (singleton== null)  {
//没有volatile关键字会因为重排序,导致singleton!=null,而实例未被成功初始化
singleton= new Singleton();
}
}
}
return singleton;
}
}


enum实现

使用jdk1.5之后的enum可以很方便实现单例。

public enum Singleton {
INSTANCE;
}


C++实现方法

C++和java差不多,但C++中的构造函数种类比较多,有普通构造函数,有拷贝构造函数。为了避免实例被外部delete,可以将类的析构函数也私有,这样在外部调用delete的时候,也会有编译错误。

静态局部变量

C++最简单的方式,就是使用静态局部变量。在第一次获取instance的时候,会进行初始化。

#include <iostream>
using namespace std;

class Singleton{
public:
static Singleton& getInstance(){
static Singleton instance;
return instance;
}

void print(){
cout << "Hello, my data:" << data << endl;
}
void add(int i){
data += i;
}

private:
Singleton(){
data = 0;
};

Singleton(Singleton &){}
~Singleton(){}

int data;
};

int main(int argc, const char* argv[]) {
Singleton & s1 = Singleton::getInstance();
Singleton & s2 = Singleton::getInstance();
s1.print();
s2.add(2);
s1.print();
}


但这种方式,在C++0x之前,是线程不安全的,C++0x之后,局部静态变量是线程安全的。

静态变量

C++中,为了避免C++中局部变量的线程安全问题,可以将局部静态变量放到类的静态变量中。

#include <iostream>
using namespace std;

class Singleton{
public:
static Singleton& getInstance(){
return instance;
}

void print(){
cout << "Hello, my data:" << data << endl;
}
void add(int i){
data += i;
}
private:
static Singleton instance;

private:
Singleton(){
data = 0;
};
Singleton(Singleton &){}
~Singleton(){}

int data;
};

Singleton Singleton::instance ;

int main() {
Singleton & s1 = Singleton::getInstance();
Singleton & s2 = Singleton::getInstance();
s1.print();
s2.add(2);
s1.print();

return 0;
}


这种实现,有个问题,单例的初始化,是类的静态初始化时完成的,无法控制两个类的初始化顺序。如果有两个单例相互依赖,则会有问题。

双检锁

为了使用延迟初始化,同时解决线程安全问题,可以使用双检锁的方式,先判断实例是否为空,如果为空,则加锁,加锁后,再进行一次为空判断。这样,只有在单例被初始化的时候,才会有加锁的性能问题。

#include <iostream>
#include <mutex>
using namespace std;

class Singleton{
public:
static Singleton& getInstance(){
if (instance == NULL){
m.lock();
if (instance == NULL){
instance = new Singleton();
}
m.unlock();
}
return *instance;
}

void print(){
cout << "Hello, my data:" << data << endl;
}
void add(int i){
data += i;
}

private:
static Singleton* instance;// = new Singleton();
static mutex m;
private:
Singleton(){
data = 0;
};
Singleton(Singleton &){}
~Singleton(){}

int data;
};

Singleton* Singleton::instance = NULL;
mutex Singleton::m;

int main(int argc, const char* argv[]) {
Singleton & s1 = Singleton::getInstance();
Singleton & s2 = Singleton::getInstance();
s1.print();
s2.add(2);
s1.print();

return 0;
}


单例角

如果有多个单例,都想使用双检锁的方法,

利用C++的模板和friend友元功能,可以快速产生单例类

#include <iostream>
#include <mutex>
using namespace std;

template<class T>
class Singleton{
public:
static T& getInstance(){
if (nullptr == instance){
m.lock();
if (nullptr == instance){
instance = new T();
}
m.unlock();
}
return *instance;
}

private:
static T* instance;
static mutex m;

protected:
Singleton(){};
Singleton(Singleton &){}
~Singleton(){}
};

template<class T>
T* Singleton<T>::instance = nullptr;

template<class T>
mutex Singleton<T>::m;

class MyClass : public Singleton<MyClass>{
friend class Singleton<MyClass>;

public:
void print(){
cout << "Hello, my data:" << data << endl;
}
void add(int i){
data += i;
}

private:
MyClass():data(0){}
MyClass(MyClass &){}
~MyClass(){}

int data;
};

int main(int argc, const char* argv[]) {
MyClass & s1 = MyClass::getInstance();
MyClass & s2 = MyClass::getInstance();
s1.print();
s2.add(2);
s1.print();

return 0;
}


应用场景

在现实中,有很多地方都是需要用到单例模式的,比如全局配置信息,数据库连接池
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息