单例模式之 --饿汉模式与懒汉模式

一、饿汉模式与懒汉模式

http://mp.weixin.qq.com/s?__biz=MjM5MjI2MzkyNw==&mid=2652124208&idx=1&sn=3cd17cb1b3b6ad03d933b7625c8a7e8d&chksm=bd486fa88a3fe6be014711cd9cf28f1868f5db17047398df24b83801e9753c33af8f352f7980&mpshare=1&scene=23&srcid=04132mNXOgPvECHgZtnKomMm#rd

http://m.blog.csdn.net/snow_5288/article/details/76306226
先了解一下什么是设计模式?
设计模式的定义: 设计模式是一套被反复使用,多数人知晓的、经过分类编目的、代码设计的总结,使用设计模式是为了可重用代码,让代码更容易被他人理解,保证代码可靠性。
设计模式的分类:按照目的可分为三类,创建型模式,结构型模式,行为型模式;按照范围,即模式主要处理类之间的关 系还是对象之间的关系分为类模式和对象模式,设计模式主要被广泛应用于面向对象编程。
从最简单的单例模式开始学起:单例模式,根据名字拆分就可以知道这个模式的意思,即单个实例的模式。

什么是单个实例的模式,既然是面向对象,那么一个类只允许产生一个实例对象的话,就是单例模式。且单例类提供获取这个唯一实例的接口

饿汉单例:即在最开始的时候,静态对象就已经创建完成;
设计方法是类中包含一个静态成员指针,该指针指向该类的一个对象,提供一个公有的静态成员方法,返回该对象指针;为了使得对象唯一,还需要将构造函数设为私有,
单例模式之懒汉单例:
(解决:线程安全问题(上锁),死锁问题(利用RAII,双检查),利用RAII(资源获得初始化,及自己定义Lock类,并完成构造与析构),保证用的同一把锁(利用引用),双检查)
所谓懒汉模式,就是尽可能晚的创建这个对象的实例,即在单例类第一次被引用时将自己初始化;其实C++里很多地方都是类似这样的思想,比如晚绑定,写时拷贝技术等,就是尽量使资源的利用最大化,不要让空闲的人还占着有限的资源。
懒汉单例的实现:其实就是将静态对象指针初始化为NULL(这就是与饿汉模式的最大不同),当单例类被引用的时候,先进行判断指针是否为NULL,为NULL的时候再去实例一个对象,不会NULL时,直接返回静态对象指针;达到保证只有一个实例的目的。
单例的分类:懒汉模式(lazy load)、饿汉模式。
懒汉模式:只有当调用对象是才创建对象–相对而言,复杂—-但适用性高,各种场景下都适用。
饿汉模式:在函数一开始就创建(main)–简单高效,但是在某些场景下有缺陷—-适用性会受到限制,动态库。
单例模式的实现过程中,需要注意以下三点:

1. 单例类的构造函数、拷贝构造、赋值运算符重载都为私有;
2. 提供一个私有的静态私有成员指针变量;(在类外进行初始化)
3. 提供一个公有的静态工厂方法(用来返回这唯一一个对象);

单例模式的线程安全问题:
以懒汉单例为例,如果此时多线程进行操作,简单点以两个线程为例,假设pthread_1刚判断完 intance 为NULL 为真,准备创建实例的时候,切换到了pthread_2, 此时pthread_2也判断intance为NULL为真,创建了一个实例,再切回pthread_1的时候继续创建一个实例返回,那么此时就不再满足单例模式的要求了, 既然这样,是因为多线程访问出的问题,那我们就来加把锁,使得线程同步;

单例模式的优点

1. 提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它;
2. 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。
3. 允许可变数目的实例,基于单例模式我们可以扩展,使用与单例控制相似的方法来获得指定个数的对象实例。(即单例类内有多个静态对象指针成员,每次当单例类被引用时随机分配一个实例对象);

单例模式的缺点

1. 因为单例模式没有抽象层,所以单例类的扩展有很大的困难;
2. 单例类的职责过重,即是工厂角色,提供了工厂的方法,同时又充当了产品的角色;
3. 滥用单例类会带来一些负面问题;

单例模式的适用场景

1. 系统只需要一个实例对象,或者考虑到资源消耗的太大而只允许创建一个对象。
2. 客户调用类的单个实例只允许使用一个公共访问点,除了该访问点之外不允许通过其它方式访问该实例 (就是共有的静态方法)

二、代码块

#include<iostream>
using namespace std;
#include<assert.h>

//单例模式

//一、饿汉模式一
class Hungry1
{
public:
    static Hungry1& GetInstance()
    {
        assert(_inst);
        return *_inst;
    }
    void Print()
    {
        cout << "Hungry1->" << _a << endl;
    }
private:
    Hungry1() :_a(0)//构造函数私有化
    {
        cout << "Hungry1" << endl;
    }
    Hungry1(const Hungry1& h);//防拷贝 在类内只给声明,不给定义
    Hungry1& operator = (const Hungry1& h);

