C++实现高频设计模式

news2025/2/26 23:26:55

image.png
面试能说出这几种常用的设计模式即可

1.策略模式

1.1 业务场景

大数据系统把文件推送过来,根据不同类型采取不同的解析方式。多数的小伙伴就会写出以下的代码:

if(type=="A"){
    //按照A格式解析
}else if(type=="B"){
    //按照B格式解析
}else{
    //按照默认格式解析
}

存在问题

  • 如果分支变多,这里的代码就会变得臃肿,难以维护,可读性低
  • 如果你需要接入一种新的解析类型,那只能在原有代码上修改

以上代码,违背了面向对象编程的开闭原则以及单一原则

  • 开闭原则(对于扩展是开放的,但是对于修改是封闭的):增加或者删除某个逻辑,都需要修改到原来代码
  • 单一原则(规定一个类应该只有一个发生变化的原因):修改任何类型的分支逻辑代码,都需要改动当前类的代码。

如果你的代码有多个if…else等条件分支,并且每个条件分支,可以封装起来替换的,我们就可以使用策略模式来优化。

1.2 策略模式定义

策略模式定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的的客户。
大白话:
假设你跟不同性格类型的小姐姐约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去逛街买买买最合适。当然,目的都是为了得到小姐姐的芳心,请看电影、吃小吃、逛街就是不同的策略。
策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。

1.3 策略模式使用

context,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用。
策略模式和工厂模式的不同之处:
工厂来生成算法对象,这没有错,但算法只是一种策略,最重要的是这些算法是随时间都可能互相替换的,这就是变化点,而封装变化就是面向对象的一个重要的思维方式,
在这里插入图片描述

1.4代码示例

#pragma once
#include <iostream>
using namespace std;
 
class Strategy{
 
public:
	virtual void algorithmInterface() = 0;
	virtual ~Strategy(){
		cout << "~Strategy() called!" << endl;
	}
 
};
 
class ConcreteStrategyA: public Strategy{
 
public:
	void algorithmInterface(){
 
		cout << "arithmetic A is called!" << endl;
	}
 
	~ConcreteStrategyA(){
 
		cout << "~ConcreteStrategyA() called!" << endl;
	}
};
 
class ConcreteStrategyB: public Strategy{
 
public:
	void algorithmInterface(){
 
		cout << "arithmetic A is called!" << endl;
	}
 
	~ConcreteStrategyB(){
	
		cout << "~ConcreteStrategyB() called!" << endl;
	}
};
 
class ConcreteStrategyC: public Strategy{
 
public:
	void algorithmInterface(){
 
		cout << "arithmetic C is called!" << endl;
	}
 
	~ConcreteStrategyC(){
 
		cout << "~ConcreteStrategyC() called!" << endl;
	}
};
 
class Context{
	
public:
	Context(Strategy *strategy){
		m_strategy = strategy;
	}
 
	~Context(){
		delete m_strategy;
	}
 
	void contextInterface(){
		m_strategy->algorithmInterface();
	}
 
 
private:
	Strategy *m_strategy;
};
#include "strategy.h" 
int main(){
 
	Context *contextA, *contextB, *contextC;
	//由于实例化不同的策略,所以最终在调用context->contextInterface()时,所获得的结果就不尽相同
	contextA = new Context(new ConcreteStrategyA);
	contextA->contextInterface();
	delete contextA;
 
	cout << endl << "----------**********----------" << endl;
	
	contextB = new Context(new ConcreteStrategyB);
	contextB->contextInterface();
	delete contextB;
 
	cout << endl << "----------**********----------" << endl;
 
	contextC = new Context(new ConcreteStrategyC);
	contextC->contextInterface();
	delete contextC;
 
	getchar();
	return 0;
}

2. 责任链模式

2.1 业务场景

请假是我们日常生活中经常遇到的事,一般请假按请的时间长短需要跟不同级别的管理者请,就是请假这个请求根据时间长短可由不同的处理者处理,非常适合责任链模式。

场景:不同的请求需要不同权限的对象来处理的情况。

2.2 模式结构

image.png

2.3 责任链模式定义

