首页 > 其他分享 >单例模式

单例模式

时间:2023-04-29 09:22:40浏览次数:35  
标签:pInstance Singleton 函数 对象 创建对象 模式 static 单例

单例模式

单例模式是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。即:类在内存中只能存在一个示例对象

设计思路:

创建的对象肯定是要存入内存的,也就是用户态的那片空间(栈、堆、读写段、只读段),如果直接将类对象的创建放在类的外面,无论放在哪块区域都是行不通的。故需要强制不能在类外创建对象,而对象的创建都需要构造函数,对象的销毁都需要析构函数。这样,我们可以将构造函数和析构函数设置为私有的,强制用户不可以在类外创建对象。于是,我们需要在类的里面创建对象,而在堆上创建对象是我们程序员最可控的区域,故我们选择在堆上创建对象。

使用场景:全局唯一的对象,网页库、日志记录器

// 以下写法为懒汉模式,单例实例在第一次被使用时才进行初始化,这叫做延迟初始化。

#include <stdio.h>
#include <iostream>

using std::cout;
using std::cin;
using std::endl;

class Singleton
{
public:
    // 将对象的创建放在函数里面
    // 在类的外面访问成员函数除了使用对象加点的方式或者指针的方式(而此时的目的就是创建对象,此时是没有对象的,这两种方法行不通)
    // 还可以将成员函数用static修饰,这样在类外可以通过类名加作用域限定符的形式直接调用来创建对象(静态成员函数的特色)
    static Singleton* getInstance()
    {
        // 为保证 只能有一个对象的需求 需要一个指针来接收堆对象的指针
        
        // Singleton* _pInstance;// 这样每次进来_pInstance同样为空,每次还是会进入if语句,起不到效果,因此需要将其设置为全局变量
        // 但全局变量要求是能不使用就不要使用的,故将其设置为类里的一个静态数据成员
        if(_pInstance == nullptr)
        {
            _pInstance = new Singleton();// 在类中创建堆对象
        }
        return _pInstance;
    }

    // 同样,为了能够在类外用类名加作用域限定符的方式去释放对象,将该函数设置为静态成员函数
    static void destroy()
    {
        // 防止double free
        if(_pInstance != nullptr)
        {
            delete _pInstance;
            _pInstance = nullptr;
        }
    }
private:
    Singleton()
    {
        cout << "Singleton()" << endl;
    }
    // 同样,析构函数需要放在private区域,防止在类外使用时,出现double free的问题
    ~Singleton()
    {
        cout << "~Singleton()" << endl;
    }
private:
    // 静态成员函数只能访问静态数据成员,故需要将其用static修饰
    static Singleton* _pInstance;   
};

// 静态数据成员必须在读写区进行初始化
// 饱汉(懒汉)模式,等到用的时候再创建实例对象
Singleton* Singleton::_pInstance = nullptr;

// 饿汉模式,程序启动就立即创建实例对象
// Singleton* Singleton::_pInstance = Singleton::getInstance();			

int main()
{

    Singleton* ps1 =  Singleton::getInstance();
    Singleton* ps2 =  Singleton::getInstance();
    
    printf("ps1 = %p\n",ps1);
    printf("ps2 = %p\n",ps2);

    Singleton::destroy();
    Singleton::destroy();
    Singleton::destroy();
    Singleton::destroy();

    // delete ps1;  // 将析构函数设置为private时,这样使用会报错

    return 0;
}

运行结果:

几个点:

为什么只能在类里面创建对象?

答:如果允许在类外创建对象,不论在那片内存区域创建对象都无法满足一个类只能创建一个实例对象的需求。

为什么getInstance()函数要设计为静态成员函数?

答:因为经分析,不可以在类的外面创建对象,而在类外访问类的成员可以通过对象加点的或者指针的形式去访问,但是此时对象并没有创建出来,这两种方式都是行不通的。所以,我们可以将getInstance()设计为静态的成员函数,这样可以直接在类外通过作用域限定符加上类名的方式调用此函数,进而创建唯一的对象。

为什么要单独设计destroy()这个函数来释放对象?

