【C++】22.单例模式+类型转换

news2024/11/19 4:45:30

1.单例模式

1°定义

之前已经学过一些设计模式

迭代器模式 -- 基于面向对象三大特性之一的 封装设计出来的 用一个迭代器类封装以后

不暴露容器结构的情况下 统一的方式访问修改容器中的数据

适配器模式 -- 体现的是一种复用

还有一些常见的设计模式如:工厂模式 装饰器模式 观察者模式 单例模式...

单例模式:一个类只能在全局(进程中)只有一个实例对象

什么场景下会使用?

比如一个进程中有一个内存池 进程中的多线程需要内存都要到这个内存池中取 那么这个内存池的类就可以设计单例模式

类的名称是随着你的场景给的 比如你的是内存池 那么你就定义成MemoryPool

2°懒汉模式

懒汉模式--第一次获取对象时 再创建对象

#include <iostream>
#include <thread>
#include <vector>
#include <Windows.h>
#include <mutex>
using namespace std;

namespace Lazy_Man
{
    //懒汉模式--第一次获取对象时 再创建对象
    class Singleton
    {
    public:
        static Singleton* GetInstance()
        {
            //Sleep(1000);//增加没加锁时出现线程不安全的条件(2个以上线程同时过了判断条件)
            //如果有两个线程同时new 最后就不是只有一个对象了
            //加锁解决
            //_mtx.lock();
            if (_pinst == nullptr)//后一波线程来的时候就直接返回了
            {
                unique_lock<mutex> lock(_mtx);
                //出作用域后解锁 但后面还有代码不想保护 添加{}局部域 
                //加锁只用保护第一次 需要优化
                //只要_pinst已经指向了new出来的实例对象 就不需要加锁了
                //可以分析出现在的代码 存在优化空间
                //双检查即可
                if (_pinst == nullptr)
                {
                    _pinst = new Singleton;
                }
            }
            //_mtx.unlock();

            //第一次给一个值
            //后面每次获取的对象就都是一样的
            return _pinst;
        }

        static void DelInstance()
        {
            unique_lock<mutex> lock(_mtx);
            delete _pinst;
            _pinst = nullptr;
        }

    private:
        Singleton()
        {}

        Singleton(const Singleton& s) = delete;

        static Singleton* _pinst;//静态的 每次过来都会是第一次的对象
        static mutex _mtx;
    };

    //1.如果要手动释放单例对象 可以调用DelInstance
    //2.如果需要程序结束时 正常释放单例对象 可以加入下面的设计
    class GC
    {
    public:
        ~GC()
        {
            Singleton::DelInstance();
        }
    };

    static GC gc;
    //定义一个全局的静态对象
    //main函数结束后会调用析构函数

    Singleton* Singleton::_pinst = nullptr;
    mutex Singleton::_mtx;
}
int main()
{
    //Singleton s1;
    //Singleton s2;
    //------------
    //cout << Singleton::GetInstance() << endl;
    //cout << Singleton::GetInstance() << endl;
    //cout << Singleton::GetInstance() << endl;
    //---------------
    //拷贝构造出来的是新的对象 拷贝构造也要禁掉
    //Singleton copy(*Singleton::GetInstance());
    
    vector<std::thread> vthreads;
    int n = 10;
    for (int i = 0; i < n; ++i)
    {
        vthreads.push_back(std::thread([]()
        {
            //cout << std::this_thread::get_id() << ":";
            cout << Lazy_Man::Singleton::GetInstance() << endl;
        }));
    }

    for (auto& t : vthreads)
    {
        t.join();
    }
    
    //有线程安全的问题
    //加锁

    return 0;
}

 

3°饿汉模式

namespace Hungry_Man
{
    //饿汉模式 一开始(main函数之前)就创建对象
    class Singleton
    {
    public:
        static Singleton* GetInstance()
        {
            return &_inst;
        }

        Singleton(const Singleton&) = delete;
    private:
        Singleton()
        {}

        static Singleton _inst;
    };

    Singleton Singleton::_inst;
    //static对象是在main函数之前创建的 这回只有主线程 所以不存在线程安全问题

    int test()
    {
        //Singleton s1;
        //Singleton s2;
        //------------
        //cout << Singleton::GetInstance() << endl;
        //cout << Singleton::GetInstance() << endl;
        //cout << Singleton::GetInstance() << endl;
        //---------------
        //拷贝构造出来的是新的对象 拷贝构造也要禁掉
        //Singleton copy(*Singleton::GetInstance());

        vector<std::thread> vthreads;
        int n = 10;
        for (int i = 0; i < n; ++i)
        {
            vthreads.push_back(std::thread([]()
                {
                    //cout << std::this_thread::get_id() << ":";
                    cout << Singleton::GetInstance() << endl;
                }));
        }

        for (auto& t : vthreads)
        {
            t.join();
        }

        //有线程安全的问题
        //加锁

        return 0;
    }
}