当你想要让一个以上的对象有机会能够处理某个请求的时候,就使用责任链模式
责任链模式为请求创建了一个接收者对象的链。执行链上有多个对象节点,每个对象节点都有机会(条件匹配)处理请求事务,如果某个对象节点处理完了,就可以根据实际业务需求传递给下一个节点继续处理或者返回处理完毕。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。
责任链模式实际上是一种处理请求的模式,它让多个处理器(对象节点)都有机会处理该请求,直到其中某个处理成功为止。责任链模式把多个处理器串成链,然后让请求在链上传递:
在这里插入图片描述

大白话:
假设你晚上去上选修课,坐到了最后一排。来到教室,发现前面坐了好几个漂亮的小姐姐,于是你找张纸条,写上:“你好, 可以做我的女朋友吗?如果不愿意请向前传”。纸条就一个接一个的传上去了,后来传到第一排的那个妹子手上。

2.4代码示例

#include <iostream>
using namespace std;

//抽象处理者
class Handler{
public:
    Handler() { m_pNextHandler = NULL; }
    virtual ~Handler() {}
    
    //设置下一个处理者
    void SetNextHandler(Handler *successor) { m_pNextHandler = successor; }
    
    //处理请求
    virtual void HandleRequest(int days) = 0;
protected:
    Handler *m_pNextHandler;  // 后继者
};


//具体处理者、主管
class Director :public Handler{
public:
    //处理请求
    virtual void HandleRequest(int days){
        if (days <= 1){
            cout << "我是主管,有权批准一天假,同意了!" << endl; 
        }
        else{
            m_pNextHandler->HandleRequest(days);
        }
    }
};

//具体处理者、经理
class Manager :public Handler{
public:
    //处理请求
    virtual void HandleRequest(int days){
        if (days <= 3){
            cout << "我是经理,有权批准三以下的假,同意了!" << endl;
        }
        else{
            m_pNextHandler->HandleRequest(days);
        }
    }
};

//具体处理者、老板
class Boss :public Handler{
public:
    //处理请求
    virtual void HandleRequest(int days){
        if (days <= 7){
            cout << "我是老板,最多让你请7天假,同意了!" << endl;
        }
        else{
            cout << "你请的假事假太长了,不同意!" << endl;
        }
    }
};


//场景
int main(){
    Handler *director = new Director;
    Handler *manager = new Manager;
    Handler *boss = new Boss;

    //设置责任链
    director->SetNextHandler(manager);
    manager->SetNextHandler(boss);


    director->HandleRequest(1);
    director->HandleRequest(2);
    director->HandleRequest(5);
    director->HandleRequest(8);
    return 0;
}

/*输出:
我是主管,有权批准一天假,同意了!
我是经理,有权批准三以下的假,同意了
我是老板,最多让你请7天假,同意了!
你请的假事假太长了,不同意!*/

2.5优缺点

优点

单一职责原则。 你可对发起操作和执行操作的类进行解耦。
开闭原则。 你可以在不更改现有代码的情况下在程序中新增处理者。

缺点

每个请求都从头到尾遍历。
如果建链不当,可能会造成循环调用,这将导致系统陷入死循环

3.模板方法模式

3.1 适合场景

假设有这么一个业务场景:内部系统不同商户,调用我们系统接口,去跟外部第三方系统交互(http方式)。走类似这么一个流程,如下:
image.png
一个请求都会经历这几个流程:

  • 查询商户信息
  • 对请求报文加签
  • 发送http请求出去
  • 对返回的报文验签

这里,有的商户可能是走代理出去的,有的是走直连。

3.2 定义

定义一个操作中的算法的骨架流程,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。它的核心思想就是:定义一个操作的一系列步骤,对于某些暂时确定不下来的步骤,就留给子类去实现,这样不同的子类就可以定义出不同的步骤。
image.png模板方法模式是通过把不变行为搬移到超类中,去除子类中的重复代码来提现他的优势。
模板方法模式提供了一个很好的代码复用。
当不变和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。通过模板方法模式把这些行为搬移到单一的地方,这样就帮助子类摆脱重复不变行为的纠缠。

3.3 代码示例

#ifndef HEAD_H
#define HEAD_H
 
