剑指Offer-题2-实现单例模式

Author Avatar
Orange 3月 24, 2018
  • 在其它设备中阅读本文章

题目:实现单例(Singleton)模式

设计一个类,我们只能生成该类的一个实例

解析

题目要求明显,设计单例模式:仅生成单个实例
如此需要考虑一下几点:

  • 构造函数需“隐藏”,且另设方法通过静态变量判断是否创建实例充当构造函数
  • 返回实例的方法必定使用 new 创建实例(否则将产生值传递生成多个单例),故需存储静态实例指针待销毁时使用
  • 静态变量用于存储变量创建状态,将其设为 int flag ?此举造成冗余,不如利用静态实例指针

理清三点后奉上本人渣码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Singleton
{
public:
~Singleton();
static Singleton* s_pInstance;
static Singleton* getInstance();
private:
Singleton();
// other members......
}

// C++中仅有静态整形常量才可于类内初始化,否则导致所有实例均含该静态成员
Singleton* Singleton::s_pInstance = nullptr;

Singleton::Singleton() {}

Singleton::~Singleton()
{
if (nullptr != Singleton::s_pInstance)
{
delete Singleton::s_pInstance;
Singleton::s_pInstance = nullptr;
}
}

Singleton* Singleton::getInstance()
{
if (nullptr == Singleton::s_pInstance)
Singleton::s_pInstance = new Singleton();
return Singleton::s_pInstance;
}

看似完美,却木有考虑多线程下同时执行 if (nullptr == Singleton::s_pInstance) 情况,加锁加锁……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 使用window API操控锁
class LockBase
{
public:
LockBase()
{
::InitializeCriticalSection(&cs);
}
~LockBase()
{
::DeleteCriticalSection(&cs);
}
void lock()
{
::EnterCriticalSection(&cs);
}
void unlock()
{
::LeaveCriticalSection(&cs);
}
private:
CRITICAL_SECTION cs;
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Singleton
{
public:
~Singleton();
static Singleton* s_pInstance;
static Singleton* getInstance();
private:
Singleton();
// other members......
};

Singleton* Singleton::s_pInstance = nullptr;

Singleton::Singleton() {}

Singleton::~Singleton()
{
if (nullptr != Singleton::s_pInstance)
{
delete Singleton::s_pInstance;
Singleton::s_pInstance = nullptr;
}
}

Singleton* Singleton::getInstance()
{
LockBase lockBase;
lockBase.lock();
if (nullptr == Singleton::s_pInstance)
Singleton::s_pInstance = new Singleton();
lockBase.unlock();
return Singleton::s_pInstance;
}

very nice ! 😀 等等!其实这个锁的范围有点大了,我们可以再次优化,缩小范围

1
2
3
4
5
6
7
8
9
10
11
12
Singleton* Singleton::getInstance()
{
if (nullptr == Singleton::s_pInstance)
{
LockBase lockBase;
lockBase.lock();
if (nullptr == Singleton::s_pInstance)
Singleton::s_pInstance = new Singleton();
lockBase.unlock();
}
return Singleton::s_pInstance;
}

不但缩小范围,而且提升效率,感觉很棒棒(๑•̀ㅂ•́)و✧ 对照下书本
有木有搞错!结果书上说还有更加优秀的解法=_=

力荐解法一:利用静态构造函数

静态构造函数:定义类时仅自动执行一次的函数
可能要让您失望了,书中使用C#,而C++没有静态构造函数!😂
先看看书中C#神级代码

1
2
3
4
5
6
7
8
9
public class Singleton
{
private static Singleton instance = new Singleton();
public static Singleton getInstance()
{
return instance;
}
// other members......
};

再看看 C++ 模拟代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Singleton
{
public:
~Singleton();
static Singleton* s_pInstance;
static Singleton* getInstance();
private:
Singleton();
// other members......
};

Singleton* Singleton::s_pInstance = new Singleton();

Singleton* Singleton::getInstance()
{
return Singleton::s_pInstance;
}

其实是在全局域中直接设定,略显牵强😝

力荐解法二:实现按需创建实例

解法一中过早创建实例占用内存,所以可用高级语言中 类仅在使用时载入 的特性改进代码
C++无法实现此方法,以下依旧是书中C#神级代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton
{
public static Singleton getInstance()
{
return Nested.instance;
}

class Nested
{
// internal表示该变量仅可在同项目内调用
internal static Singleton instance = new Singleton();
};

// other members......
};

本题扩展

实现可继承单例,派生类都只能产生一个实例
可以在单例类中使用指针数组判断对应子类标记是否已存在,若存在直接返回,反之创建子类,应加锁实现

CC许可协议署名非商业性使用相同方式共享
本文采用 CC BY-NC-SA 3.0 Unported 协议进行许可