好久不见,又来到了这里。。

博客中断了如此之久,当然是有借口的。首先是单件模式的特殊性,它涉及到多线程!OMG,小弟才疏学浅,还从未写过一个多线程的程序,多进程的倒是写过一个,不过跟该模式就没太大关系了。另外,前一篇工厂模式,费了九牛二虎之力,却只弄清楚一个对象的创建,未免太桑人心了。。再者,寒假一放,就堕落了。。每天睡到中午,整体出去玩,哪有心思看设计模式。。。

凡此种种,都成为我没有更新博客的借口。但饭要吃,博客也还是要写,今天恨下心来,果断先放弃多线程的部分。从延迟初始化,析构等等概念入手,先写它一篇,将来弄清楚了多线程,再写一篇。。

单件模式,都说是最简单的设计模式,其实也是,你要做的事情就是私有化构造函数,赋值函数,设置一个唯一的静态变量,再写一个静态函数来获取这个唯一的静态变量,就成为单件模式了。

如何做呢,先看看Java可以怎么做(再做个声明,以下讨论,没有考虑多线程情况):

public class Singleton {
    // 声明并初始化一个静态变量,由于是静态的,该变量唯一
    private static Singleton single = new Singleton();
    // 初始化
    private Singleton() {
        System.out.println("Singleton constructor...");
    }
    // 模拟析构函数实现
    public void finalize() {
        System.out.println("Singleton destructor...");
    }
    // 返回唯一的静态变量
    public static Singleton getInstance() {
        return single;
    }
    // 普通方法
    public void method() {
        System.out.println("The method of Singleton...");
    }
}

main函数如下:

        System.out.println("Appliction started...");
        Singleton sc1,sc2;
        System.out.println("Start using instance...");
        sc1 = Singleton.getInstance();
        sc1.method();
        sc2 = Singleton.getInstance();
        sc2.method();
        System.out.println("Appliction leaved...");

我们看到,这种方法是先声明一个静态变量并初始化为空,获取实例时判断,若为空,说明仍未创建,则实例化该类给静态,并返回该静态变量。其运行结果如下:

Appliction started...

Start using instance...

Singleton constructor...

The method of Singleton...

The method of Singleton...

Appliction leaved...

可以看到,Singleton只被初始化了一次。

至于为什么没有调用finalize 方法。也许是Java一些内部机制决定的,我不继续研究了,毕竟析构也不是Java应该关心的内容。

Java代码实现单例模式,我不做过多介绍了,毕竟不是我熟悉的语言,Head First上的说明也非常详细。

下面就开始考虑如何移植到c++上。

受Java代码影响,首先想到的是使用静态指针:

class Singleton {
private:
	static Singleton* single;
	Singleton() {
		std::cout << "Singleton constructor..." << std::endl;
	}
	// avoid to use copy constructor function.
	Singleton(const Singleton&);
	const Singleton& operator=(const Singleton&);
public:
    ~Singleton() {
	    std::cout << "Singleton destructor..." << std::endl;
	}
    // returns an unique instance
	static Singleton* getInstance() {
		if (single == NULL) {
			single = new Singleton();
		}
		return single;
	}
	void method() {
	    std::cout << "The method of Singleton..." << std::endl;
	}
};
Singleton* Singleton::single = NULL;

其运行结果与Java相同。

但是,c++没有垃圾回收器!把内存的释放交给OS,让程序运行结束时再释放?根据单件模式的思想,其实这样做也是有一定道理的,因为单件模式中的单件,一定是要等整个程序运行结束才会被释放。所以,new了以后不delete实际上也是可以接受的。

但是心里总有疙瘩,所以查阅了很多资料,发现了智能指针。

智能指针就是被设计出来解决c++没有垃圾收集器这一缺陷的。智能指针能使对象在没有引用时,自动销毁。具体实现我就不详述了,详情可以参阅百度百科——智能指针中的内容。

智能指针在 <memory> 中定义,用法如下:

class Singleton {
private:
    static std::auto_ptr<Singleton> single;
    Singleton() {
        std::cout << "Singleton constructor..." << std::endl;
    }
    // avoid to use copy constructor function.
    Singleton(const Singleton&);
    const Singleton& operator=(const Singleton&);
public:
    ~Singleton() {
        std::cout << "Singleton destructor..." << std::endl;
    }
    // returns an unique instance
    static Singleton* getInstance() {
        if (single.get() == NULL) {
            single.reset( new Singleton() );
        }
        return single.get();
    }
    void method() {
        std::cout << "The method of Singleton..." << std::endl;
    }
};
std::auto_ptr<Singleton> Singleton::single(NULL);

使用了智能指针以后,运行结果如下:

Appliction started...

Start using instance...

Singleton constructor...

The method of Singleton...

The method of Singleton...

Appliction leaved...

Singleton destructor...

可以看到,在程序执行完成后,单例被析构了,很完美~

受到一些资料的启发,想起c++是可以直接声明静态变量的!

于是有了如下代码:

class Singleton {
private:
    static Singleton single;
    Singleton() {
        std::cout << "Singleton constructor..." << std::endl;
    }
    // avoid to use copy constructor function.
    Singleton(const Singleton&);
    const Singleton& operator=(const Singleton&);
public:
    ~Singleton() {
        std::cout << "Singleton destructor..." << std::endl;
    }
    // returns an unique instance
    static Singleton* getInstance() {
        return &single;
    }
    void method() {
        std::cout << "The method of Singleton..." << std::endl;
    }
};
Singleton Singleton::single;

直接声明一个单件,在获取实例时,返回其地址就行了。这样看来,智能指针似乎没有多大的意义了。。

其结果如下:

Singleton constructor...

Appliction started...

Start using instance...

The method of Singleton...

The method of Singleton...

Appliction leaved...

Singleton destructor...

可以看到,与之前唯一的不同是,单件的构造在程序运行之前了。。这样的话,如果这个单件很大,就会造成不必要的浪费,这当然是我们想避免的。

于是我想,局部静态变量能否达到效果呢?

class Singleton {
private:    // 不再声明静态成员变量
    Singleton() {
        std::cout << "Singleton constructor..." << std::endl;
    }
    ...
public:
    // returns an unique instance
    static Singleton* getInstance() {
        // 声明为静态局部变量,只在第一次进入函数时初始化
        static Singleton single;
        return &single;
    }
    ...
};

经测试,结果如下:

Appliction started...

Start using instance...

Singleton constructor...

The method of Singleton...

The method of Singleton...

Appliction leaved...

Singleton destructor...

perfect! 初始化,析构,全都是那么称心如意。也许这就是c++中最简单的实现方案?(不知道多线程会造成什么影响-_-||)

回过头去看Java,就发现原来Java不支持局部静态变量。。。

话说回来,如果多线程中,局部静态变量可以被初始化多次的话,这最简单的方案就无法使用了,而且还不易扩展,也许多线程中,智能指针还能派上用场。

待我了解了多线程以后,再回来研究。。。