c++ 单例模式介绍与实现以及单例模式的自动释放

要求:一个类只能创建一个对象

不能是栈对象

只能是堆对象,且在类内部

应用场景

只要出现全局对象的地方都可以使用单例模式

取代全局变量的位置

替换原因:全局对象不便管理

全局唯一的资源或只需要出现一个

配置文件、网页库、字典库、日志系统写的日志对象

配置文件的信息是程序的输入

优点

内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例

避免对资源的多重占用(比如写文件操作)

缺点

没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

解决方案

将构造函数私有化

这样该类就不会被实例化

在这里插入图片描述
定义一个私有的静态数据成员指针,指向本类型的对象,即单例对象

私有的本类型静态数据成员指针
在这里插入图片描述

初始化本类型静态数据成员指针

在这里插入图片描述

定义一个静态的成员函数getInstance方法用于获取实例

静态成员函数无需对象也可调用

如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。

static Singleton * getInstance()
{
    if(_pInstance == nullptr) {
        _pInstance = new Singleton();
    }
    return _pInstance;
}

若本类型还未创建过对象,new一个本类型对象,若已经存在本类型对象,返回本类型对象指针

两种模式

饿汉模式

饿汉模式就是立即加载,一般情况下在调用getInstance 方法之前就已经产生了实例,也就是在类加载的时候已经产生了一个单里对象了。

饿汉模式是线程安全的

代码演示(java实现)
在这里插入图片描述

初始化

Singleton * Singleton::instance = new Singleton();

懒汉模式

什么时候需要这个单例对象时,再去创建实例。

代码演示(java实现)
在这里插入图片描述
懒汉模式的问题

if(instance == nullptr)
{
    instance = new Singleton();
}

这两行代码既包含了读操作,也包含了修改操作,这两行代码是两个不走,不是原子性的。此代码存在线程安全问题

改进方案:在多线程下运行时,需要进行加锁操作(最终实现)

class Singleton {
public:
    static Singleton * getInstance(){
        if(instance == nullptr) {
            pthread_mutex_lock(&mutex);//需要在数据成员内加入mutex
            if(instance == nullptr) {
                instance = new Singleton();
            } 
            pthread_mutex_unlock(&mutex);
        }
        return instance;
    }
    static void destroy()
    {
        if(_pInstance) {
            //在类内部执行delete表达式
            delete _pInstance;
        }
    }
private:
    Singleton();
    ~Singleton();
    static pthread_mutex_t mutex;
    static Singleton * instance;
};

初始化

Singleton * Singleton::instance = nullptr;

单例模式的自动释放

必要性

程序需要检查是否存在内存泄漏,如果单例对象没有进行内存回收,那么检测工具会认为发生了内存泄漏,会干扰程序员判断是否发生内存泄漏

检测内存泄漏的工具valgrind

使用方式

valgrind --tool=memcheck --leak-check=fukk ./test(可执行文件)
valgrind --tool=massif --stack=yes ./client
使友元模式进行设计

单例对象声明与实现

class AutoRelease; //前向声明

class Singleton
{
    friend AutoRelease;
public:
    static Singleton * getInstance();
private:
    Singleton();
    ~Singleton();
private:
    static Singleton * _pInstance;
};

SIngleton * Singleton::getInstance()
{
    if(nullptr == _pInstace)
    {
        _pInstance = new Singleton;
    }
    return _pInstance;
}

自动释放类声明与实现

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

可自动释放的单例对象实现

//静态成员初始化
Singleton * Singleton::_pInstance = nullptr;

void test0()
{
    Singleton *p1 = Singleton::getInstance();
    //自动释放实现需要声明一个AutoRelease类
    AutoRelease p2;
}
内部类加静态数据成员形式

单例对象的声明

