设计模式--单例模式

简介:

单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。

上面是引用维基百科上的定义。                                                                      

 

既然单例模式只允许存在一个对象,那么对象的拷贝,赋值就都是不允许的,因此把拷贝构造函数、赋值操作符全部声明为private

 

第一种实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class  Singleton
{
  private :
      static  Singleton *m_instance;
      Singleton(){} //隐藏构造函数
      Singleton( const  Singleton &){} //隐藏拷贝构造函数
      Singleton& operator=( const  Singleton &a){} //隐藏赋值操作符
      ~Singleton(){} //隐藏析构函数
  public :
     static  Singleton *getInstance()
     {
         if (m_instance == NULL)
             m_instance = new  Singleton;
         return  m_instance;
     }
     
};
Singleton * Singleton::m_instance = NULL;

 

这种实现很明显满足单例模式的要求,但是有两个问题 (1)我们new的对象没有被delete,(2)这种设计不是线程安全的(两个线程可能同时判断m_instance == NULL,这样就有两个实例被创建了),为了解决上面的问题,第二种实现如下


第二种实现

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
class  Singleton
{
  private :
      static  Singleton *m_instance;
      Singleton(){} //隐藏构造函数
      Singleton( const  Singleton &){} //隐藏拷贝构造函数
      Singleton& operator=( const  Singleton &a){} //隐藏赋值操作符
      ~Singleton(){} //隐藏析构函数
      class  DelInstance
      {
      public :
         ~DelInstance()
         {
             if (Singleton::m_instance)
                 delete  Singleton::m_instance;
         }
      };
      static  DelInstance delIns; //负责回收new出来的对象;
 
  public :
     static  Singleton *getInstance()
     {
         if (m_instance == NULL)
         {
             lock(); //加锁(lock 和 unlock是随便写的函数,c++本身没有)
             if (m_instance == NULL)
                 m_instance = new  Singleton;
             unlock(); //释放锁
         }
         return  m_instance;
     }
 
};
Singleton::DelInstance Singleton::delIns;
Singleton * Singleton::m_instance = NULL;

 

这里我们使用了另一个私有内嵌类DelInstance,在Singleton内定义了一个静态的的对象delIns来负责回收new出来的对象,因为静态对象delIns在程序结束时会自动调用自己析构函数从而释放m_instance指向的内存,这里新手可能会犯两个错误:

1、不使用额外的类,直接把delete语句写在singleton的析构函数中。这种做法是错误的,因为我们是通过new出来的singleton实例,程序结束时不会自动调用析构函数,再者如果真的调用了就会进入一个无限循环的状态,即singleton的析构函数是为了删除一个singleton对象,删除该对象时,又会调用singleton的析构函数,这样一直循环下去。

2、delIns成员不定义成static。这也是错误的,如果delIns不是static,那么delIns就要等到singleton对象析构时才会析构,但是delIns的目的就是要析构singleton,这就矛盾了。如果delIns是static的,他的生命期和他所在的类对象没有关系,他相当于是全局的,当他的生命期到的时候就会自动析构,从而析构singleton。

getInstance中我们使用了double-check来保证线程安全,只有当m_instance是NULL时,线程才会加锁。这样也保证了只创建了一个对象实例。

 

这是一种懒汉模式,即等到需要时才创建对象的实例。                                                                                 本文地址


第三种实现

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
class  Singleton
{
  private :
      static  Singleton *m_instance;
      Singleton(){} //隐藏构造函数
      Singleton( const  Singleton &){} //隐藏拷贝构造函数
      Singleton& operator=( const  Singleton &a){} //隐藏赋值操作符
      ~Singleton(){} //隐藏析构函数
      class  DelInstance
      {
      public :
         ~DelInstance()
         {
             if (Singleton::m_instance)
                 delete  Singleton::m_instance;
         }
      };
      static  DelInstance delIns; //负责回收new出来的对象;
 