class Fundamental{
public:
	virtual void primitiveOperation1();
	virtual void primitiveOperation2();
 
	void templateMethod();
	virtual ~Fundamental();
};
 
class ConcreteClassA: public Fundamental{
public:
	void primitiveOperation1();
	void primitiveOperation2();
 
	virtual ~ConcreteClassA();
};
 
class ConcreteClassB: public Fundamental{
public:
	void primitiveOperation1();
	void primitiveOperation2();
 
	virtual ~ConcreteClassB();
};
 
#endif	//HEAD_H
#include "Head.h"
#include <iostream>
#include <string>
using namespace std;
 
void Fundamental::primitiveOperation1(){
	cout << "Fundamental::primitiveOperation1()" << endl;
}
 
void Fundamental::primitiveOperation2(){
	cout << "Fundamental::primitiveOperation1()" << endl;
}
 
void Fundamental::templateMethod(){
	this->primitiveOperation1();
	this->primitiveOperation2();
}
 
Fundamental::~Fundamental(){
	cout << "Fundamental::~Fundamental()" << endl;
}
 
void ConcreteClassA::primitiveOperation1(){
	cout << "ConcreteClassA::primitiveOperation1()" << endl;
}
 
void ConcreteClassA::primitiveOperation2(){
	cout << "ConcreteClassA::primitiveOperation1()" << endl;
}
 
ConcreteClassA::~ConcreteClassA(){
	cout << "ConcreteClassA::~ConcreteClassA()" << endl;
}
 
void ConcreteClassB::primitiveOperation1(){
	cout << "ConcreteClassB::primitiveOperation1()" << endl;
}
 
void ConcreteClassB::primitiveOperation2(){
	cout << "ConcreteClassB::primitiveOperation2()" << endl;
}
 
ConcreteClassB::~ConcreteClassB(){
	cout << "ConcreteClassB::~ConcreteClassB()" << endl;
}
#include "Head.h"
#include <iostream>
#include <string>
using namespace std;
 
 
int main(){  
	Fundamental *fundamental = new ConcreteClassA;
 
	fundamental->templateMethod();
	delete fundamental;
 
	cout << "-------------- 华丽的分割线 --------------" << endl;
 
	Fundamental *concrete = new ConcreteClassB;
	concrete->templateMethod();
	delete concrete;
 
	getchar();
	return 0;
}

4. 观察者模式

4.1 适合场景

登陆注册应该是最常见的业务场景了。就拿注册来说事,我们经常会遇到类似的场景,就是用户注册成功后,我们给用户发一条消息,又或者发个邮件等等,因此经常有如下的代码:

void register(User user){
  insertRegisterUser(user);
  sendIMMessage();
  sendEmail();
}

这块代码会有什么问题呢?如果产品又加需求:现在注册成功的用户,再给用户发一条短信通知。于是你又得改register方法的代码了。。。这是不是违反了开闭原则啦。

void register(User user){
  insertRegisterUser(user);
  sendIMMessage();
  sendMobileMessage();
  sendEmail();
}

并且,如果调发短信的接口失败了,是不是又影响到用户注册了?!这时候,是不是得加个异步方法给通知消息才好。可以使用观察者模式优化。
其它场景:
1)服务器发布新版本,让客户端更新。
2)游戏群发邮件给补贴等。
3)聊天室系统。

4.2 观察者模式定义

观察者模式又叫做发布-订阅模式;
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动的更新自己。
观察者模式属于行为模式,一个对象(被观察者)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。它的主要成员就是观察者和被观察者

  • 被观察者(Observerable):目标对象,状态发生变化时,将通知所有的观察者。
  • 观察者(observer):接受被观察者的状态变化通知,执行预先定义的业务。

使用场景: 完成某件事情后,异步通知场景。如登陆成功
在这里插入图片描述

4.3代码示例

#ifndef HEAD_H
#define HEAD_H
 
#include <iostream>
#include <string>
#include <list>
using namespace std;
 
class Observer;
 
class Subject{
public:
	void attach(Observer *observer);
	void detach(Observer *observer);
	void notify();
	~Subject();
 
private:
	list<Observer*> observers;
};
 
class Observer{
public:
	virtual void update(){}
	virtual string getName(){ return ""; }
};
 
