考察:要求实现各种变形,懒汉式、饿汉式

https://mp.weixin.qq.com/s?__biz=MzkzNzIzNjI4Nw==&mid=2247485722&idx=1&sn=e315a9f3e893db98276a18117a24612e&source=41#wechat_redirect

有时候我们想让一个类能够保证自己仅仅能生成一个实例化对象 (对应上面的窗口类去保证自己创建对象时候的唯一性) ,并提供一个访问它的全局访问点,该实例被所有程序模块共享。

# 单例模式的设计思路

应该怎么保证上面窗口类在按钮类的回调函数里面调用的时候
能够 不用 if else 判断 而由窗口类对象自己去保证唯一性呢?

第一步

首先应该不能让外界调用new的方式去创建他的对象 因为如果这样的话   外部还是可以产生多个对象了。
我们还是得引入if else来判断 ,
所以我们就应该阻止其他程序建立对象,   阻止的方式就是把构造函数声明为私有

第二步

其次应该让这个窗口类对象应在作为静态成员对象声明在自己类的内部声明 因为这样的话 我们就可以在类里面实现对这个对象的唯一性判断 (由于它是静态的 各个类对象之间会共有这一个成员对象)

第三步

最后 应该在判断完这个静态成员类对象唯一以后应该有一个全局唯一接口 (静态成员函数) 让外面访问这个对象

总结一下实现步骤: 1. 私有化构造函数,不让其他程序创建的对象初始化。 2. 在本类中 new 一个静态本类对象。 3. 定义一个静态成员函数,它的功能是让其他程序可以通过这个函数获取到本类的对象。

# 饿汉式:

1. 资源的占用:

饿汉模式 在类创建的就得 new 好它的静态成员对象 故占用空间

2. 类中静态成员 初始化顺序不确定所导致的异常.

也就是说类中的两个静态成员

getInstance 和 Instance 不同编译单元中的初始化顺序是未定义的,

如果在 Instance 初始化完成之前调用

getInstance () 方法会返回一个未定义的实例

由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间

class Singleton
{
    //1.声明构造函数为私有
    private:
    Singleton(){};
    //2.声明一个本类的对象
    private:
    static Singleton* instance;
    //3.对外提供本类对象的公共接口
    public:
    static Singleton* getInstance() 
    {
        return instance;
    }
};
Singleton* Singleton::instance=new Singleton;
  • 静态成员函数
  1. 静态成员函数可以在类内直接定义也可以在类外定义(初始化 / 实现),在类外实现时不需要加 static。
  2. 静态成员函数只能访问静态数据成员和静态成员函数,普通成员函数可以访问静态成员函数和静态数据成员
  3. 静态成员函数属于类,不属于任意一个类对象
  4. 静态成员函数没有 this 指针
  5. 可以使用 <类名>::< 函数名 > 访问,也可由类对象使用 (./->) 访问

第一条解释:不能在类声明中初始化静态成员变量,因为声明描述了如何分配内存,但不分配内存。静态类成员位于 ** 静态存储区,* 并 * 不是类对象的组成部分,所以需要在类声明之外使用单独的语句进行初始化。在类声明中不可初始化静态数据成员的一种例外情况是,静态数据成员为整型或枚举型 const。

  • 静态数据成员
  1. 对于类静态数据成员,无论有多少个该类的对象,该静态数据成员在内存中只有一份拷贝 (其他普通数据成员,每个类对象都有自己的内存拷贝),该静态数据成员由所有该类对象共享
  2. 静态数据成员存储在全局数据区,在定义时分配存储空间,程序运行结束时销毁
  3. 静态数据成员不能再类中定义和初始化,只能在类中声明,在类外进行定义和初始化,默认初始化为 0
  4. 静态数据成员的初始化为 <类型名> < 类名 >::< 变量名 > = < 值 >
  5. 静态数据成员遵从 public private protected 访问规则
  6. 静态数据成员可以直接使用类名加作用域运算符 (::) 直接访问 < 类名 >::< 变量名 >(访问规则允许的情况下)

# 懒汉式:

class Singleton
{
    //1.声明构造函数为私有
    private:
    Singleton(){};
    //2.声明一个本类的对象
    private:
    static Singleton* instance;
    //3.对外提供本类对象的公共接口
    public:
    static Singleton* getInstance() 
    {
        if(instance==NULL)
        {
            instance=new Singleton;
        }
    return instance;
    }
};
Singleton* Singleton::instance=NULL;

1. 内存泄露

①析构函数没有被执行:

程序退出时,析构函数没被执行.

这在某些设计不可靠的系统上会导致资源泄漏,

想一想上面的 Instance 指向的空间什么时候释放呢?

更严重的问题是,该实例的析构函数什么时候执行?

如果在类的析构行为中有必须的操作,

比如关闭文件,释放外部资源,那么上面的代码无法实现这个要求。

我们需要一种方法,正常的删除该实例。

**2. 线程安全 **

②线程不安全:我们注意到在 static Singleton* getInstance () 方法中,
是通过 if 语句判断 静态实例变量 是否被初始化来觉得是否进行初始化,
那么在多线程中就有可能出现多次初始化的问题。
比方说,有两个多线程同时进入到这个方法中,
同时执行 if 语句的判断,
那么就会出现两次两次初始化静态实例变量的情况。

# 懒汉式(线程安全):

1. 用双重检测锁 DLC 解决线程安全

class Singleton 
{ 
  public: 
  static pthread_mutex_t mutex; 
  static Singleton* getInstance(); 
  protected: //定义线程互斥锁
  Singleton() 
  { 
      pthread_mutex_init(&mutex); 
  } 
  private: 
  static Singleton* p; 
}; 
  pthread_mutex_t Singleton::mutex; //初始化互斥锁
  Singleton* Singleton::p = NULL; //类外初始化静态指针:通过作用域调用
  Singleton* Singleton::getInstance() //获取实例
  { 
      if (NULL == p) 
      { 
          pthread_mutex_lock(&mutex); 
          if (NULL == p) 
              p = new Singleton(); 
          pthread_mutex_unlock(&mutex); 
      } 
      return p; 
  }

2. 内部静态变量实现懒汉模式

class Singleton 
{ 
    public: 
    static pthread_mutex_t mutex; //静态成员:互斥锁变量
    static Singleton* getInstance(); //静态成员函数
    protected: 
    Singleton() 
    { 
        pthread_mutex_init(&mutex); 
    } 
}; 
pthread_mutex_t Singleton::mutex; //类外初始化
Singleton* Singleton::getInstance() //类外定义/实现
**{ **
    pthread_mutex_lock(&mutex); 
**    static singleton ss; **
    pthread_mutex_unlock(&mutex); 
**    return &ss; **
}

简单工厂模式:

1. 实现了客户端和创建对象的工厂类打交道(传入参数让工厂直到应该创建什么类型的对象),与具体实现类的解耦。
2. 增加新的功能是通过修改源代码实现,不符合开闭原则