C++单例模式实现

embedded/2024/11/17 0:16:39/

单例模式(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;
}


http://www.ppmy.cn/embedded/138125.html

相关文章

git命令提交项目

此为linux下的命&#xff0c; windows的话&#xff0c;去掉sudo即可 *转载至链接 http://www.eqicode.com/ 1、进入项目代码根目录&#xff0c;执行&#xff1a; sudo git init 把这个目录变成git可以管理的仓库。此时在文件加下&#xff0c;会出现一个 .git的隐藏文件&#…

LeetCode 40-组合总数Ⅱ

题目链接&#xff1a;LeetCode40 欢迎留言交流&#xff0c;每天都会回消息。 class Solution {List<List<Integer>> rs new ArrayList<>();LinkedList<Integer> path new LinkedList<>();public List<List<Integer>> combinatio…

【golang-技巧】-线上死锁问题排查-by pprof

1.背景 由于目前项目使用 cgo golang 本地不能debug, 发生死锁问题&#xff0c;程序运行和期待不一致&#xff0c;通过日志排查可以大概率找到 阻塞范围&#xff0c;但是不能找到具体问题在哪里&#xff0c;同时服务器 通过k8s daemonset 部署没有更好的方式暴露端口 获取ppr…

开发中SQL积累

1.SQL中判断varchar类型是否为空&#xff1f; 检查 NULL 值&#xff1a; WHERE column_name IS NULL 检查空字符串&#xff1a; WHERE column_name 结合 NULL 和空字符串的检查&#xff1a; WHERE column_name IS NULL OR column_name 2.TRIM函数 作用&#xff1a;…

ODC 如何精确呈现SQL耗时 | OceanBase 开发者工具解析

前言 在程序员或DBA的日常工作中&#xff0c;编写并执行SQL语句如同日常饮食中的一餐一饭&#xff0c;再寻常不过。然而&#xff0c;在使用命令行或黑屏客户端处理SQL时&#xff0c;常会遇到编写难、错误排查缓慢以及查询结果可读性不佳等难题&#xff0c;因此&#xff0c;图形…

redis高性能键值数据库技术简介

什么是redis redis是远程字典服务&#xff08;Remote Dictionary Server &#xff09;的简写&#xff0c;是一个完全开源的高性能的Key-Value数据库&#xff0c;提供了丰富的数据结构如string、Hash、List、SetSortedset等等。数据是存在内存中的&#xff0c;同时Redis支持事务…

Verilog HDL学习笔记

Verilog HDL&#xff08;Hardware Description Language&#xff09;是在一种硬件描述语言&#xff0c;类似于计算机的高级编程设计语言&#xff0c;它具有灵活性高&#xff0c;容易学习和使用等特点&#xff0c;同时Verilog能够通过文本的形式来描述数字系统的硬件结构和功能。…

GPT o1 模型使用及API调用

智匠MindCraft最新加入的o1-preview和o1-mini模型。本文将详细介绍这两款模型的技术参数、应用场景及价格对比。 1. o1-preview模型 最大输入&#xff1a;128K tokens最大输出&#xff1a;32K tokens输入价格&#xff1a;107元/百万tokens输出价格&#xff1a;426元/百万tokens…