首页 > 设计模式 之美 -- 单例模式

设计模式 之美 -- 单例模式

为什么要使用单例?

一个类只允许创建一个对象或者实例。

背景简介:使用多线程并发访问同一个类,为了保证类的线程安全,可以有两种方法:

  1. 将该类定义为单例模式,即该类仅允许创建一个实例
  2. 为该类的成员函数添加类级别的锁

举例:

一个向指定文件写入日志的类,为了保证该类并发调用时写入文件的日志不会覆盖,需执行以上操作。

单例模式的几种经典的实现方式:

  • 饿汉式

    在类的加载期间,将静态实例初始化好,所以实例的创建是线程安全的。但是这样的方式不支持延迟加载,且每加载一个实例都会重新初始化一次,开销较大。

  • 懒汉式

    懒汉模式相对于饿汉模式的优点是支持延迟加载(C++ 种的动态绑定),只有实例化的时候才知道该对象的实例。但是会导致出现频发加锁,释放锁造成效率底下 的问题。

  • 双重检测

    双重检测既支持延迟加载,又支持高并发的单例实现方式。

  • 静态内部类

    JAVA支持的静态内部类实现单例。这种实现方式既支持延迟加载,也支持高并发实例,实现起来也比双重检测简单。

  • 枚举

    最简单的实现方式,基于枚举实现的单例。通过枚举类型本身的特性,保证了实例创建的线程安全性和实例的唯一性。

C语言单例模式的实现:

饿汉模式

csingleton.h

#ifndef CSINGLETON_H
#define CSINGLETON_H
#include typedef struct { void* (*ctor)(void *_self);void* (*dtor)(void *_self);void* (*createInstance)(void *self);void *instance;
} _CSingleton;extern const void *CSingleton;
void *GetInstance(void);#endif

csingleton.c

#include "csingleton.h"
#include static void *csingletonCtor(void *_self) { _CSingleton *self = _self;self->instance = _self;return self;
}static void *csingletonDtor(void *_self) { _CSingleton *self = _self;self->instance = NULL;return self;
}static void *csingletonCreateInstance(void *_self) { _CSingleton *self = _self;self->instance = _self;return self;
}static _CSingleton _csingleton = { csingletonCtor, csingletonDtor, csingletonCreateInstance, NULL
};
const void *CSingleton = &_csingleton;void *GetInstance(void) {  //当调用该函数加载类的时候进行初始化if (NULL == ((_CSingleton*)CSingleton)->instance) { return csingletonCtor(CSingleton);} else { return ((_CSingleton*)CSingleton)->instance;}
}

main.c

#include "csingleton.h"
#include int main(int argc, char *argv[]) { void *ps1 = GetInstance();void *ps2 = GetInstance();if (ps1 == ps2) { fprintf(stdout, "ps1 = ps2
");}return 0;
}

懒汉模式

#include 
#include 
#include 
#include 
#include typedef struct ID{ char *name;int id_num;
}id;static id *_id = NULL;/*通过锁进行并发访问时线程间竞争的控制*/
static omp_lock_t lock;id *getInstance(){ omp_set_lock(&lock);if(NULL != _id) { omp_unset_lock(&lock);return _id;} else { _id = (id*)malloc(sizeof(id));assert(_id != NULL);omp_unset_lock(&lock);return _id;}
}int main(int argc, char *argv[]) { omp_set_num_threads(20);//运行时的库函数,设置运行的线程数目id * i1, *i2;omp_init_lock(&lock);{ i1 = getInstance() ;i1->name = "Rong";i1->id_num = omp_get_thread_num();}{ i2 = getInstance() ;i2->name = "Tao";}omp_destroy_lock(&lock);if(i1 == i2){ fprintf(stdout, " i1 == i2 
");}fprintf(stdout, "i1->name = %s, i1->score = %d
",i1->name, i1->id_num);fprintf(stdout, "i2->name = %s, i2->score = %d
",i2->name, i2->id_num);return 0;
}

C++实现单例模式

饿汉模式

