首页 > 编程语言 >C++单例模式实现

C++单例模式实现

时间:2024-11-13 22:43:25浏览次数:3  
标签:pInstance Singleton nullptr 模式 AutoRelease static C++ 单例 once

单例模式(Singleton Pattern)是软件设计模式中的一种,用于确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。

一、初始版本(手动创建释放)

一个类只有一个实例的实现方法:

  • 隐藏构造函数,是外界无法创造对象
  • 通过类静态成员函数getInstance返回静态局部对象指针(指向堆空间的指针数据成员),确保对象生命周期和程序一致,并且在程序中唯一
  • 使用destory释放堆空间
#include <iostream>
using std::cout;
using std::endl;

class Singleton{
public:
    static Singleton *getInstance(){
        if(_pInstance==nullptr){
            _pInstance=new Singleton();
        }
        return _pInstance;
    }
    static void destory(){
        if(_pInstance){
            delete _pInstance;
            _pInstance=nullptr;
        }
    }
private:
    Singleton(){}
    static Singleton *_pInstance;
    int data;

};
Singleton *Singleton::_pInstance=nullptr;
int main()
{
    cout<<Singleton::getInstance()<<endl;
    cout<<Singleton::getInstance()<<endl;
    Singleton::destory();
    return 0;
}

缺点:

需要人为手动释放堆空间,容易疏忽造成内存泄露

二、RAII思想

RAII(Resource Acquisition Is Initialization)是一种编程思想,主要用于C++等需要手动管理资源的语言中。RAII的核心思想是将资源的生命周期与对象的生命周期绑定,通过对象的构造函数来获取资源,通过析构函数来释放资源。这样做的好处是,当对象超出作用域并被销毁时,其析构函数会自动被调用,从而释放资源,避免了内存泄漏和其他资源泄露的问题。

RAII的主要特点包括:

  • 资源获取即初始化:在对象构造时获取所需的资源,如内存、文件句柄、网络连接等。
  • 自动资源释放:对象在超出作用域后自动调用析构函数,释放在构造函数中获取的资源。

三、利用RAII思想实现自动释放

 1、利用另一个对象的生命周期管理资源

#include <iostream>
using std::cout;
using std::endl;
class Singleton;


class Singleton{
public:
    static Singleton *getInstance(){
        if(_pInstance==nullptr){
            _pInstance=new Singleton();
        }
        return _pInstance;
    }
    static void destory(){
        if(_pInstance){
            delete _pInstance;
            _pInstance=nullptr;
        }
    }
private:
    Singleton(){}
    static Singleton *_pInstance;
    int data;

};
Singleton *Singleton::_pInstance=nullptr;
class AutoRelease{
public:
    AutoRelease(){}
    ~AutoRelease(){
        Singleton::destory();
    }
};
int main()
{
    AutoRelease autoRls=AutoRelease();
    cout<<Singleton::getInstance()<<endl;
    cout<<Singleton::getInstance()<<endl;
    return 0;
}

缺点:仍然需要手动做额外的事情。

改进:嵌套类

2、嵌套类

Singleton中嵌套AutoRelease类,定义一个静态的AutoRelease成员_ar,创建Singleton对象自动产生一个AutoRelease对象,程序结束时会销毁全局静态区中的_ar,调用AutoRelease的析构函数,释放资源。

#include <iostream>
using std::cout;
using std::endl;

class Singleton{
public:
    static Singleton *getInstance(){
        if(_pInstance==nullptr){
            _pInstance=new Singleton();
        }
        return _pInstance;
    }
    static void destory(){
        if(_pInstance){
            delete _pInstance;
            _pInstance=nullptr;
        }
    }
private:
    class AutoRelease{
    public:
        AutoRelease(){}

        ~AutoRelease(){
            destory();
        }
    };
    Singleton(){}
    static Singleton *_pInstance;
    int data;
    static AutoRelease _ar;
};
Singleton *Singleton::_pInstance=nullptr;
Singleton::AutoRelease Singleton::_ar;
int main()
{
    cout<<Singleton::getInstance()<<endl;
    cout<<Singleton::getInstance()<<endl;
    return 0;
}

四、使用atexit函数释放资源

atexit 函数是 C 语言标准库中的一个函数,用于在程序正常退出时注册一个函数,以便在程序结束时自动调用。

int atexit(void (*function)(void));
  • function 参数是一个函数指针,指向一个不带参数且没有返回值的函数 
#include <iostream>
using std::cout;
using std::endl;

class Singleton{
public:
    static Singleton *getInstance(){
        if(_pInstance==nullptr){
            _pInstance=new Singleton();
        }
        return _pInstance;
    }
    static void destory(){
        if(_pInstance){
            delete _pInstance;
            _pInstance=nullptr;
        }
    }
private:
    Singleton(){
        atexit(destory);
    }
    static Singleton *_pInstance;
    int data;

};
Singleton *Singleton::_pInstance=nullptr;
int main()
{
    cout<<Singleton::getInstance()<<endl;
    cout<<Singleton::getInstance()<<endl;
    return 0;
}

五、线程安全

以上实现方式均为懒汉式,在多线程情况下可能会创建多个堆空间,而_pInstance只能指向一个,造成内存泄露。

Singleton *Singleton::_pInstance=nullptr;//懒汉式

可以使用饿汉式在程序最开始就创建,但这样可能带来内存压力。

