C++设计模式——单例模式

来源:互联网 发布:运行java程序 编辑:程序博客网 时间:2024/06/04 11:54

设计模式并不只是一种方法和技术,更是一种思想、一个方法论。设计模式与具体编程语言无关,其主要目的是使人们可以更加简单方便地复用成功的设计和体系结构、建立面向对象的设计思想、面向接口编程、编写的程序高内聚、低耦合。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路

单例模式是创建型模式之一,也是设计模式中最简单的形式之一。有懒汉式、饿汉式、注册式这三种形式。我将先用懒汉式举例。

目的:在系统中,使该类对象的实例只有一个。

动机:从业务角度来分析,在某些情况下,设计者或用户会要求核心对象在系统中只有该类的一个实例。例如多个任务需要调用打印机类的打印方法,而实际情况是计算机连接的打印机只有一个,此时必须限制打印机类的实例个数。

    再例如从技术角度来分析,某个类被多个用户共用,但该类包含的数据量非常庞大,相应的方法实现起来也十分复杂。如果在系统中创建该类的多个实例,不仅会严重消耗系统的资源,还有可能使用户接受的数据不一致。此时只需创建该类的一个实例就可以满足用户需求。

实现原理:为了使得系统中只有该类的一个实例,我们不能让客户端可以随意创建该类的对象。那么我就将该类的构造方法私有化(private),使得客户端(即类外环境)无法直接创建类的实例。例如下列语句是错误的


这种情况意味着我们无法在main中直接创建Singleton对象。那么此时该怎么在main中创建该类的实例呢?这里有一个思路,就是我们需要通过调用类中的其他方法来间接调用类的构造方法,也就是让类自己负责创建自己的实例。可是调用类中的方法都是需要以对象名.方法名的形式在main中调用的,现在我们连对象都无法创建,更别提调用了。实际上,在C++中,除了公有构造方法,还有一种类型的方法是可以不通过对象实例来调用的,叫做静态方法。我们可以用一个公有的静态方法(public static)来实现类构造方法的调用,即客户端通过类名调用这个静态方法,然后这个静态方法再调用构造函数来创建一个类的实例。
那么我们创建的实例应该放在哪里呢?给客户端的指针吗?假如我们真的将实例给了客户端的一个指针,那么当我们需要将实例给多个指针时(多个指针象征多个用户),我们需要让指针们相互赋值。这样的话,如果用来赋值的第一个指针并没有成功创建实例,那么之后的相互赋值则是在传递一个无意义的地址值。而且由于后边的代码是相互赋值,不会再有创建实例的机会。所以,我们必须将创建出来的实例放在类中的一个静态指针,并且在客户创建时就赋给用户,这样客户端的用户可以访问共同的位置。在用户访问的同时将实例赋给用户,用户无需相互赋值。若第一个用户未创建成功,之后的用户也会为了得到实例而进行创建操作。
到目前为止,我们已经可以创建类的实例,但是我们似乎只是在换一种方式创建对象,并没有任何限制系统中实例个数的操作。如果仅仅进行上述的操作,那么每个用户都会创建一个对象,累积在内存中,这样的结果和我们单例模式的目的相违背。其实限制对象个数的方法很简单,只要在静态方法中添加一个判断条件即可。


必要条件:

1.将该类的构造方法私有化(private),使得客户端(即类外环境)无法直接创建类的实例。

2.设置一个公有的静态方法,在静态方法中调用构造方法。

3.类中建立一个私有的静态指针,用来存放创建的唯一实例。


懒汉式代码:在客户端需要使用的时候才创建对象。




运行结果:


饿汉式代码:在类初始化时就已经创建了对象。




运行结果:



注册式代码:父类实例指针只有一个实例,可以是父类,也可以是子类。父类、子类被调用时创建实例。



运行结果:


如果将main函数中第一行注释掉,运行结果:

在我看一些设计模式的书时,有些书并没有讲到注册式代码。我觉得原因有二:
1.使用注册式代码时,父类构造函数不能再为private而是protected,因为子类在构造时会访问父类构造,如果父类构造为private,子类是无法访问父类构造的,链接时会出错。
2.在子类中需要将父类添加为友元类,否则父类在构造子类实例时无法访问子类构造。
这两点原因都在破坏类的封装性,而标准的单例模式对类的封装性要求极高。而且在描写注册式代码时,我发现我渐渐将构思的重点转向了如何更方便的构造子类,这是工厂模式要考虑的事情。所以这么做这并不是纯正的单例模式思想。而且由于C++中友元类不可以被继承,假如子类还要向下泛化子类,那么这个新的子类需要手动添加代码friend class Singleton,父类也要添加创建子类的判断,类间耦合度太高,这是很麻烦的事情。也许我们会在需要单例模式和工厂模式混合使用的情况下,才探讨这个方法吧。
我也看到很多单例模式中涉及到线程间互斥的问题,这个问题我会继续研究,在我觉得我的想法足够切实可靠的时候,我会回来修改这篇文章。

0 0