class ConcreteSubject : public Subject{
public:
	string getSubjectState();
	void setSubjectState(const string &str);
 
private:
	string subjectState;
};
 
class ConcreteObserver : public Observer{
public:
	ConcreteObserver(ConcreteSubject *subject, string name);
	void update();
	string getName();
 
private:
	string name;
	string observerState;
	ConcreteSubject *subject;
};
 
#endif //HEAD_H
#include "Head.h"
 
void Subject::attach(Observer *observer){
	observers.push_back(observer);
}
 
void Subject::detach(Observer *observer){
	observers.remove(observer);
}
 
void Subject::notify(){
	list<Observer*>::iterator it = observers.begin();
	while(it != observers.end()){
		(*it)->update();
		it++;
	}
}
 
Subject::~Subject(){
	cout << "开始析构了" << endl;
 
	list<Observer*>::iterator it = observers.begin();
	while(it != observers.end()){
 
		cout << "开始删除: " << (*it)->getName() << endl;
		delete *it;
		it++;
	}
	observers.clear();
}
 
std::string ConcreteSubject::getSubjectState(){
	return subjectState;
}
 
void ConcreteSubject::setSubjectState(const string &str){
	subjectState = str;
}
 
ConcreteObserver::ConcreteObserver(ConcreteSubject *subject, string name){
	this->subject = subject;
	this->name = name;
}
 
void ConcreteObserver::update(){
	observerState = subject->getSubjectState();
	cout << "发布者更新东西了 !!! 订阅者:" << name << "  状态:" << observerState << endl; 
}
 
std::string ConcreteObserver::getName(){
	return name;
}
#include "Head.h"
int main(int *argc, int *argv[]){
 
	ConcreteSubject *subject = new ConcreteSubject;
 
	subject->attach(new ConcreteObserver(subject, "第一个粉丝"));
	subject->attach(new ConcreteObserver(subject, "第二个粉丝"));
	subject->attach(new ConcreteObserver(subject, "第三个粉丝"));
 
	subject->setSubjectState("Hello");
	subject->notify();
 
	cout << "----------- 华 丽 的 分 割 线 -----------" << endl;
	subject->attach(new ConcreteObserver(subject, "王二麻子"));
	subject->setSubjectState("呵呵");
	subject->notify();
 
	cout << endl;
	delete subject;
 
	getchar();
	return 0;
}

5. 工厂模式

5.1 业务场景

工厂模式一般配合策略模式一起使用。用来去优化大量的if…else…或switch…case…条件语句。

在程序中,需要创建的对象很多,导致对象的new操作多且杂时,需要使用简单工厂模式;
由于对象的创建过程是我们不需要去关心的,而我们注重的是对象的实际操作,所以,我们需要分离对象的创建和操作两部分,如此,方便后期的程序扩展和维护。

5.2定义

简单工厂模式(Simple Factory Pattern)专门定义一个类来负责创建其他类的实例,被创建的实例通常具有共同的父类。
是一种实例化对象的方式,只要输入需要实例化对象的名字,就可以通过工厂对象的相应工厂函数来制造你需要的对象。

5.3优缺点

优点:

本着高内聚低耦合的原则,将系统的逻辑部分和功能分开。

缺点:

简单工厂模式会增加系统类的个数,在一定程度上增加了系统的复杂度和理解难度;
系统扩展难,一旦增加新产品,就需要修改工厂逻辑,不利于系统的扩展与维护;简单工厂模式中所有产品的创建都是由同一个工厂创建,工厂类职责较重,业务逻辑较为复杂,具体产品与工厂类之间耦合度高,严重影响了系统的灵活性和扩展性。

5.4 示例场景

某电视机厂为各个品牌代工生产电视机,可以使用简单工厂的模式来实现。

5.5示例代码

#include <iostream>
#include <vector>
using namespace std;
  
typedef enum ProductTypeTag{
  Hair,
  Hisense,
}PRODUCTTYPE;
  
//抽象产品类 TV(电视机类)
class TV{
public:
  virtual void Show() = 0;
  virtual ~TV(){};//声明析构函数为虚函数,防止内存泄漏
};
 //具体产品类 HairTV(海尔电视类)
