Chinaunix首页 | 论坛 | 博客
  • 博客访问: 7918906
  • 博文数量: 701
  • 博客积分: 2150
  • 博客等级: 上尉
  • 技术积分: 13233
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-29 16:28
个人简介

天行健,君子以自强不息!

文章分类

全部博文(701)

文章存档

2019年(2)

2018年(12)

2017年(76)

2016年(120)

2015年(178)

2014年(129)

2013年(123)

2012年(61)

分类: C/C++

2018-02-12 17:12:50

一、单例

1.1. 意图

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

1.2. 动机

如何保证一个类只有一个实例,并且这个实例易于被访问呢?
如希望系统中只有一个脱机打印机实例,只有一个访问配置文件的实例时。
如果使用全局变量,将使得一个对象可以被访问,但它不能防止你实例化多个对象。

一个更好的办法是:
让类自身负责保存它的唯一实例。
这个类可以保证没有其他实例可以被创建(通过截取创建新对象的请求),
并且它可以提供一个访问该实例的方法。

.1.3. 适用性

在下面的情况下可以使用Singleton模式
? 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
? 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个
扩展的实例时。

1.4 解析

. 在面向对象开发中,对象与对象之间的成员变量是相互独立的。
要想共用数据,则需要使用静态成员和静态方法。
. 只要在类中声明静态成员变量,即使不定义对象,也可以为静态成员变量分配空间,
进而可以使用静态成员变量。
(因为静态成员变量在对象创建之前就已经被分配了内存空间)
. 静态成员变量虽然在类中,但它并不是随对象的建立而分配空间的,
也不是随对象的撤销而释放(一般的成员在对象建立时会分配空间,在对象撤销时会释放)。
静态成员变量是在程序编译时分配空间,而在程序结束时释放空间。
. 静态成员的定义和声明要加个关键static。
静态成员可以通过双冒号来使用,即<类名>::<静态成员名>。
. 初始化静态成员变量要在类的外面进行。
初始化的格式如下:数据类型  类名::静态成员变量名 = 初值;
. 不能用参数初始化表,对静态成员变量进行初始化。
. 既可以通过类名来对静态成员变量进行引用,也可以通过对象名来对静态成员变量进行引用。
. 普通成员函数和静态成员函数的区别是:
普通成员函数在参数传递时编译器会隐藏地传递一个this指针.
通过this指针来确定调用类产生的哪个对象;
但是静态成员函数没有this指针,不知道应该访问哪个对象中的数据,
所以在程序中不可以用静态成员函数访问类中的普通变量.

1.5. 参与者

? Singleton
— 定义一个Instance操作,允许客户访问它的唯一实例。
Instance是一个类操作,即C + +中的一个静态成员函数。
— 可能负责创建它自己的唯一实例。

1.6. 协作

? 客户只能通过S i n g l e t o n的I n s t a n c e操作访问一个S i n g l e t o n的实例。

1.7 示例代码

1.7.1 《设计模式》中示例代码:

Sinleton类定义如下:
class Singleton {
public:
static Singleton* Instance();
protected:
Singleton();
private:
static Singleton* _instance;
};

Singleton类的实现:
Singleton* Singleton::_instance =0;
Singleton* Singleton::Instance() {
if (_instance == 0){
_instance = new Singleton;
}
return _instance;
}

1.7.2 线程安全的代码

一个用于加载和解析配置文件的类,Configuration,用单例实现 。
Configuration类定义如下:
class CConfiguration{
public:
~CConfiguration(){};
static CConfiguration *get_instance();

private:
static CConfiguration* _instance;
CConfiguration() {
_instance = NULL; // 即this->_instance = NULL;
};

/* Singleton garbage */
class SingletonGarbo {
public:
~SingletonGarbo() {
if (CConfiguration::_instance) {
delete CConfiguration::_instance;
}
}
};

static SingletonGarbo _garbo;
};

Configuration的实现如下:
CConfiguration::SingletonGarbo CConfiguration::_garbo;
CConfiguration *CConfiguration::_instance = NULL;

CConfiguration *CConfiguration::get_instance() {
//静态成员函数第一次被调用时,满足该条件
if (NULL == _instance) {
//先是创建了一个实例对象,然后把实例对象的地址给了指向该实例对象的指针
_instance = new CConfiguration();
}
return _instance;
}

解析:


1.7.3 更多的实现

《C++实现单例的5种方法总结》

二、单例的继承

2.1 原理

单例的意图,是保证一个类仅有一个实例,并提供一个访问它的全局访问点。
那在继承时,使用的也是同样的该接口

2.2 示例代码

// gearman_worker_factory.h
class CGearmanWorkerFactory{
public:
static CGearmanWorkerFactory* get_instance(const char* name);
~CGearmanWorkerFactory(){};


protected:
CGearmanWorkerFactory() {
_instance = NULL;
}

private:
static CGearmanWorkerFactory* _instance;

// Singleton garbage
class SingletonGarbo {
public:
~SingletonGarbo() {
if (CGearmanWorkerFactory::_instance) {
delete CGearmanWorkerFactory::_instance;
}
}
};

static SingletonGarbo _garbo;
};

// gearman_worker_factory.cpp
CGearmanWorkerFactory::SingletonGarbo CGearmanWorkerFactory::_garbo;
CGearmanWorkerFactory* CGearmanWorkerFactory::_instance = NULL;
CGearmanWorkerFactory* CGearmanWorkerFactory::get_instance(const char *name){
if (NULL == _instance) {
if (strcmp(name, "record") == 0) {
RecordFactory* ins_record = CRecordFactory::get_instance();
_instance = dynamic_cast(ins_record);
} else if (strcmp(name, "player") == 0){
CPlayerFactory* ins_player = CPlayerFactory::get_instance();
_instance = dynamic_cast(ins_player);
}
}

// record_factory.h
class CRecordFactory: public CGearmanWorkerFactory {
public:
static CRecordFactory* get_instance();
~CRecordFactory(){};
private:
static CRecordFactory* _instance;
CRecordFactory(){
_instance = NULL;
}
// Singleton garbage
class SingletonGarbo {
public:
~SingletonGarbo() {
if (CRecordFactory::_instance) {
delete CRecordFactory::_instance;
}
}
};
static SingletonGarbo _garbo;
}

// record_factory.cpp
CRecordFactory* CRecordFactory::_instance = NULL;
CRecordFactory* CRecordFactory::get_instance(){
if (NULL == _instance) {
_instance = new CRecordFactory();
}
return _instance;
}

// player_factory.h
// player_factory.cpp
//和record_factory一样;

三、勘误

3.1 网上的一个解法
他在这里用到了一个友元,实际上破坏了单例的意图:
是保证一个类仅有一个实例,并提供一个访问它的全局访问点。
且实际应用时,通过new和get_instance获得的是两个不同的对象指针,运行时将会出错;






阅读(2755) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~