//单例对象自动释放的实现方式之一: 嵌套类+静态对象
class Singleton
{
public:
    static Singleton * getInstance()
    {
        if(nullptr == _pInstance)
            _pInstance == new Singleton();
        return _pInstance;
    }
private:
    class AutoRelease
    {
    public:
        ~AutoRelease()
        {
            cout << "~AutoRelease()" << endl;
            if(_pInstance)
                delete _pInstance;  //内部类可以直接使用外部类的数据成员
        }
    };
private:
    Singleton() {  cout << "Singleton()" << endl;  }
    ~Singleton() {  cout << "~Singleton()" << endl;}
    static SIngleton * _pInstance;
    static AutoRelease _ar;  //_ar是静态对象,位于静态存储区
                             //当程序退出main函数时会自动回收
                             //分配给单例对象的内存
    int _ix;
};

ar时AutoRelease类静态对象,位于全局静态区, pInstance是外部Singleton类静态对象指针,也位于全局静态区

因此当程序退出main函数时,会delete _at的同时会调用析构函数

从而可以自动回收分配给_pInstance 的内存空间

静态成员的初始化

Singleton * Singleton::_pInstance = nullptr;
Singleton::AutoRelease Singleton::_ar;

类调用

int main()
{
    Singleton * p1 = Singleton::getInstance();
    Singleton * p2 = SIngleton::getInstance();
    cout << "p1 = " << p1 << endl;
    cout << "p2 = " << p2 << endl;
    return 0;
}
atexit方式进行

atexit函数

函数说明

用来注册指向exit() 函数前执行的终止处理程序

当程序通过调用exit()或从main中返回时被调用。终止处理程序执行的顺序和注册时的顺序时相反的,终止处理程序没有参数传递。同一个函数若注册多次,那它也会被调用多次。

当一个子进程时通过调用fork函数产生时,它将继承父进程的所有终止处理程序。在成功调用exec系列函数后,所有的终止处理程序都会被删除

函数原型

#include <stdlib.h>
int atexit(void (*function)(void));

参数列表

function

即所要注册的终止处理函数指针

使用实例

#include <iostream>

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

class Singleton
{
public:
    static Singleton * getInstance()
    {
        //对于多线程环境,不安全
        if(nullptr == _pInstance)
        {
            _pInstance = new Singleton();
            atexit(destroy); 
        }
        return _pInstance;
    }

    static void destroy()
    {
        if(_pInstance)
        {
            delete _pInstance; //1、调用析构函数 2、operator delele
            _pInstance = nullptr;
        }
    }
private:
    Singleton()
    {
        cout << "Singleton()" << endl;
    }
    ~Singleton()
    {
        cout << "~Singleton()" << endl;
    }
private:
    static Singleton * _pInstance;
};

/* Singleton *Singleton::_pInstance = nullptr; //饱汉模式(懒汉模式)*/
Singleton * Singleton::_pInstance = getInstance(); //饿汉模式
pthread_once形式

pthread_once 函数

函数说明

在多线程环境中,有些事仅需要执行一次。通常当初始化应用程序时,可以比较容易地将其放在main函数中。但当你写一个库时,就不能在main里面初始化了,你可以用静态初始化,但使用一次初始化(pthread_once)会比较容易些

函数功能

本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次

函数原型

int pthread_once(pthread_once_t * once_control,
                 void(*init_routine)(void));

参数列表

once_control

表示是否执行过,必须初始化为PTHREad_ONCE_INIT

init_routine

初始化函数

使用实例

#include <iostream>

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

class Singleton
{
public:
    static Singleton * getInstance();
    static void init();
    static void destroy();
private:
    Singleton()
    {
        cout << "Singleton()" << endl;
    }
    ~Singleton()
    {
        cout << "~Singlenton()" << endl;
    }
private:
    static Singleton * _pInstance;
    static pthread_once_t _once;
};
Singleton * Singleton::getInstance() {
    pthread_once(&_once, init);

    return _pInstance;
}
void Singleton::init()
{
    _pInstance = new Singleton();
    atexit(destroy);
}
void Singleton::destroy()
{
    if(_pInstance)
    {
        delete _pInstance;
        _pInstance = nullptr;
    }
}
Singleton * Singleton::_pInstance = nullptr; //饱汉模式
// Singleton * Singleton::_pInstance = Singleton::getInstance();//饿汉模式
pthread_once_t Singleton::_once = PTHREAD_ONCE_INIT;
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值