网站建设个一般需要花费多少钱,抖音商家页面模板,邯郸学校网站建设费用,建设一个网站可以做什么#x1f482; 个人主页:[pp不会算法v](https://blog.csdn.net/weixin_73548574?spm1011.2415.3001.5343) #x1f91f; 版权: 本文由【pp不会算法^v^】原创、在CSDN首发、需要转载请联系博主 #x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦… 个人主页:[pp不会算法v](https://blog.csdn.net/weixin_73548574?spm1011.2415.3001.5343) 版权: 本文由【pp不会算法^v^】原创、在CSDN首发、需要转载请联系博主 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦
c设计模式系列文章
什么是单例设计模式 顾名思义所谓单例设计模式就是在这个应用程序中某个类只有一个实例程序中的所有模块共用这个模块
举几个经常用到单例设计模式的应用场景1、网络编程的时候我们将socket,各种网络操作...封装成一个类那么这个类一般只有一个实例2、数据库封装类这个一般也是只有一个实例3、我上次使用javafx开发一个IM项目的时候对于好友列表这个gui控件自定义的也用到了单例设计模式以为整个应用程序就这一个好友列表 ..................剩下的大家可以根据自己的开发经验分享在评论区 什么是单例设计模式中的饿汉式和懒汉式 font colorred饿汉式/font程序一开始就创建这个实例 font colorred懒汉式/font第一次调用getInstance()的时候才创建这个实例
其实这个很好记饿汉他很饥饿可定很急着去吃东西所以才急着我还没有去主动获取他就在程序开始的时候创建实例了懒汉很懒所以她一点都不急不主动那么只有我们主动找他要的时候太才会创建实例 c中单例设计模式的实现
内部类析构 因为这个类只有一个实例那么我们就要防止在外部去创建这个类的实例对象就要私有化这个类的构造函数 又因为要保证这个实例不会被外部显示析构我们还要将析构函数私有化 Single.h:
class Single{
public:
static Single*getSingleInstance()
{
if(m_instanceNULL)
m_instancenew Single();
return m_instance;
}
private:
Single(){}
Single(const Single);
Single operator(const Single);
~Single(){}
static void deleteSingleInstance()
{
if(m_instance!NULL)
{
Single*instancem_instance;
m_instanceNULL;
delete instance;
}
}
class Deletor{
public:
Deletor()
{
//这里你可以创建这个类的实例也可以不创建创建那么就是饿汉式不创建就是懒汉式
}
~Deletor()
{
deleteSingleInstance();
}
}
static Single*m_instance;
static Deletor deletor;
}
Single.cpp:
Single* Single::m_instanceNULL;
Single::Deletor Single:: deletor; 这里的Deletor的静态对象deletor就解决了内存泄漏的问题这个内部类对象外部也访问不到而它又是一个静态的变量我们知道c应用程序在 结束之前会逆序析构静态变量而这个类的析构是public那么就能自动析构析构的时候就释放了单例的堆内存然后你将这个类的一些操作封装在public下那么这个单例就已经不需要你管了你要用的时候获取它的指针就行了 智能指针析构 上面这种使用内部类的方式看着有一丢丢麻烦而且不是很优雅 c给我们提供了一个很好用的工具智能指针
智能指针简介 更加具体的介绍请自行去查阅资料这里面大有文章 C 智能指针是一种用于管理动态分配的对象内存的工具可以帮助避免内存泄漏和悬空指针等问题。C11 引入了两种主要类型的智能指针std::shared_ptr 和 std::unique_ptr。 std::shared_ptr代表一个共享所有权的智能指针。多个 shared_ptr 对象可以同时拥有一个对象并在不再需要时自动释放对象内存。它使用引用计数的方式来追踪有多少个 shared_ptr 共享同一对象的所有权。当引用计数减为零时对象内存将被释放。 #include memory std::shared_ptrType sharedPtr std::make_sharedType(args); std::unique_ptr代表一个独占所有权的智能指针。只能有一个 unique_ptr 指向一个对象不能进行拷贝构造或赋值操作。当 unique_ptr 被销毁或重置时它所拥有的对象内存会被释放。 #include memory std::unique_ptrType uniquePtr std::make_uniqueType(args); 通过使用智能指针可以避免显式地调用 delete 或 delete[] 来释放动态分配的对象内存从而减少内存泄漏的风险。可以使用智能指针提供的成员函数和操作符来访问和操作所拥有的对象。另外还可以自定义删除器函数来指定动态释放对象内存的方式。 除了 shared_ptr 和 unique_ptrC 还提供了 std::weak_ptr、std::auto_ptrC11 之前的版本等其他类型的智能指针每种都有不同的所有权和语义特点适用于不同的场景。选择合适的智能指针类型可以提高代码的安全性和可维护性。 那么我们就用智能指针来修改上面使用内部类的方式实现的单例设计模式
Single.h:
#include memory//使用智能指针要包含的头文件
class Single{
public:
static Single*getSingleInstance()
{if (m_instance NULL)m_instance std::make_uniqueSingle();return m_instance.get();
}
private:
Single(){}
Single(const Single);
Single operator(const Single);
~Single(){}
static std::unique_ptrSingle m_instance;
}
Single.cpp:
std::unique_ptrSingle Single::m_instanceNULL; 单例模式的线程安全问题 首先我们要知道单例模式什么时候会出现线程安全问题其实就只有一个时刻就是创建实例的时候创建之后就不存在线程安全的问题
首先最简单的避免这个问题的方法就是饿汉式在只有主线程的时候就创建了实例也就不存在线程安全问题了
但是怎么解决懒汉式的线程安全问题呢
当实例未创建的时候多个线程同时去请求这个实例那么都通过了NULL的判断那么就会重复创建实例为了解决这个问题我罗列处一下几种处理方法
1、一次判断互斥锁
Single*getInstance()
{
//加锁
mutex.lock();
if(m_instanceNULL)
m_instancenew Single();
//解锁
mutex.unlock();
return m_instance;
}
2、双重检测锁模式DCLDouble-Checked Locking Pattern
Single* getInstance()
{
if(m_instanceNULL){mutex.lock();if(m_instanceNULL)m_instancenew Single();mutex.unlock();}
return m_instance;
}
为什么要检测两次呢相较于第一种方法这不是多次一举吗开始我看到也是这么认为的但是后来一想线程安全只会在创建实例的时候出现第一种方法就算创建之后多线程同时获取实例的时候也会产生竞争产生等待那么是不是除了创建实例的时候的等待是有意义的后面的等待都是多余的了纯纯是消耗性能了再看这个DCL机制当我们创建完成之后还会等待吗根本不会因为第一次检测就通过了直接返回实例的指针了不会进入锁的区域但是没创建的时候就会竞争等待这样就解决了第一种方式的弊端 3、原子操作
思考一下第二种方式还有没有潜在的问题存在呢
有的c中new一个对象的全过程 在 C 中new 一个对象的过程确实可以分为三个阶段分配内存空间、初始化成员变量和调用构造方法。 分配内存空间在使用 new 运算符时会首先从堆Heap中分配一块合适大小的内存空间用于存储对象的数据。 初始化成员变量在分配内存空间后编译器会根据对象的定义对对象的成员变量进行初始化。这包括调用各个成员变量的默认构造函数或者使用初始化列表进行初始化。 调用构造方法在成员变量初始化完成之后会调用对象的构造方法对对象进行进一步的初始化操作。构造方法是类的特殊成员函数用于完成对象的初始化工作。 在上述过程中当分配内存空间完成后对象的指针就不再为空了。具体来说当成功分配内存空间并返回指针时该指针指向的内存地址即为对象的有效地址可以开始对对象进行访问和操作。 那么问题就来了可能一个线程拿到了锁开始new出实例但是还只完成了第一步分配内存空间但是此时m_instance就已经不为NULL了那么别的线程如果此时获取实例那么第一次检测就通过了那么得到的实例就是没有完成初始化和构造的实例这就会造成严重的错误甚至导致程序崩溃
所以我们要保证要么就彻底创建实例要么就不创建这就涉及到原子操作了 v
atomicSingle* Single::m_instance;//记得在源文件初始化这里我就不写了getInstance() {if (m_instance NULL) { mutex.lock();if (pInstance nullptr) { pInstance new Widget(); }mutex.unlock();} return m_instance;
}
但是上面再判断的时候使用原子类型也会进行原子操作但是这是没必要的我们只需要在创建实例的时候进行原子操作就行了所以可以优化一下 Single* getInstance() {Single* p m_instance;if (p nullptr) { mutex.lock();if ((p pInstance) NULL) { m_instance p new Single(); }mutex.unlock();} return p;
}
参考了很多别人的文章如果有不对的还请指正如果后面有更好的方法再补充吧....