class HairTV : public TV{
public:
  void Show(){
    cout<<"I'm HairTV "<<endl;
  }
};
 //具体产品类 HisenseTV(海信电视类)  
class  HisenseTV : public TV{
public:
  void Show(){
    cout<<"I'm HisenseTV"<<endl;
  }
};
  
// 工厂类 TVFactory(电视机工厂类)
class TVFactory{
public:
  TV* CreateTV(PRODUCTTYPE type){
    switch (type){
    case Hair:
      return new HairTV(); 
    case Hisense:
      return new HisenseTV();
    default:
      return NULL;
    }
  }
};
  
int main(int argc, char *argv[]){
    // 创建工厂类对象
    TVFactory* myTVFactory = new  TVFactory();
    TV* hairTV = myTVFactory->CreateTV(Hair);
    if (hairTV != NULL)
        hairTV->Show();
 
    TV* hisenseTV = myTVFactory->CreateTV(Hisense);
    if (hisenseTV != NULL)
        hisenseTV->Show();
 
    delete  myTVFactory;
    myTVFactory = NULL;
 
    delete hairTV;
    hairTV = NULL;
 
    delete hisenseTV;
    hisenseTV = NULL;  
  
    return 0;
}

6. 单例模式

6.1 业务场景

单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。I/O与数据库的连接,一般就用单例模式实现的。Windows里面的Task Manager(任务管理器)也是很典型的单例模式。

6.2定义

全局有且只有一个类的static实例,在程序任何地方都能够调用到。比如游戏客户端的本地Excel的加载,我们都会格式化成json。

一个好的单例应该具备下面4点

  • 1.全局只有一个实例:static 特性,同时禁止用户自己声明并定义实例(把构造函数设为 private)
  • 2.线程安全
  • 3.禁止赋值和拷贝
  • 4.用户通过接口获取实例:使用 static 类成员函数

6.3 单例模式的经典写法

6.2.1 汉模式

懒汉式(Lazy-Initialization)的方法是直到使用时才实例化对象,也就说直到调用Instance() 方法的时候才 new 一个单例的对象, 如果不被调用就不会占用内存。如果单线程没有问题,当多线程的时候就会出现不可靠的情况。

#include <iostream>
using namespace std;
 
/*
*	版本1 SingletonPattern_V1 存在以下两个问题
*	
*	1. 线程不安全, 非线程安全版本
*	2. 内存泄露
*/
class SingletonPattern_V1{
private:
	SingletonPattern_V1() {
		cout << "constructor called!" << endl;
	}
	SingletonPattern_V1(SingletonPattern_V1&) = delete;
	SingletonPattern_V1& operator=(const SingletonPattern_V1&) = delete;
	static SingletonPattern_V1* m_pInstance;
 
public:
	~SingletonPattern_V1() {
		cout << "destructor called!" << endl;
	}
	//在这里实例化
	static SingletonPattern_V1* Instance() {
		if (!m_pInstance) {
			m_pInstance = new SingletonPattern_V1();
		}
		return m_pInstance;
	}
	void use() const { cout << "in use" << endl; }
};
 
//在类外初始化静态变量
SingletonPattern_V1* SingletonPattern_V1::m_pInstance = nullptr;
 
//函数入口
int main(){
    //测试
	SingletonPattern_V1* p1 = SingletonPattern_V1::Instance();
	SingletonPattern_V1* p2 = SingletonPattern_V1::Instance();
 
	return 0;
}
存在的问题

获取了两次类的实例,构造函数被调用一次,表明只生成了唯一实例
1)线程安全的问题,当多线程获取单例时有可能引发竞态条件:第一个线程在if中判断 m_pInstance是空的,于是开始实例化单例;同时第2个线程也尝试获取单例,这个时候判断m_pInstance还是空的,于是也开始实例化单例;这样就会实例化出两个对象,这就是线程安全问题的由来;
解决办法:加锁
2)内存泄漏. 注意到类中只负责new出对象,却没有负责delete对象因此只有构造函数被调用,析构函数却没有被调用;因此会导致内存泄漏。
**解决办法1:**当然我们自己手动调用delete来进行释放是可以的,但是维护在何处释放又成了问题。
解决办法2: 使用共享指针;