    static Hungry1* _inst;//静态成员在类外进行初始化
    // 指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例
    int _a;
};
Hungry1* Hungry1::_inst = new Hungry1();//静态成员在main函数之前初始化

void Hungry1Test()
{
    cout << "main.c" << endl;//打这一句是为了证明,Hungry的对象是在main之前就已经初始化了
    //Hungry h1 = Hungry::GetInstance();//拷贝不了
    Hungry1::GetInstance().Print();
    Hungry1::GetInstance().Print();
    Hungry1::GetInstance().Print();
}
//饿汉模式二
class Hungry2
{
public:
    static Hungry2& GetInstance()
    {
        static Hungry2 _inst;//静态变量只会创建一次
        return _inst;
    }
    void Print()
    {
        cout << "Hungry2->" << _a << endl;
    }
private:
    Hungry2() :_a(0)//构造函数私有化
    {
        cout << "Hungry2" << endl;
    }
    Hungry2(const Hungry2& h);//防拷贝 在类内只给声明,不给定义
    Hungry2& operator = (const Hungry2& h);

    int _a;
};

void Hungry2Test()
{
    cout << "main.c" << endl;//打这一句是为了证明,Hungry的对象是在main之前就已经初始化了
    //Hungry h1 = Hungry::GetInstance();//拷贝不了
    Hungry2::GetInstance().Print();
    Hungry2::GetInstance().Print();
    Hungry2::GetInstance().Print();
}
//单例模式二
//懒汉模式一
class Singleton1
{
public:
    static Singleton1& GetInstance()
    {
        if (_inst == NULL)
        {
            _inst = new Singleton1();
            cout << "once" << endl;
        }
        else
        {
            cout << "not once" << endl;
        }
        return *_inst;
    }
    void Print()
    {
        cout << "Singleton1->" << _a << endl;
    }
private:
    Singleton1() :_a(0)
    {
        cout << "Singleton1" << endl;
    }
    Singleton1(const Singleton1& s1);
    Singleton1& operator = (const Singleton1& s1);

    static Singleton1* _inst;
    int _a;
};
Singleton1* Singleton1::_inst = NULL;

void Singleton1Test()
{
    cout << "main.c" << endl;
    //Singleton1 s1;//创建不了对象
    Singleton1::GetInstance().Print();
    Singleton1::GetInstance().Print();
    Singleton1::GetInstance().Print();
}
//懒汉模式二
#include<mutex>

class Lock
{
public:
    Lock(mutex& mtx)
        :_mtx(mtx)
    {
        _mtx.lock();//加锁
    }
    ~Lock()
    {
        _mtx.unlock();//解锁
    }
private:
    Lock(const Lock& mtx);//防止拷贝
    Lock& operator = (const Lock& mtx);//防止赋值
    mutex& _mtx;//这里加上引用很重要(保证每次使用的是同一把锁)
};
class Singleton2
{
public:
    static Singleton2& GetInstance()
    {
        if (_inst == NULL)//双检查机制,只有创建实例的时候才进行加锁解锁来提高代码效率
        {
            //_mtx.lock();//加锁,加锁期间其余线程不能访问该临界区
            Lock lock(_mtx);//RAII机制(自己进行加锁与解锁操作)
            //利用RAII机制自己“造轮子”避免死锁
            //RAII:资源获取即初始化,相当于资源分配和初始化时原子操作,其中不会中断

            //lock_guard<mutex>lock(_mtx);//调用库里的函数来避免自己造轮子的开销
            if (_inst == NULL)//只有当第一次进来_inst为空的时候才创建实例
            {
                _inst = new Singleton2();
                cout << "once" << endl;

                //内存栅栏(防止编译器优化)保证构造函数先执行
                //保证tmp已完成构造函数的初始化
                //Singleton2* tmp = new Singleton2;
                //MemoryBarrier();//优化----内存栅栏,防止编译器对指令流水进行乱序优化
                //_inst = tmp;
            }
            //_mtx.unlock();//解锁
        }
        else
        {
            cout << "not once" << endl;
        }
        return *_inst;
    }


    void Print()
    {
        cout << "Singleton2->" << _a << endl;
    }
private:
    Singleton2() :_a(0)
    {
        cout << "Singleton2" << endl;
    }
    Singleton2(const Singleton2& s1);
    Singleton2& operator = (const Singleton2& s1);

    static Singleton2* _inst;// 指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例 
    int _a; // 单例类里面的数据
    static mutex _mtx; // 保证线程安全的互斥锁
};
Singleton2* Singleton2::_inst = NULL;//给静态指针赋初值为NULL (这点很重要)
mutex Singleton2::_mtx ;//_mtx会调用mutex默认的无参构造函数,所以不用初始化

void Singleton2Test()
{
    cout << "main.c" << endl;
    //Singleton1 s1;//创建不了对象
    Singleton2::GetInstance().Print();
    Singleton2::GetInstance().Print();
    Singleton2::GetInstance().Print();
}

三、结果展示

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值