int main()
{
    Hungry_Man::test();
    //一开始就创建好了 没有线程安全问题
    return 0;
}

4°总结

总结对比一下饿汉和懒汉的区别

  • 懒汉模式需要考虑线程安全和释放的问题 实现相对复杂 饿汉模式不存在以上问题 实现简单
  • 懒汉是一种懒加载模式 需要时再初始化对象 不会影响程序的启动 饿汉模式则相反 程序启动阶段就创建初始化实例对象 会导致程序启动慢 影响体验
  • 如果有多个单例类 假设有依赖关系(B依赖A) 要求A单例先创建初始化 B再启动 那么就不能用饿汉 因为无法保证初始化顺序 这个用懒汉我们就可以手动控制

总结一下:实际中懒汉模式还是更实用一些

2.类型转换

1°C/C++

int main()
{
    int i = 1;
    double d = 8.88;
    i = d;//类型转换 C语言支持相近类型的隐式类型转换(相近类型 也就是意义相似的类型)
    cout << i << endl;

    int* p = nullptr;
    p = (int*)i;//C语言支持相近类型的强制类型转换(不相近类型 也就是意义差别很大的类型)
    cout << p << endl;

    //C++ 兼容C语言留下来的隐式转换和显示转换 但是C++觉得C语言做得不规范 C++想兼容一下
    //引入了四种
    //static_cast,reinterpret_cast,const_cast,dynamic_cast
    
    d = static_cast<double>(i); //对应C语言隐式类型转换(相近类型)
    p = reinterpret_cast<int*>(i);//对应C语言大部分强制类型转换(不想近类型)
    
    //const int ci = 10;//加volatile可防止优化const 
    volatile const int ci = 10;
    //int* pi = (int*)&ci;//C语言
    int* pi = const_cast<int*>(&ci);//对应C语言强制类型转换中去掉const属性(不相近类型)
    *pi = 20;
    cout << *pi << endl;//20
    cout << ci << endl;//10 这里的打印10是因为ci存储的内存被改了 但是ci被放进了寄存器 这里去寄存器中取
    //还是19 本质是由于编译器对const对象存取优化机制导致
    //const变量先放进寄存器 内存当中改成了20 但取ci的时候是去寄存器里取得 所以还是10
    //想要禁止编译器做这个优化 每次都到内存中取值 就把volatile加上
    return 0;
}

static_cast、reinterpret_cast、const_cast、dynamic_cast

2°static_cast

static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用st

atic_cast,但它不能用于两个不相关的类型进行转换

对应隐式类型转换

3°reinterpret_cast

reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型

转换为另一种不同的类型

对应强制类型转换

4°const_cast

const_cast最常用的用途就是删除变量的const属性,方便赋值

去掉const属性

5°dynamic_cast

dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)

向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)

向下转型:父类对象指针/引用-子类指针/引用(用dynamic_cast转型是安全的)

注意:

  1. dynamic_cast只能用于含有虚函数的类
  2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
class A
{
public:
    virtual void f()
    {}

protected:
    int _a;
};

class B :public A
{
protected:
    int _b;
};

void f_cast(A* pa)
{
    //如果想区分pa是指向父类还是子类对象?
    //B* pb = (B*)pa;
    //强转可能出问题
    //如果pa指向的是父类对象  
    //只会多看4个字节
    //但看不到_b
    B* pb = dynamic_cast<B*>(pa);
    //如果pa指向子类对象 则转换成功
    //如果pa指向父类对象 则转换失败 返回nullptr
    if (pb != nullptr)
    {
        cout << "转换成功:pa指向子类对象" << endl;
        //pb是指向子类 pa也指向子类 是可以转换的
    }
    else
    {
        cout << "转换失败:pa指向父类对象" << endl;
    }
}


int main()
{
    A a;
    B b;

    //C++子类对象可以赋值给父类的对象 指针 引用
    //这个过程会发生切片 这个过程是语法天然支持的 这个叫向上转换 都可以成功
    //如果是父类的指针或者引用 传给子类的指针 这个过程叫向下转换 这个过程有可能能成功
    //要看具体情况
    //最后需要注意的是:dynamic_cast向下转换只能针对继承中的多态类型(父类必须包含虚函数)
     //dynamic_cast如何识别父类的指针是指向父类对象还是子类对象的呢?
    //dynamic_cast的原理:dynamic_cast通过去找虚表的上方存储的标识信息
    //来判断指向父类对象还是子类对象
    A* pa = &a;
    f_cast(pa);
    pa = &b;
    f_cast(pa);

    return 0;
}

 

