单例模式(Singleton Pattern)是设计模式中的一种,它用来保证系统中最多只能存在一个它的实例,其做法是由类自身来创建和持有它的对象实例,把对实例的创建权和管理权都控制在自己手中,以便控制实例数目。

关于如何在 C++ 中实现单例模式的讨论已经太多了,我只是简单介绍一下可以继承的单例类。

首先介绍一下通常所见的单例类的写法,不妨设这个类叫做 Singleton。

Singleton.h:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef _SINGLETON_H_
#define _SINGLETON_H_

#include <memory>

class Singleton
{
public:
    static Singleton& GetInstance();

private:
    Singleton();
    ~Singleton();

    // Use auto_ptr to make sure that the allocated memory for instance
    // will be released when program exits (after main() ends).
    static std::auto_ptr<Singleton> s_instance;
    friend class std::auto_ptr<Singleton>;

    Singleton(const Singleton&);
    Singleton& operator =(const Singleton&);
};

#endif

Singleton.cpp:

 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
#include "Singleton.h"
#include <iostream>
#include <boost/thread.hpp>

using namespace std;
using namespace boost;

auto_ptr<Singleton> Singleton::s_instance;

Singleton::Singleton()
{
    cout << "Construct Singleton" << endl;
}

Singleton::~Singleton()
{
    cout << "Destruct Singleton" << endl;
}

Singleton& Singleton::GetInstance()
{
    static mutex s_mutex;
    if (s_instance.get() == NULL)
    {
        mutex::scoped_lock lock(s_mutex);
        if (s_instance.get() == NULL)
        {
            s_instance.reset(new Singleton());
        }
        // 'lock' will be destructed now. 's_mutex' will be unlocked.
    }
    return *s_instance;
}

这个类写的也不完美啦,比如双重判定也会有失效的时候,不过凑合用吧,哈哈。不过话说 boost 库里也有 singleton,我为什么要自己写个呢,无奈地飘过。

废话不多说了,上面的单例类基本上解决了多线程安全问题、实例内存自动释放问题,算是一段可以使用的程序。不过如果系统中有大量单例类(这时候也得好好考虑一下 design 有没有问题),每个都要这么写一番岂不是很麻烦?要是可以写一个单例基类,以后再创造单例类的时候直接继承一下多方便啊。不过很明显的问题就在那个 static 对象指针,这个用来保存唯一实例的静态变量如果定义在基类里面,那所有的子类都只能用这同一个变量来保存它们各自的实例了,社会主义国家总得让每个子类都过上温饱生活吧!

以前的时候我还真不知道该怎么解决这个问题,但 05 年用了 WTL(Windows Template Library)之后,我才意识到模板类可以帮助我(话说我真的是自己想到的,虽然现在搜一下能搜到一大堆)。这里要用的还不是普通的模板类,而是像 ATL、WTL 里面那样把要定义的类自身放入模板参数中,形如 class MyClass : public Base<MyClass> {};。这样做有很多优点啦,最显著的比如不需要虚表(节省内存哦)、多态函数的调用在编译时就确定了(既加快了运行速度,也有利于编译器对代码进行优化)。

不妨把这个单例基类叫做 ISingleton 吧,看起来好像是个 interface 呢。代码如下:

 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
34
35
36
37
38
39
40
41
42
#ifndef _ISingleton_H_
#define _ISingleton_H_

#include <memory>
#include <boost/thread.hpp>

template <typename T>
class ISingleton
{
public:
    static T& GetInstance()
    {
        static boost::mutex s_mutex;
        if (s_instance.get() == NULL)
        {
            boost::mutex::scoped_lock lock(s_mutex);
            if (s_instance.get() == NULL)
            {
                s_instance.reset(new T());
            }
            // 'lock' will be destructed now. 's_mutex' will be unlocked.
        }
        return *s_instance;
    }

protected:
    ISingleton() {}
    ~ISingleton() {}

    // Use auto_ptr to make sure that the allocated memory for instance
    // will be released when program exits (after main() ends).
    static std::auto_ptr<T> s_instance;

private:
    ISingleton(const Singleton&);
    ISingleton& operator =(const ISingleton&);
};

template <typename T>
std::auto_ptr<T> ISingleton<T>::s_instance;

#endif

要利用 ISingleton 创建一个自己的单例类,比如 MySingleton,可以使用如下的代码:

 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
#include "Singleton.h"
#include "ISingleton.h"
#include <iostream>

using namespace std;

class MySingleton : public ISingleton<MySingleton>
{
public:
    // blah blah

private:
    MySingleton()
    {
        cout << "Construct MySingleton" << endl;
    }

    ~MySingleton()
    {
        cout << "Destruct MySingleton" << endl;
    }

    friend ISingleton<MySingleton>;
    friend class auto_ptr<MySingleton>;

    MySingleton(const MySingleton&);
    MySingleton& operator =(const MySingleton&);
};

最最重要的,千万不要忘了把 MySingleton 的构造和析构函数弄成 private 的,还要添加两个友元。有人说 ISingletonMySingleton 的析构函数都要加 virtual,我倒是觉得没有必要呢,你说呢?另外要注意,MySingleton 不能被继承哦。

Like this post? Share on: TwitterFacebookEmail

Comments

So what do you think? Did I miss something? Is any part unclear? Leave your comments below.

comments powered by Disqus

Keep Reading


Published

Last Updated

Category

程序开发

Tags

Stay in Touch