单件模式是一种较为简单的创建型模式,在日常的开发中也常常使用。目的是在应用程序中保证某个类的对象仅能有一个。在C++的世界中,因为它不是纯面向对象的语言,它支持全局变量的特性,所以在许多的场合它可以用全局变量来替代。非常典型的例子就是在MFC的应用程序中的theAPP对象,它就是一个全局的唯一对象。类似这样的例子在C++的程序中有许多,在C++中存在这样的现象是有一定的原因。这里先介绍一个基于C++实现的单件模式例子。
1#pragma once
2#include <deque>
3
4/**/////////////////////////////////////////////////////////////////////////// 5// Forward declaration
6//
7//
8/**/////////////////////////////////////////////////////////////////////////// 9class CSendJob;
10class JobDeque;
11
12/**//// <summary>
13/// 本类为作业启动信息储存体
14/// 此类仅为JobDeque所使用,不宜让其它客户使用
15/// </summary>16class JobStartInformation
17{
18 friend class JobDeque;
19public:
20 ~JobStartInformation()
21 {
22 //Don`t delete the m_pJob pointer.
23 }
24
25private:
26 JobStartInformation(CSendJob *pJob, BOOL bManual)
27 :m_pJob(pJob),
28 m_bManualStart(bManual)
29 {
30 }
31
32public:
33 CSendJob* GetJob() const { return m_pJob; }
34 BOOL IsManualStart() const { return m_bManualStart; }
35
36private:
37 BOOL m_bManualStart;
38 CSendJob *m_pJob;
39};
40
41class JobDeque
42{
43 // Constructor and Destructor
44public:
45private:
46 virtual ~JobDeque(void);
47private:
48 JobDeque(void);
49 JobDeque(const JobDeque &);
50 JobDeque& operator=(const JobDeque& );
51
52 // Methods:
53public:
54 void Start( CSendJob *pJob, BOOL bManualStart);
55 void Completed( CSendJob* pJob);
56private:
57 void Launch();
58private:
59 USHORT m_usRunAmount;
60 std::deque<JobStartInformation > m_jobDeque;
61
62public:
63 static JobDeque* GetJobDeque();
64 static void DestroyJobDeque();
65
66private:
67 /**////<summary>
68 /// 可以并行执行的作业数目
69 ///</summary>
70 static const USHORT m_susParallelAmount;
71 static JobDeque *m_spJobDeque;
72
73#ifdef _DEBUG
74 void Validate();
75#else
76 void Validate()
77 {
78 }
79#endif
80};
81
1#include "StdAfx.h"
2#include <algorithm>
3#include "SendJob.h"
4#include "JobDeque.h"
5using namespace std;
6
7// JobDeque.cpp
8/**////<summary>
9/// 这里仅对全局唯一的作业对象地址作比较
10///</summary> 11bool operator==(const JobStartInformation& lo, const JobStartInformation& ro) throw()
12{
13 if(&lo == &ro)
14 {
15 return true;
16 }
17
18 if(NULL ==lo.GetJob() && NULL == ro.GetJob())
19 {
20 return true;
21 }
22 if(NULL == lo.GetJob() || NULL == ro.GetJob())
23 {
24 return false;
25 }
26 return lo.GetJob() == ro.GetJob();
27}
28
29bool operator!=(const JobStartInformation& lo, const JobStartInformation& ro) throw()
30{
31 return !(lo == ro);
32}
33
34bool operator<(const JobStartInformation& lo, const JobStartInformation& ro) throw()
35{
36 if(lo == ro)
37 {
38 return true;
39 }
40 return lo.GetJob() < ro.GetJob();
41}
42
43
44/**/////////////////////////////////////////////////////////////////////////// 45// 静态变量
46const USHORT JobDeque::m_susParallelAmount = 1;
47JobDeque* JobDeque::m_spJobDeque = NULL;
48
49/**/////////////////////////////////////////////////////////////////////////// 50// Constructor and Destructor
51//
52//
53//
54/**/////////////////////////////////////////////////////////////////////////// 55JobDeque::JobDeque(void)
56{
57 m_usRunAmount = 0;
58}
59
60JobDeque::~JobDeque(void)
61{
62}
63
64/**/////////////////////////////////////////////////////////////////////////// 65//
66//
67//
68/**/////////////////////////////////////////////////////////////////////////// 69void JobDeque::Start( CSendJob *pJob, BOOL bManualStart)
70{
71 ATLASSERT(pJob && _T("Argument of pJob is NULL in JobDeque::Start."));
72 if(!pJob)
73 {
74 return;
75 }
76
77 JobStartInformation jobInfo(pJob, bManualStart);
78 deque<JobStartInformation >::iterator itFinder = find(m_jobDeque.begin(), m_jobDeque.end(), jobInfo);
79 if(itFinder == m_jobDeque.end())
80 {
81 m_jobDeque.push_back(jobInfo);
82 pJob->SetSyncState(CSendJob::COPYING);
83 }
84 Launch();
85 return;
86}
87
88void JobDeque::Completed( CSendJob* pJob )
89{
90 ATLASSERT(pJob && _T("Argument of pJob is NULL in JobDeque::Start."));
91 if(!pJob)
92 {
93 return;
94 }
95 --m_usRunAmount;
96 Validate();
97 Launch();
98 return;
99}
100
101void JobDeque::Launch()
102{
103 if(m_usRunAmount < m_susParallelAmount && m_jobDeque.size() > 0)
104 {
105 m_jobDeque.front().GetJob()->Send(m_jobDeque.front().IsManualStart());
106 m_jobDeque.pop_front();
107 ++m_usRunAmount;
108 Validate();
109 }
110}
111
112JobDeque* JobDeque::GetJobDeque()
113{
115 if (NULL == m_spJobDeque)
116 {
117 if 进入互斥区成功
118 {
119 if(NULL == m_spJobDeque)
120 {
121 m_spJobDeque = new JobDeque();
122 }
退出斥区
123 }
132 }
133 return m_spJobDeque;
134}
135
136void JobDeque::DestroyJobDeque()
137{
138 if(m_spJobDeque)
139 {进入互斥区
140 delete m_spJobDeque;
141 m_spJobDeque = 0;
退出互斥区
142 }
143}
144
145#ifdef _DEBUG
146void JobDeque::Validate()
147{
148 if(m_susParallelAmount < m_usRunAmount || m_usRunAmount < 0)
149 {
150 ATLASSERT(_T("The counter in JobDeque work error!"));
151 }
152}
153#endif
154
在这里就省略全局实例的的例子,在上面的例子中不仅有取得实例的静态函数,还有销毁实例的静态函数。客户可以任何地方及任何时间调用获得单件的函数,但是用户只能且必须在应用程序的最后出口处调用释放实例的函数。也曾看过C++实现的单件模式的例子,例子中没有销毁函数。这与C#,JAVA这样的纯面向对象实现的例子非常地相似。可是C++中并没有自动垃圾回收装置,所以还是要我们自己在某个地方销毁new出来的这个全局实例对象。在上面可以看出C++实现的单件模式不及全局变量来得简单而高效。我持有这个观点是基于以下两个方面的考虑
(1) 利用全局变量的方案来产生全局唯一实例时不用考虑多线程的情况,在上面的例子中产生实例时考虑了多线程情况。
(2) 利用全局变量的方案产生的实例对象会在程序退出时自动地销毁对象,而单件模式需明确地delete实例。
这里并没有断言全局变量就优于单件模式,出于如下的考虑可能又会选用单件模式
(1) 全局变量的方式使代码的耦合度提高了,而且需事先总体设计好。须保证开发人员和后续的维护人员不能再去产生的新的实例,theApp全局实例是MFC世界基础的知识了。
(2) 单件实例的构造函数的参数中需要自定义对象实例时也会迫使开发人员选用单件模式。
我认为两种方案都有优点和缺点,不能说一个方案绝对地优于另外一个方案。究竟选用哪种方案取决于应用的环境的约束以及开发人员的偏好。这里阐述了一些个人的观点,希望能起到抛砖引玉的作用。