** 线程安全、内存安全的懒汉式单例 **
#include <iostream>
using namespace std;
#include <memory> // C++11 shared_ptr头文件
#include <mutex>  // C++11 mutex头文件
/*
*	版本2 SingletonPattern_V2 解决了V1中的问题
*
*	1. 通过加锁让线程安全了
*	2. 通过智能指针(shareptr 基于引用计数)内存没有泄露了
*/
class SingletonPattern_V2 
{
public:
	~SingletonPattern_V2() {
		std::cout << "destructor called!" << std::endl;
	}
	SingletonPattern_V2(SingletonPattern_V2&) = delete;
	SingletonPattern_V2& operator=(const SingletonPattern_V2&) = delete;
 
	//在这里实例化
	static std::shared_ptr<SingletonPattern_V2> Instance() {
		//双重检查锁
		if (m_pInstance == nullptr) {
			std::lock_guard<std::mutex> lk(m_mutex);
			if (m_pInstance == nullptr) {
				m_pInstance = std::shared_ptr<SingletonPattern_V2>(new SingletonPattern_V2());
			}
		}
		return m_pInstance;
	}
 
private:
	SingletonPattern_V2() {
		std::cout << "constructor called!" << std::endl;
	}
	static std::shared_ptr<SingletonPattern_V2> m_pInstance;
	static std::mutex m_mutex;
};
 
//在类外初始化静态变量
std::shared_ptr<SingletonPattern_V2> SingletonPattern_V2::m_pInstance = nullptr;
std::mutex SingletonPattern_V2::m_mutex;
 
int main(){
	std::shared_ptr<SingletonPattern_V2> p1 = SingletonPattern_V2::Instance();
	std::shared_ptr<SingletonPattern_V2> p2 = SingletonPattern_V2::Instance();
	return 0;
}
优缺点
优点

1)基于 shared_ptr,内部实现的是基于引用计数的智能指针,每次实例被赋值或者拷贝,都会引用+1,在内部的析构中判断引用计数为0的时候会调用真正的delete。用了C++比较倡导的 RAII思想,用对象管理资源,当 shared_ptr 析构的时候,new 出来的对象也会被 delete掉。以此避免内存泄漏。
2)加了锁,使用互斥锁来达到线程安全。这里使用了两个 if判断语句的技术称为双重检测锁;好处是,只有判断指针为空的时候才加锁,避免每次调用 get_instance的方法都加锁,锁的开销毕竟还是有点大的。

缺点

使用智能指针会要求外部调用也得使用智能指针,就算用个typedef也是一长串代码不好维护且不美观。非必要不应该提出这种约束; 使用锁也有开销; 同时代码量也增多了,实际上设计最简单的才是最好的。

最推荐的懒汉式单例(magic static)——局部静态变量-记这个
#include <iostream>
using namespace std;
/*
*	版本3 SingletonPattern_V3 使用局部静态变量 解决了V2中使用智能指针和锁的问题
*
*	1. 代码简洁 无智能指针调用
*	2. 也没有双重检查锁定模式的风险
*/
class SingletonPattern_V3{
public:
	~SingletonPattern_V3() {
		std::cout << "destructor called!" << std::endl;
	}
	SingletonPattern_V3(const SingletonPattern_V3&) = delete;
	SingletonPattern_V3& operator=(const SingletonPattern_V3&) = delete;
	static SingletonPattern_V3& Instance() {
		static SingletonPattern_V3 m_pInstance;
		return m_pInstance;
 
	}
private:
	SingletonPattern_V3() {
		std::cout << "constructor called!" << std::endl;
	}
};
 
int main(){
	SingletonPattern_V3& instance_1 = SingletonPattern_V3::Instance();
	SingletonPattern_V3& instance_2 = SingletonPattern_V3::Instance();
       return 0;
}

这样保证了并发线程在获取静态局部变量的时候一定是初始化过的,所以具有线程安全性。
C++静态变量的生存期 是从声明到程序结束,这也是一种懒汉式。