6°explicit

explicit关键字阻止经过转换构造函数进行的隐式转换的发生

class A
{
public:
    explicit A(int a)
    {
        cout << "A(int a)" << endl;
    }

    explicit A(int a1, int a2)
    {
        cout << "A(int a1, int a2)" << endl;
    }

    A(const A & a)
    {
        cout << "A(const A& a)" << endl;
    }

private:
    int _a1;
    int _a2;
};

int main()
{
    A a1(1);
    //隐式转换-> A tmp(1); A a2(tmp);
    //再优化成直接构造
    //A a2 = 1;
    //多个参数也可以防止隐式类型转换
    //A a3 = { 1,2 };//C++11
}

【C++】22.单例模式+类型转换 完

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/543257.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

定义制造业操作(定义 MES/MOM 系统)

制造业操作包含众多工厂级活动&#xff0c;涉及设备&#xff08;定义、使用、时间表和维护&#xff09;、材料&#xff08;识别、属性、位置和状态&#xff09;、人员&#xff08;资格、可用性和时间表&#xff09;&#xff0c;以及这些资源与包含其信息碎片的众多系统之间的互…

BFT 最前线 | 腾讯发布Q1季报;超2000伙伴测试阿里云通义千问;黄仁勋:芯片是NVIDIA加速与AI计算的的“理想应用”

原创 | 文 BFT机器人 AI视界 TECHNOLOGY NEWS 01 阿里云&#xff1a;超2000伙伴测试通义千问 行业专属大模型将成趋势 2023阿里云常州峰会上&#xff0c;阿里云智能中国区总裁黄海清透露&#xff1a;目前通义千问大模型已有超2000个合作伙伴进行测试。未来大模型面向各行各…

微信视频怎么录屏?2个方法教您轻松录制!

案例&#xff1a;怎么录制微信视频通话&#xff1f; 【我在和家人或者朋友打微信视频电话时&#xff0c;总是会发生一些有趣的事情&#xff0c;我想把这些美好的画面通过录屏记录下来。但我不知道如何录制微信视频通话&#xff1f;有没有大佬支个招&#xff01;】 微信视频通…

macOS Big Sur 11.7.7 (20G1345) 正式版 ISO、PKG、DMG、IPSW 下载

本站下载的 macOS 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U 盘安装&#xff0c;或者在虚拟机中启动安装。另外也支持在 Windows 和 Linux 中创建可引导介质。 2023 年 5 月 18 日&#xff08;北京…

Boost开发指南-1.2progress_timer

progress_timer progress_timer也是一个计时器&#xff0c;它继承自timer&#xff0c;会在析构时自动输出时间&#xff0c;省去了timer手动调用elapsed()的工作&#xff0c;是一个用于自动计时相当方便的小工具。 progress_timer位于名字空间boost&#xff0c;为了使用progre…

浅谈PMO对组织战略的支持︱美团骑行事业部项目管理中心负责人边国华

美团骑行事业部项目管理中心负责人边国华先生受邀为由PMO评论主办的2023第十二届中国PMO大会演讲嘉宾&#xff0c;演讲议题&#xff1a;浅谈PMO对组织战略的支持。大会将于6月17-18日在北京举办&#xff0c;更多内容请浏览会议日程 议题内容简要&#xff1a; 战略是组织运行的…

Mysql【基础篇】—— mysql基础知识

Mysql【基础篇】—— mysql基础知识 ~&#x1f60e; 前言&#x1f64c;关系型数据库SQl 总结撒花&#x1f49e; &#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭&#xff1a;全神贯注的上吧&#xff01;&#xff01;&#xff01; &#x1f60a;作者简介…

CPU、内存使用率告警问题处理

可能原因 CPU 或内存使用率过高&#xff0c;容易引起服务响应速度变慢、服务器登录不上等问题。而引起 CPU 或内存使用率过高可能由硬件、系统进程、业务进程或者木马病毒等因素所致。 排查思路 定位消耗 CPU 或内存的具体进程。对 CPU 或内存占用率高的进程进行分析。 如果是…

Mac下webstorm安装运行ant desgin pro踩坑记录

最近在跟做鱼皮大佬的《用户中心》项目练手&#xff0c;由于是2022年的项目而且不是用Mac做的&#xff0c;遇到了很多问题&#xff0c;在此记录一下。 1.安装node.js版本过高&#xff0c;目前官网最新长期支持版本是18.16.0 LTS。视频中安装的是16.14.0 LTS&#xff0c;如果遇…