  public :
     static  Singleton *getInstance()
     {
         return  m_instance;
     }
 
};
Singleton::DelInstance Singleton::delIns;
Singleton * Singleton::m_instance = new  Singleton;

 

这是属于饿汉模式,即一开始就创建一个类的实例,以后都返回其地址,是线程安全的。


第四种实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class  Singleton
{
  private :
      static  Singleton s;
      Singleton(){} //隐藏构造函数
      Singleton( const  Singleton &){} //隐藏拷贝构造函数
      Singleton& operator=( const  Singleton &a){} //隐藏赋值操作符
      ~Singleton(){} //隐藏析构函数
  public :
     static  Singleton *getInstance()
     {
         return  &s;
     }
     
};
 
Singleton Singleton::s;

 

饿汉模式,这种实现定义一个私有的静态对象实例,(注意不能在类中定义自身的非静态对象,因为这样会形成无限循环定义,而静态对象因为保证只有一个副本,因此不会循环定义),这也是线程安全的


第五种实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class  Singleton
{
  private :
      Singleton(){} //隐藏构造函数
      Singleton( const  Singleton &){} //隐藏拷贝构造函数
      Singleton& operator=( const  Singleton &a){} //隐藏赋值操作符
      ~Singleton(){} //隐藏析构函数
  public :
     static  Singleton *getInstance()
     {
         lock(); //c++11 可以不用加锁
         static  Singleton s;
         unlock();
         return  &s;
     }
 
};

 

懒汉模式,这种实现只有当第一次调用getInstance时定义局部静态变量s,以后直接返回。c++11之前需要加锁,因为变量的初始化操作不是原子操作,不加锁就不是线程安全的的;c++11则不用加锁,因为c++11保证:如果指令逻辑进入一个未被初始化的声明变量,所有并发执行应当等待完成该变量完成初始化(见here)。


注意

 

对于第一、二、三、四种方式,都有可能导致static initialization order fiasco (可参考here)。因为c++中,在全局或名字空间范围内的全局变量,在一个类中被声明为static,或,在一个文件范围被定义为static。这三种变量统称“非局部静态对象”,这三种对象的初始化顺序“C++”未作明确规定。因此如果有个类在构造函数中调用了getInstance:

struct Foo {
    Foo() {
        Singleton::getInstance();
    }
  };
  Foo foo;
不能保证foo初始化时,m_instance已经初始化





本文转自tenos博客园博客,原文链接:http://www.cnblogs.com/TenosDoIt/p/3639395.html,如需转载请自行联系原作者