最推荐的一种单例实现方式:

  1. 通过局部静态变量的特性保证了线程安全 (C++11, GCC > 4.3, VS2015支持该特性);
  2. 不需要使用共享指针,代码简洁;不需要使用互斥锁。
  3. 注意在使用的时候需要声明单例的引用 SingletonPattern_V3& 才能获取对象

6.2.2 饿汉模式

比较饥饿、比较勤奋,实例在初始化的时候就已经建好了,不管你后面有没有用到,都先新建好实例再说。这个就没有线程安全的问题,但是呢,浪费内存空间呀。

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

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

相关文章

2023最受推荐的五款项目管理工具

1、进度猫 进度猫是国内一款轻量级项目管理工具&#xff0c;适用于实时协作的团队。 以甘特图为向导&#xff0c;基于任务清单todolist&#xff0c;支持多用户协作&#xff1b; 甘特图显示具体任务清单、时间和任务的进度&#xff1b; 对未完成任务、已完成任务进行分类管…

AIGC|实践探索Langflow集成AzureOpenAI

目录 一、背景 二、AzureOpenAI介绍 三、langflow集成支持AzureOpenAI langflow集成AzureOpenAI联通改造流程&#xff1a; 四、效果演示 一、背景 langflow是一个LangChain UI&#xff0c;它提供了一种交互界面来使用LangChain&#xff0c;通过简单的拖拽即可搭建自己的实…

【文件读取/包含】任意文件读取漏洞 afr_1

1.1漏洞描述 漏洞名称任意文件读取漏洞 afr_1漏洞类型文件读取漏洞等级⭐漏洞环境docker攻击方式 1.2漏洞等级 高危 1.3影响版本 暂无 1.4漏洞复现 1.4.1.基础环境 靶场docker工具BurpSuite 1.4.2.靶场搭建 1.创建docker-compose.yml文件 version: 3.2 services: web: …

图片转excel表格怎么弄?有何密笈?

一般的软件要将图片转excel表格&#xff0c;都需要待识别的图片要有明显清晰的表格线&#xff0c;但金鸣识别则不需要这些条件的限制&#xff0c;即便是无表格线或缺少横线或竖线的图片&#xff0c;也能很好地识别成excel&#xff0c;另外&#xff0c;别的软件一般会限制文件大…

足跟痛筋膜炎最佳的治疗

足跟疼痛最常见的原因是跟腱炎。跟腱是从小腿肌肉到足跟骨的伸展部分。其他原因包括设备不当&#xff0c;走路加重脚跟的冲击力&#xff0c;鞋里面没有足弓的支撑&#xff0c;因为肥胖或怀孕脚弓变平&#xff0c;这可能会增加足跟疼痛的风险。除了跟腱炎外&#xff0c;足底筋膜…

python对数据的处理合集——字典、列表...

1.两个列表的数据对比 ①list2包含了list1,求出list2多余的值 #codingutf-8list1[1,3,5] list2[1,3,5,7,9,11] list[] for i in list2:if i not in list1:list.append(i) print(list)②求出两个列共同的值 ③两个列表合并 #第一种&#xff1a; list1 [1, 2, 3] list2 [4, …

ElasticSearch集群内存占用高?如何降低内存占用看这篇文章就够啦!(冻结索引)

ElasticSearch集群内存占用高&#xff1f;如果降低内存占用看这篇文章就够啦 一、冻结索引的介绍 经常搜索的索引被保留在内存中&#xff0c;因为重建索引和帮助高效搜索需要花费时间。另一方面&#xff0c;可能存在我们很少访问的索引。这些索引不需要占用内存&#xff0c;可…

MAC地址_MAC地址格式_以太网的MAC帧_基础知识

MAC地址 全世界的每块网卡在出厂前都有一个唯一的代码,称为介质访问控制(MAC)地址 一.网络适配器(网卡) 要将计算机连接到以太网&#xff0c;需要使用相应的网络适配器(Adapter)&#xff0c;网络适配器一般简称为“网卡”。在计算机内部&#xff0c;网卡与CPU之间的通信&…

聚观早报 |英伟达发布H200;夸克发布自研大模型