文档处理容易“翻车”,来看看CCIG上的大咖怎么说

一、前言 哪怕在互联网时代高速发展的今天&#xff0c;文档依然是人们在日常生活、工作中产生的信息的重要载体。 学生的作业、开具的发票、医生的医嘱、合同、简历、金融票据等都是通过文档来呈现的&#xff0c;它在我们的生活中随处可见。 现在我们为了更高效、安全的开展业务…

金铲铲之战8.5版本

金币篇 1、如果有强大的经济基础&#xff0c;那么这个游戏赢下来回变的超级简单&#xff0c;因此获取更多的金币是吃鸡最基础也是最关键的保障 说明&#xff1a;金币可以用来升级、刷新商店和购买人口 金币来源&#xff1a; 1、基础金币&#xff1a;根据野怪波次给予金币奖励…

Spread.NET 16.1.0 Winform Crack-new

添加对将表单控件添加到工作表的支持。 2023 年 5 月 18 日 - 16:24新版本 特征 窗体 支持表单控件- 添加了对将表单控件添加到工作表的支持。表单控件是可以插入到工作表中以处理数据并按指定处理数据的对象。可以轻松引用表单控件并与单元格数据进行交互。有 8 种不同类型的表…

第三十五章 多个组件状态数据共享

之前&#xff0c;我们的求和案例只是一个组件&#xff0c;属于是自己玩自己&#xff0c;接下来我们通过多个组件&#xff0c;通过redux实现它们之间的数据互通。 步骤1&#xff1a;更改项目目录结构 src |--containers | |--Count |--redux | |--actions | |--count.js | |-…

内网渗透(七十八)之域权限维持之ACL滥用(下)

ACL滥用(下) 7、GenericAll 权限 应用于组 再来看看GenericAll 权限 应用于组如何进行权限维持。 由于用户hack是普通的域用户,因此他没有往Domain Admins 组添加用户的权限,如图所示,以用户hack 身份往Domain Admins 组中添加用户,可以看到,添加用户失败。 现在我们…

目标检测,将voc格式转化为coco格式详细过程

在目标检测方法研究中&#xff0c;数据集的格式至关重要&#xff0c;为了减小模型的训练时长&#xff0c;需要现在小数据集进行模型研究&#xff0c;从而需要将VOC数据集转化为coco格式。下面一步一步来看&#xff1a; 1. 下载VOC数据集 Pascal VOC&#xff0c;即Pattern Ana…

windows2003系统SSL证书单站点部署https

本文将讲解&#xff0c;在windows 2003操作系统下&#xff0c;IIS 6 环境的服务器ssl证书安装教程。 安装前&#xff0c;请准备好SSL证书&#xff0c;没有的话&#xff0c;可以点这里申请SSL证书>>> 部署前请退出服务器内安装的杀毒软件&#xff08;360、金山、安全…

Linux 安装Qt6 教程及错误解决

在Linux环境&#xff0c;通常为Ubuntu&#xff0c;安装Qt开发环境&#xff0c;与Windows安装相比&#xff0c;还是稍显繁琐&#xff0c;需要多做几个步骤。 这里的Ubuntu版本采用的是ubuntu-22.04.2-desktop-amd64&#xff0c;所以&#xff0c;比旧版本会少很多坑&#xff0c;…

链表题目强化练

目录 前言 两数相加 删除链表的倒数第N个结点 环形链表 相交链表 合并 K 个升序链表 复制带随机指针的链表 前言 初学者在做链表的题目时有一个特点&#xff0c;就是每看一个链表的题都觉得很简单&#xff0c;但真正到了扣代码的时候不是这卡一块就是那卡一块。这是因为…

No module named PyQt5

背景&#xff1a;将Python文件转成APP&#xff0c;在编辑器中运行没有问题&#xff0c;一使用pyinstaller转成app就报错了 尝试了各种网上的解法都无效&#xff0c;最后是GPT4解决的 Q&#xff1a; 我遇到一个奇怪的问题&#xff0c;我在python文件中&#xff0c;引用了pyqt5…

Android Termux安装MySQL数据库 | 公网安全远程连接【cpolar内网穿透】

文章目录 前言1.安装MariaDB2.安装cpolar内网穿透工具3. 创建安全隧道映射mysql4. 公网远程连接5. 固定远程连接地址 前言 Android作为移动设备&#xff0c;尽管最初并非设计为服务器&#xff0c;但是随着技术的进步我们可以将Android配置为生产力工具&#xff0c;变成一个随身…