Singleton *Singleton::_pInstance=getInstance();

使用pthread_once和atexit保障初始化代码只执行一次 ,实现线程安全

pthread_once 是 POSIX 线程库中的一个函数,用于在多线程环境中确保某个初始化函数只执行一次。这个函数特别有用,当你需要在程序启动时进行一些初始化操作,但又希望这些操作只执行一次,即使有多个线程同时尝试执行它们。

int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
  • once_control 是一个控制变量,必须指向一个初始化为 PTHREAD_ONCE_INIT 的 pthread_once_t 类型的静态或全局变量。
  • init_routine 是一个函数指针,指向一个不带参数且没有返回值的函数,这个函数包含了初始化代码。
#include <iostream>
#include<pthread.h>
using std::cout;
using std::endl;

class Singleton{
public:
    static Singleton *getInstance(){
        pthread_once(&_once,init);
        return _pInstance;
    }
    static void init(){
        _pInstance=new Singleton();
        atexit(destory);
    }
    static void destory(){
        if(_pInstance){
            delete _pInstance;
            _pInstance=nullptr;
        }
    }
private:
    Singleton(){}
    static Singleton *_pInstance;
    int data;
    static pthread_once_t _once;
};
Singleton *Singleton::_pInstance=nullptr;
pthread_once_t Singleton::_once=PTHREAD_ONCE_INIT;

int main()
{
    cout<<Singleton::getInstance()<<endl;
    cout<<Singleton::getInstance()<<endl;
    return 0;
}

标签:pInstance,Singleton,nullptr,模式,AutoRelease,static,C++,单例,once
From: https://blog.csdn.net/weixin_73809064/article/details/143711845

相关文章

  • C++ 左值引用和右值引用之间的转换
    intretVal(int&&v){cout<<"右值引用:";returnv;}intretVal(int&v){cout<<"左值引用:";returnv;}intretVal(constint&v){cout<<"const左值引用:";returnv;}i......
  • [Docker#7] 容器 | OOM | 常用命令 | 交互模式 | 批量处理
    目录什么是容器生活案例为什么需要容器?容器的生命周期3种特殊情况3.1容器OOM3.2容器异常退出3.3容器暂停容器命令清单Docker容器常用命令dockercreatedockerrundockerpsdockerlogsdockerexecdockerstartdockerstopdockerrestartdockerkilld......
  • 【c++】广度优先搜索详解
    BFS(图论)BFS全称是 BreadthFirstSearch,中文名是宽度优先搜索,也叫广度优先搜索。是图上最基础、最重要的搜索算法之一。所谓宽度优先。就是每次都尝试访问同一层的节点。如果同一层都访问完了,再访问下一层。这样做的结果是,BFS算法找到的路径是从起点开始的 最短 合法......
  • 【c++】游戏作品分享
    1.法术对战#include<iostream>usingnamespacestd;intmain(){ intyhp=444; intchp=2000; intaaa; intlan=2000; intdu=0; inttuy=0; for(inti=1;i<=9999;i++) { cout<<"你的血量:"<<yhp<<endl<<"电脑血量:&quo......
  • 【c++】游戏作品分享
     c++代码#include<bits/stdc++.h>#include<xiaohoucode.h>#include<time.h>#include<stdlib.h>#include<unistd.h>#include<termio.h>#include<ctype.h>#include<stdio.h>#include<math.h>usingnamespa......
  • Visual C++ 6.0中文版安装包下载教程及win11安装教程
    本文分享的是VisualC++6.0(简称VC++6.0)中文版安装包下载及安装教程,关于win11系统下安装和使用VC++6.0使用问题解答,大家在安装使用的过程中会遇到不同的问题,如遇到解决不了的问题请给我留言!一、安装包的下载vc6.0安装包下载连接:https://pan.quark.cn/s/979dd8ba4f35二、......
  • C++零基础入门&趣味学信息学奥赛_开发环境安装
    Arduino软件安装,安装树莓派Pico开发板及上传Pico固件目录1.安装Windows驱动程序1.1.下载安装arduino软件:1.2.安装开发板Pico1.3.上传Arduino兼容的Pico固件1.安装Windows驱动程序                                        ......
  • C++ 移动构造和拷贝构造函数匹配
    既有拷贝构造又有移动构造这个比较好理解,普通的函数匹配规则就可以。右值移动,左值拷贝。——《C++Primer》P477我们不能隐式地将一个右值引用绑定到一个左值。有拷贝构造但没有移动构造这种情况,右值也会被拷贝。如果一个类没有移动构造函数,函数匹配规则保证该类型的对象......
  • C++继承和参数化类型(模板)各自的优点
    在C++中,继承和参数化类型(模板)都是强大的代码重用机制,它们各自具有独特的优点。以下是对这两种机制优点的比较和归纳:C++继承的优点代码重用:继承允许子类继承父类的属性和方法,从而避免了重复编写相同的代码。这不仅提高了开发效率,还减少了代码中的冗余。扩展性:通过继承,可以创建......
  • C++ 逆向之常用字符集互转
    在过往的编程过程中,常常会因为碰到字符集问题而头痛,而每次在进行字符集转换的时候,各种搜索网上文档,想找字符集转换的示例程序,但是都不尽人意,本篇文章的目的就是彻底解决之前编程过程中对字符集认识以及字符集转换之间似懂非懂、云里雾里的状态,并在文章结尾附上ANSI、UNICODE和U......