答:因为析构函数已经被设置为私有成员了,在类外是无法调用的。并且同样为了能在类外直接使用,我们也需要将其设计为静态成员函数。

为什么静态数据成员_pInstance需要用static修饰?

答:因为静态成员函数只能访问静态的数据成员。

注意点:

饱汉模式线程不安全,饿汉模式线程安全。

单例模式的自动释放

1.友元类的形式

主要是利用栈对象在对象离开作用域时,自动调用析构函数的性质,来实现自动释放。

代码实现如下:

// 将此类声明为Singleton类的友元类,这样才能访问其私有成员
class AutoRelease
{
public:
    AutoRelease()
    {
        cout << "AutoRelease()" << endl;
    }

    ~AutoRelease()
    {
        cout << "~AutoRelease()" << endl;
        if(Singleton::_pInstance)
        {
            delete Singleton::_pInstance;
            Singleton::_pInstance = nullptr;
        }
    }
};

int main(int argc, char **argv)
{
    Singleton *ps = Singleton::getInstance();

    AutoRelease ar;//栈对象,离开作用域时,自动调用AutoRelease的析构函数,进而释放单例对象
    /* ps->destroy(); */

    return 0;
}

2.内部类+静态数据成员

代码实现:

#include <iostream>

using std::cout;
using std::endl;

//2、单例模式自动释放的第2种形式:内部类 + 静态数据成员

class Singleton
{
public:
    static Singleton *getInstance()
    {
        if(nullptr == _pInstance)
        {
            _pInstance = new Singleton();
            /* static AutoRelease ar;//ok */
        }

        return _pInstance;
    }

    static void destroy()
    {
        if(_pInstance)
        {
            delete _pInstance;
            _pInstance = nullptr;
        }
    }

private:
    Singleton()
    {
        cout << "Singleton()" << endl;
    }

    ~Singleton()
    {
        cout << "~Singleton()" << endl;
    }

private:
    //内部类
    class AutoRelease
    {
    public:
        AutoRelease()
        {
            cout << "AutoRelease()" << endl;
        }
    
        ~AutoRelease()
        {
            cout << "~AutoRelease()" << endl;
            if(_pInstance)
            {
                delete _pInstance;
                _pInstance = nullptr;
            }
        }
    };
    
private:
    static Singleton *_pInstance;
    // 静态数据成员
    static AutoRelease _ar;
};

Singleton *Singleton::_pInstance = getInstance();
Singleton::AutoRelease Singleton::_ar;

int main(int argc, char **argv)
{
    Singleton *ps = Singleton::getInstance();
    return 0;
}

3.atexit函数+饿汉模式

atexit函数会注册function函数,当进程正常结束后,注册的function会被自动调用,function函数被注册几次,该函数就会被调用几次。

代码实现:

static Singleton *getInstance()
{
    //多线程不安全的问题
    if(nullptr == _pInstance)
    {
        _pInstance = new Singleton();
        atexit(destroy);	// 注册destroy函数,进程结束时自动被调用
    }

    return _pInstance;
}

4.pthread_once + atexit

在多线程条件下,若保证once_control唯一,那么init_routine函数只会被执行一次

代码实现:

#include <stdlib.h>
#include <pthread.h>
#include <iostream>

using std::cout;
using std::endl;

//4、pthread_once  + atexit
class Singleton
{
public:
    static Singleton *getInstance()
    {
        pthread_once(&_once, init);
        return _pInstance;
    }

    static void init()
    {
        _pInstance = new Singleton();
        atexit(destroy);
    }

    static void destroy()
    {
        if(_pInstance)
        {
            delete _pInstance;
            _pInstance = nullptr;
        }
    }

private:
    Singleton()
    {
        cout << "Singleton()" << endl;
    }

    ~Singleton()
    {
        cout << "~Singleton()" << endl;
    }

private:
    static Singleton *_pInstance;
    static pthread_once_t _once;
};

Singleton *Singleton::_pInstance = nullptr; //饱(懒)汉模式
/* Singleton *Singleton::_pInstance = Singleton::getInstance();//饿汉模式 */
pthread_once_t Singleton::_once = PTHREAD_ONCE_INIT;//程序一启动就初始化,保证它是唯一的