目录
相关文章
|
2月前
|
设计模式 存储 SQL
PHP中的设计模式:单例模式的探索
在PHP开发中,单例模式是一种常用的设计模式,它确保一个类只有一个实例,并提供一个全局访问点。本文将通过一个简单的例子,逐步引导你理解如何在PHP中实现和利用单例模式,以及它在实际项目中的应用价值。
|
2天前
|
设计模式 SQL 安全
PHP中的设计模式:单例模式的深入探索与实践在PHP开发领域,设计模式是解决常见问题的高效方案集合。它们不是具体的代码,而是一种编码和设计经验的总结。单例模式作为设计模式中的一种,确保了一个类仅有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的基本概念、实现方式及其在PHP中的应用。
单例模式在PHP中的应用广泛,尤其在处理数据库连接、日志记录等场景时,能显著提高资源利用率和执行效率。本文从单例模式的定义出发,详细解释了其在PHP中的不同实现方法,并探讨了使用单例模式的优势与注意事项。通过对示例代码的分析,读者将能够理解如何在PHP项目中有效应用单例模式。
|
2天前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入探讨与实践
在PHP开发领域,设计模式是提升代码可读性、可维护性和扩展性的重要工具。本文聚焦于单例模式——一种确保类只有一个实例,并提供全局访问点的设计模式。我们将从定义、实现方式、应用场景以及在PHP框架中的运用等方面进行详细探讨,旨在帮助PHP开发者更好地理解和运用单例模式。
|
14天前
|
设计模式 存储 安全
PHP中的设计模式:单例模式深度解析
在软件开发的广袤天地中,设计模式如同璀璨星辰,指引着程序员们解决复杂问题的方向。其中,单例模式以其独特的魅力,确保了一个类仅有一个实例,并提供了一个访问它的全局访问点,成为众多项目中不可或缺的设计智慧。本文旨在深入探讨PHP中单例模式的实现方式、应用场景及背后的哲理,引导读者思考其在现代软件架构中的重要性与运用策略。
29 5
|
18天前
|
设计模式 安全 Java
Java 编程中的设计模式:单例模式的深度解析
【9月更文挑战第22天】在Java的世界里,单例模式就像是一位老练的舞者,轻盈地穿梭在对象创建的舞台上。它确保了一个类仅有一个实例,并提供全局访问点。这不仅仅是代码优雅的体现,更是资源管理的高手。我们将一起探索单例模式的奥秘,从基础实现到高级应用,再到它与现代Java版本的舞蹈,让我们揭开单例模式的面纱,一探究竟。
25 11
|
15天前
|
设计模式 存储 缓存
PHP中的设计模式:单例模式的深入解析
在PHP开发中,设计模式是提高代码可维护性、扩展性和重用性的关键技术之一。本文将深入探讨PHP中的单例模式,包括其定义、实现方式、应用场景以及优缺点。通过对单例模式的全面剖析,帮助开发者更好地理解和应用这一设计模式,从而编写出更加高效和优雅的PHP代码。
|
14天前
|
设计模式 安全 Java
设计模式--单例模式Singleton
这篇文章详细介绍了单例模式Singleton的八种实现方式,包括饿汉式(静态常量和静态代码块)、懒汉式(线程不安全和线程安全的同步方法、同步代码块)、双重检查、静态内部类和枚举。每种方式都有详细的代码示例和优缺点说明,帮助理解单例模式的应用和选择适合的实现方法。
设计模式--单例模式Singleton
|
17天前
|
设计模式 SQL 安全
PHP中的设计模式:单例模式的深入探索与实践在PHP的编程实践中,设计模式是解决常见软件设计问题的最佳实践。单例模式作为设计模式中的一种,确保一个类只有一个实例,并提供全局访问点,广泛应用于配置管理、日志记录和测试框架等场景。本文将深入探讨单例模式的原理、实现方式及其在PHP中的应用,帮助开发者更好地理解和运用这一设计模式。
在PHP开发中,单例模式通过确保类仅有一个实例并提供一个全局访问点,有效管理和访问共享资源。本文详细介绍了单例模式的概念、PHP实现方式及应用场景,并通过具体代码示例展示如何在PHP中实现单例模式以及如何在实际项目中正确使用它来优化代码结构和性能。
|
19天前
|
设计模式 数据库连接 PHP
PHP中的设计模式:单例模式的深入解析与实践
在PHP开发中,设计模式是提高代码可维护性、扩展性和复用性的关键技术之一。本文将深入探讨单例模式——一种确保类只有一个实例,并提供该实例的全局访问点的设计模式。我们将从单例模式的基本概念入手,剖析其在PHP中的应用方式,并通过实际案例展示如何在不同场景下有效利用单例模式来优化应用架构。
|
2月前
|
设计模式 存储 负载均衡
【五】设计模式~~~创建型模式~~~单例模式(Java)
文章详细介绍了单例模式(Singleton Pattern),这是一种确保一个类只有一个实例,并提供全局访问点的设计模式。文中通过Windows任务管理器的例子阐述了单例模式的动机,解释了如何通过私有构造函数、静态私有成员变量和公有静态方法实现单例模式。接着,通过负载均衡器的案例展示了单例模式的应用,并讨论了单例模式的优点、缺点以及适用场景。最后,文章还探讨了饿汉式和懒汉式单例的实现方式及其比较。
【五】设计模式~~~创建型模式~~~单例模式(Java)