#include 
using namespace std;class Singleon
{ 
private:Singleon(){ cout << "Singleon()" << endl;}static Singleon* instance;
public:static Singleon* GetSingleon(){ return instance;}static Singleon* Destroy(){ delete instance;instance = NULL;}
};/*编译加载类的时候即初始化,静态成员编译的时候即会初始化*/
Singleon* Singleon::instance = new Singleon();int main(){ Singleon* sl1 = Singleon::GetSingleon();Singleon* sl2 = Singleon::GetSingleon();Singleon* sl3 = Singleon::GetSingleon();cout << sl1 << endl;cout << sl2 << endl;cout << sl2 << endl;system("pause");return 0;}

懒汉模式

#include 
using namespace std;class Singleon
{ 
private:Singleon(){ cout << "Singleon()" << endl;}static Singleon*instrance;
public:static Singleon* GetSingleon(){ if (NULL == instrance){ instrance = new Singleon();cout << "对象创建成功" << endl;}else{ cout << "对象已经创建成功,无须再建" << endl;}return instrance;}static Singleon* Destroy(){ delete instrance;instrance = NULL;}
};/*类加载的时候不进行初始化,实例化的时候进行初始化*/
Singleon* Singleon::instrance =  NULL;
int main()
{ Singleon* sl1 = Singleon::GetSingleon();Singleon* sl2 = Singleon::GetSingleon();Singleon* sl3 = Singleon::GetSingleon();cout << sl1 << endl;cout << sl2 << endl;cout << sl2 << endl;system("pause");return 0;
}

更多相关:

  • 因为函数参数是按值传递的,所以要想改变变量,必须传递地址。 二级指针实际上就是指针变量的地址,如果传递二级指针,函数声明必须写**。 (void**)&必须是本质上就是指针变量的地址才可以做这样的转换,并不是说把一个一级指针也可以转换,void**的本质是标识一个二级指针。 &data就是(默认数据类型 **)&data,(void...

  • 文章目录1. 解决问题2. 应用场景3. 实现如下:C++实现C语言实现4. 缺点 1. 解决问题 在工厂方法模式中,我们卖衣服。此时我们为每一种衣服创建不同的工厂,帽子有一个工厂专门创建,裤子有一个工厂专门创建,T恤有一个工厂专门创建。这样的方式保证了代码设计的开闭原则(对扩展开发,对修改关闭),解决了简单工厂模式中暴露的...

  • 转载于:http://blog.csdn.net/u012819339/article/details/50654764   实体作品请参看优酷视频。 若以上链接点击无效请把该链接地址复制到浏览器地址栏 http://v.youku.com/v_show/id_XODYzODczNzQ4.html 说明: 该作品为arvik于2014...

  • - (void)viewDidLoad {[super viewDidLoad];NSLog(@"我在玩手机");NSLog(@"手机没电了");[self chargeMyIphone:^{NSLog(@"出门逛街");}];NSLog(@"我在看电视"); }-(void)chargeMyIphone:(void(^)(void...

  • http://stackoverflow.com/questions/150446/how-do-i-detect-when-someone-shakes-an-iphone 我的实现(基于Eran Talmor): 没必要application.applicationSupportsShakeToEdit = YES; Set th...

  • class str(basestring):"""str(object='') -> stringReturn a nice string representation of the object.If the argument is a string, the return value is the same object."""d...

  • 目录结构: contents structure [-] 类的基本使用专有方法继承单重继承多重继承砖石继承 1.类的基本使用 下面是类使用的一个简单案例, class person:"person 类的描述信息"def __init__(self,name="",age=0):self.name = nameself.age =...

  • 一、反射 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。 python面向对象中的反射:通过字符串的形式操作对象相关的属性。pytho...

  • 一、set集合的方法 set不是特别常用,但是set的一些特性可以方便处理一些特殊情况。 集合(set)是一个无序不重复元素的序列。 可以使用大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。 创建格式: parame = {value01,va...

  • 1>UITextField实现leftView: self.inputTextField = [[UITextField alloc]initWithFrame:CGRectMake(10, 10, 200, 25)];self.inputTextField.delegate = self;self.inputTextField....