int main(int argc, char **argv)
{
    Singleton *ps = Singleton::getInstance();

    return 0;
}

标签:pInstance,Singleton,函数,对象,创建对象,模式,static,单例
From: https://www.cnblogs.com/MyXjil/p/17363572.html

相关文章

  • MFC-CListCtrl-InsertColumn报告模式下插入一列
     inti=mylist4.InsertColumn(0,_T("姓名"),LVCFMT_CENTER,70,-1);//在报告模式下插入一列/*参数1:intnCol要插入列的列号参数2:LPCTSTRlpszColumnHeading字符串地址参数3:intnFormat=LVCFMT_LEFT指定列对齐方式的整数,缺省值是左对齐。......
  • Cloud集群模式XXL-job开启自动注册执行器
    在微服务架构下,传统的springtask、Quartz已经不再推荐使用,在数据与业务增长的同时,定时任务处理数据是避免不了的,抛开单节点不谈,集群服务如果想要执行以往的定时任务最简单粗暴的方式就是使用分布式锁来保证唯一性,但是由于不可控原因,可能在执行任务期间所以压力集中到其中一个节......
  • 从实例出发,了解单例模式和静态块
    就算你没有用到过其他的设计模式,但是单例模式你肯定接触过,比如,Spring中bean默认就是单例模式的,所有用到这个bean的实例其实都是同一个。单例模式的使用场景什么是单例模式呢,单例模式(Singleton)又叫单态模式,它出现目的是为了保证一个类在系统中只有一个实例,并提供一个访问它的......
  • Java设计模式-单例模式
    一、前言单例模式是一种设计模式,它确保一个类只能创建一个实例,并提供一种全局访问这个实例的方式。在Java中,单例模式可以通过多种方式来实现,其中最常见的是使用私有构造函数和静态方法实现二、基本语法在Java中,实现单例模式的方式有多种,其中最常见的实现方式包括以下几种:1、......
  • 开源大数据可视化工具,企业打造高效办公新模式!
    在现代化办公环境中,做好数据资源管理,实现数字化办公,是大多数企业现下追求的梦想。采用开源大数据可视化工具可以帮助广大用户实现这一梦想。那么,什么是开源大数据可视化工具,又有什么功能和特点?本文就针对广大用户关心的问题做一个阐述和介绍,希望能给大家带来帮助。1、关于开源大......
  • 单例模式的几种写法(包含双检锁写法)
    饿汉式单例类1.publicclass2.{3.private4.5.}6.7.privatestaticSingletoninstance=new8.9.privatestatic10.return11.}12.} 饿汉式提前实例化,没有懒汉式中多线程问题,但不管我们是不是调用getInstance()都会存在一个......
  • Python单例的常用几种实现方法
    这两天在看自己之前写的代码,所以正好把用过的东西整理一下,单例模式,在日常的代码工作中也是经常被用到,所以这里把之前用过的不同方式实现的单例方式整理一下。装饰器的方式这种方式也是工作中经常用的一种,用起来也比较方便,代码实现如下defSingleton(cls):_instance={......
  • 设计模式
    第一章软件架构设计原则1.1开闭原则开闭原则的核心思想就是面向抽象编程开闭原则是面向对象编程中的一个设计原则,也被称为OCP原则。它的定义为:软件中的对象(类、模块、函数等)应该对扩展开放,对修改关闭。换句话说,一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码......
  • 单例模式
    一、线程安全性的讲解1、视频截图 2、线程安全性的代码加不加临界区进行验证1//!!!!!!!!!加C++泛型编程与STL开发实战QQ群:726114806下载代码和交流2#include<afxwin.h>3#include<iostream>4#include<stdio.h>5usingnamespacestd;67CRITICAL_SECTIONg_......
  • 【策略设计模式详解】C/Java/JS/Go/Python/TS不同语言实现
    简介策略模式(StrategyPattern)属于行为型设计模式。将每一个算法封装到具有共同接口的独立类中,根据需要来绑定策略,使得具体实现和策略解耦。当你想使用对象中各种不同的算法变体,使用if...else所带来的复杂和难以维护,可使用策略模式。或者当有许多相同类,它们仅在执行某些行为时......