【聚观365】11月15日消息 英伟达发布H200 夸克发布自研大模型 iQOO 12系列开启销售 红魔9 Pro配置细节 禾赛科技第三季度营收4.5亿元 英伟达发布H200 全球市值最高的芯片制造商英伟达公司&#xff0c;正在升级其H100人工智能处理器&#xff0c;为这款产品增加更多功能&am…

天马行空的超级炫酷旋转图片-前端

一、实现代码&#xff08;html部分&#xff09; <!DOCTYPE html> <html> <head lang"en"><meta charset"UTF-8"><title>3D旋转</title><style type"text/css">*{padding: 0;margin: 0;}body,html{he…

笔记本电脑没有声音?几招恢复声音流畅!

笔记本电脑已经成为我们日常生活和工作的重要工具&#xff0c;而其中的声音是其功能之一。然而&#xff0c;有时您可能会遇到笔记本电脑没有声音的问题&#xff0c;这可能是由多种原因引起的。在本文中&#xff0c;我们将深入探讨笔记本电脑没有声音的常见原因&#xff0c;并提…

通过阿里云宕机这件事,来看国内程序员的畸形职场文化

1、阿里云宕机始末 阿里在变更这块有三板斧&#xff0c;可监控、可灰度&#xff0c;可回滚。另外他们内部非常喜欢用这类简短的语句传递意图。 听起来非常简单&#xff0c;就目前大多数互联网公司基础设施都会支持这3项&#xff0c;只是支持的程度不太一样&#xff0c;普通的监…

Freeswitch中CHANNEL_UNHOLD取回事件

1. CHANNEL_UNHOLD取回事件 2023-11-15T09:18:45.3740800 INFO c.e.c.v.s.c.i.FsServerEventHandler - eventReceived:CHANNEL_UNHOLD 2023-11-15T09:18:45.3740800 INFO c.e.c.v.s.c.i.FsServerEventHandler - EventBody********:{variable_effective_caller_id_number1000,…

【博客系统】 一

该博客系统基于servlet和mysql数据库 , 并且通过xshell终端工具部署至云服务器. 实现的功能包括: 1.博客列表页 2.博客详情页 3.登陆页面 4.强制登陆检查 5.获取用户信息 6.退出登陆 7.发布博客 一.系统展示 登陆页面 博客列表页 博客详情页 博客编辑页 下面就开始编写代码了.…

【Proteus仿真】【STM32单片机】锂电池管理系统

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真STM32单片机控制器&#xff0c;使用LCD1602显示模块、DS18B20温度传感器、PCF8691 ADC模块、按键、LED蜂鸣器模块等。 主要功能&#xff1a; 系统运行后&#xff0c;LCD1602显示温…

Java方法中不使用的对象应该手动赋值为NULL吗?

在java方法中&#xff0c;不使用的对象是否应该手动赋值为null&#xff1f;我们先来通过一个示例看一下。 垃圾回收示例一 public class GuoGuoTest {public static void main(String[] args) {byte[] placeholder new byte[64 * 1024 * 1024];System.gc();} } 上面代码向内…

第五章 路由技术及应用

目录 5.1 直连路由概述 5.1.1 直连路由工作原理 5.1.2 直连路由配置 5.2 直连路由仿真 5.3 静态路由技术 5.3.1 静态路由定义 5.3.2 静态路由工作原理 5.3.3 静态路由配置 5.3.4 默认路由 (1) 默认路由概述 (2) 配置默认路由 (3) 默认路由应用场合&#xff1a;上网…

QGIS之二十四安装插件

1、从菜单栏中找到插件 2、搜索插件 从搜索框中搜索插件&#xff0c;如“cesium" 3、安装插件 4、查看插件 安装好的插件从这边可以看到&#xff0c;当然&#xff0c;其它插件可能在其它位置 5、已安装插件 可以查看已安装的插件

050-第三代软件开发-软件部署脚本(二)

第三代软件开发-软件部署脚本(二) 文章目录 第三代软件开发-软件部署脚本(二)项目介绍软件部署脚本(二) 关键字&#xff1a; Qt、 Qml、 bash、 shell、 脚本 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#xff08;Qt Meta-Object Languag…

界面组件DevExpress Reporting v23.1亮点 - 全新升级报表查看器

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表 界面组件DevExpress Reporting v23.1已经发布一段…