设计模式【cpp实现版本】
文章目录
- 设计模式
- 1.单例模式代码设计
- 1.饿汉式单例模式
- 2.懒汉式单例模式
- 2.简单工厂和工厂方法
- 1.简单工厂
- 2.工厂方法
- 3.抽象工厂模式
- 4.代理模式
- 5.装饰器模式
- 6.适配器模式
- 7.观察者模式
设计模式
1.单例模式代码设计
为什么需要单例模式,在我们的项目设计中,有时候有些类我们只需要一个就好了,就比如我们的日志类,数据库连接池类等等。在整个项目中使用同一个即可完成我们的需求功能。但这也导致可能会有多线程同时的去访问我们单例类,所以单例类的对象创建必须是线程安全的!
1.饿汉式单例模式
单例模式分为两种,一种是饿汉式,当程序还没有获取对象,实力对象就已经产生了,实力对象是一个静态对象,存放在程序的数据段。并且由于类中的静态对象在程序开始运行时就已经被创建,所以饿汉式单例模式一定是线程安全的!饿汉式单例模式实现如下:
class Singleton {
public:static Singleton* getInstance() {return &instance;}
private:static Singleton instance;Singleton() =default;Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;
};
Singleton Singleton::instance;
饿汉式单例模式几个需要注意的点:
- 构造函数私有化,默认实现即可。拷贝构造,赋值运算符重载 delete掉
- 提供一个类的静态成员方法,该方法是唯一能获取类的实例化对象的接口
- 类里面定义的静态成员对象,需要在类外进行初始化。
2.懒汉式单例模式
懒汉式单例模型的创建方式是,只有当程序用到时,才会去创建,这也是正常项目中经常会去使用的模式,毕竟对于任何一款产品,没有人希望在我玩之前需要初始化一大堆内容才可以使用。
懒汉式的单例模式,有两种实现方式,分为加锁版本和不加锁版本。我们先来看下加锁版本的实现:
mutex mtx;
class Singleton {
public:static Singleton* getInstance() {//锁+双重条件判断if (instance == nullptr) {lock_guard<mutex> guard(mtx);if (instance == nullptr) {instance = new Singleton();}}return instance;}
private://线程所共享的内容,别缓存static Singleton* volatile instance;Singleton() =default;Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;
};
Singleton* volatile Singleton::instance = nullptr;
这份代码有以下的几个实现上的关键点:
- 锁+双重if语句判断条件。因为
instance = new Singleton();
这行代码其实涉及三个过程,并非原子性操作。通过外层的i可以挡掉大部分的申请。- 开辟内存
- 构造对象
- 给instance赋值
- 线程所共享的内容,别缓存,加上
volatile
关键字确保安全。 - 类里面定义的静态成员变量,需要类外初始化。
懒汉式单例模式还有一种写法,编译器也是会确保线程安全的,并且不用用户加锁,编译器在编译时会堆这种写法进行自动的加锁操作。下面这段代码非常的优美,也是我写懒汉式单例模式最喜欢使用的方法。
class Singleton {
public:static Singleton* getInstance() {static Singleton instance;return &instance;}
private:Singleton() = default;Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;
};
- 函数里面的静态变量定义,只有第一次在运行到这里的时候才会初始化,保证了单例对象的唯一性。
- 静态对象的创建,在汇报指令上已经自动添加线程互斥指令了。可以通过Linux下,g++ -o run test.cpp -g,gdb run 验证
上面这段代码即不用定义静态成员变量,也不需要加锁,非常推荐使用!非常的优美!
下面是一段用于测试单例模式的代码,通过判断p1,p2,p3三个对象的地址是否相同,可以知道我们写的类是不是单例模型的!
Singleton* p1 = Singleton::getInstance();
Singleton* p2 = Singleton::getInstance();
Singleton* p3 = Singleton::getInstance();
cout << p1 <<endl<< p2 <<endl<< p3 << endl;
2.简单工厂和工厂方法
1.简单工厂
为什么需要工厂模式:主要是封装了对象的创建,不封装的结果就是用户自己调用new去获取想要的对象。下面我们定义两个产品,分布是车和灯,代码如下:
//产品系列1:车
class Car {
public:Car(string name) :_name(name) {}virtual void show() = 0;
protected:string _name;
};
class Bmw :public Car {
public:Bmw(string name) :Car(name) {}void show() {}cout << "a car of Bmw " <<_name<< endl;}
};
class Audi :public Car {
public:Audi(string name) :Car(name) {}void show() {cout << "a car of Audi "<<_name<< endl;}
};
如果我们不使用工厂,会怎么样呢?通过下面的代码我们会发现,不使用工厂,我们得知道车的型号才行,才能创建出一辆车,但是,我可能就只要一辆车而已并不在乎车的型号,但是我们创建对象却一定要给定型号,这就好比:一个士兵,他想要一把枪,但是现在这个士兵必须知道枪的型号才能拿到这个枪,这不是多此一举吗。简而言之,我只需要创建出来对象能用就行,而并不关心这个对象的创建细节。鉴于此,引入简单工厂。
Car* p1 = new Bmw("X1");
Car* p2 = new Audi("A6");
p1->show();
p2->show();
下面是一个简单工厂,这个简单工厂的作用是:封装对象的创建过程,用户去创建一个工厂,工厂中封装了各个对象的new过程,对外提供的是一个createCar接口和一个想要的车的类型的参数即可。
enum CarType {BMW,AUDI
};
class SimpleFactory {
public:Car* createCar(CarType ct) {switch (ct) {case BMW:return new Bmw("X1");case AUDI:return new Audi("A6");default:cerr << "传入工厂的参数不正确" << ct << endl;break;}return nullptr;}
};
现在我们再来看下简单工厂应该如何使用,当然,正常的业务场景中我们应该使用智能指针管理空间。
SimpleFactory* factory=new SimpleFactory();
Car* p1=factory->createCar(BMW);
Car* p2=factory->createCar(AUDI);
p1->show();
p2->show();
通过这个简单工厂,用户就可以不需要知道想要的车的型号是上面,怎么创建的,只需要向工厂提出申请即可。
但是简单工厂也有不好之处:一般来说:同一个工厂不可能创建多个不同牌子的车,而且当前这一个工厂需要做的事情也太多了吧,而且这个工厂设计不封闭!如果要增加一辆新车呢?要修改的地方可就太多了!正常来说一个工厂应该只负责一种产品的创建!所以这里我们需要引入工厂方法;
2.工厂方法
为了解决简单工厂不封闭的问题,工厂方法的设计如下,定义一个工厂基类,对于每一种车的类型,都定义一个工厂,这些工厂都继承与工厂基类。目前的为了能使用多态。
class Factory {
public:virtual Car* createCar(string name) = 0;
};
//宝马工厂
class BMWFactory :public Factory {
public:Car* createCar(string name="X6") {return new Bmw(name);}
};
//奥迪工厂
class AUDIFactory :public AbstractFactory {
public:Car* createCar(string name="A8") {return new Audi(name);}
};
那么工厂方法该怎么用呢?
Factory* bmwfactory=new BMWFactory();
Factory* audifactory=new AUDIFactory();Car* p1 = bmwfactory->createCar();
Car* p2 = audifactory->createCar();p1->show();
p2->show();
如果现在需要再增加一个奔驰工厂,我们并不需要去修改原有的工厂,即对已有的功能封闭!这里的这个工厂方法就是一种设计模式!
3.抽象工厂模式
工厂方法已经很nice了,但还是会有一些不足之处,比如:现在考虑产品,一类产品(有关联关系的系列产品),就比如华为耳机,手机,转接头,这种一个系列的产品应该属于一个工厂。我们现在继续增加一个产品,各厂商的车灯信息:
//系列产品2 灯
class Light {
public:virtual void show() = 0;
};
class BmwLight :public Light {
public:void show() { cout << "BMW Light!" << endl; }
};
cass AudiLigh :public Light {
public:void show() { cout << "Audi Light" << endl;
};
我们将工厂方法改成抽象工厂,即对有一组关联关系的产品簇提供产品对象的统一创建,我们将工厂方法中的Factory基类改名成AbstractFactory,然后对这个基类进行实现:
class AbstractFactory {
public:virtual Car* createCar(string name) = 0;virtual Light* createCarLight() = 0;//工厂方法,创建汽车关联的产品,车灯
};
那么现在的宝马工厂和奥迪工厂就变成了:
//宝马工厂
class BMWFactory :public AbstractFactory {
public:Car* createCar(string name) {return new Bmw(name);}Light* createCarLight() {return new BmwLight();}
};
//奥迪工厂
class AUDIFactory :public AbstractFactory {
public:Car* createCar(string name) {return new Audi(name);}Light* createCarLight() {return new AudiLigh();}
};
我们在使用时:
AbstractFactory* bmwfty=new BMWFactory();
AbstractFactory* audifty=new AUDIFactory();Car* p1=bmwfty->createCar();
Light* l1=bmwfty->createCarLight();p1->show();
l1->show();
但是抽象工厂也有缺点:假如现在宝马做了一个业务,其他公司没有,如果在基类中增加某个纯虚函数。其他所以的工厂都要去重写这个业务,不重写的话自己就成抽象类了,以后就无法再进行实例化对象!几种工厂模式的总结如下:
- 简单工厂:Simple Factory
- 把对象的创建封装在一个接口函数里面,通过传入不同的标识返回创建的对象。客户不用对自己负责new对象,不用了解对象创建的详细过程
- 缺点:提供创建对象实例的接口函数不闭合,不能对修改关闭
- 工厂方法:Factory Method
- Factory基类,提供一个纯虚函数(创建产品),定义派生类(具体产品的工厂)负责创建对应的产品,可以做到不同的产品,在不同的工厂里面创建,能够对现有工厂,以及产品的修改关闭。
- 缺点:很多产品是有关联关系的,是属于一个产品簇(例子中的车和车灯),不应该放在不同的工厂里面去创建这样一是不符合实际的产品对象创建逻辑,二是工厂类太多了不好维护
- 抽象工厂:
- 把有关联关系的属于一个管理簇的所有产品创建的接口函数,放在一个抽象工厂里面 AbstractFactory,派生类(具体产品的工厂)应该负责创建该产品簇里面所有的产品
- 几个工厂的产品需要保持一致,如果某个工厂有自研的,其他工厂没有的产品将无法处理!
4.代理模式
结构型模式,关心类和对象的组合,主要有代理模式,装饰器模式,适配器模式。代理Proxy模式,平时见的非常多的!通过代理类,来控制实际对象的访问权限!示意图如下:
比如现在有,客户,助理(proxy),老板(委托类),我们需要对访问老板的权限进行控制,不是任意的客户都可以访问老板!
现在我们举一个视频网站的例子,有三种角色,免费,vip电影,用券才能观看的电影。我们定义出这个网站类:
class VideoSite {
public:virtual void freeMovie() = 0;//免费virtual void vipMovie() = 0;//vip电影virtual void ticketMovie() = 0;//用券观看电影
};
假如我们实现了这三个功能,可以去服务器后台观看电影的接口,我们将这个类命名为FixBugVideoSite:
class FixBugVideoSite:public VideoSite {
public:virtual void freeMovie() {cout << "观看免费电影" << endl;}virtual void vipMovie() {cout << "观看vip电影" << endl;}virtual void ticketMovie() {cout << "观看ticket电影" << endl;}
};
那么现在我们就可以创建一系列的代理类了,用根据用户的级别,来控制用户的观影权限。
class FreeVideoSiteProxy :public VideoSite {
public:FreeVideoSiteProxy() { pVideo = new FixBugVideoSite(); }~FreeVideoSiteProxy() { delete pVideo; }virtual void freeMovie() {pVideo->freeMovie();}virtual void vipMovie() {cout << "需升级vip" << endl;}virtual void ticketMovie() {cout << "需升级ticket" << endl;}
private:VideoSite* pVideo;
};//同理
class VipVideoSiteProxy :public VideoSite {};
设计的思想是:组合,代理类通过一个抽象类的指针指向委托类【具有全部权限】的对象,从而进行权限的控制。
在具体的使用时,客户直接访问的是代理对象,所提供的有限的功能,使用方法如下:
void watchMovie(VideoSite* ptr){ptr->freeMovie();ptr->vipMovie();ptr->ticketMovie();
}//普通用户
VideoSite* p1=new FreeVideoSiteProxy();
watchMovie(p1);//vip用户
VideoSite* p2=new VipVideoSiteProxy();
watchMovie(p2);
总结:客户直接访问的是代理对象所提供的有限的功能。
5.装饰器模式
装饰器模式(结构模式):跟访问权限没关系,主要是增加现有类的功能(装饰),起到一个增强的功能。但是,增加现有类的功能,还有一个方法,就是新增加一个子类。为什么不通过增加子类的方法而是通过装饰器模式呢?请看下图的分析:
就拿我们之前举例子的汽车类来说,如果要给这三个品牌的汽车都增加定速巡航和自动刹车这两个功能,如果通过增加子类的方式,需要新添加6个子类,需要增加的类太多辣!为了增强现有类的功能,通过实现子类的方式重写接口,确实可以完成任务,功能扩展的,但代码中有太多的子类添加进来了!
所以我们可以通过装饰器模式,来对一个类实现更多的功能扩展。如下图所示,如果通过装饰器模式,只需要提供(1+2)个类即可,几个功能几个类。
其中:CarDecorator 持有一个需要被装饰的对象(Car* 指针),ConcreteDecorator01 实现定速巡航,ConcreteDecorator02 实现自动刹车。接下来我们通过代码进行演示:
//装饰器的基类(可省略,如果没有公共的方法)
class CarDecorator :public Car {
public:CarDecorator(Car* p) :pCar(p) { }
private:Car* pCar;
};//装饰器1 定速巡航的功能
class ConcreteDecorate01 :public Car {
public:ConcreteDecorate01(Car* p) :_pCar(p) {}void show() {_pCar->show();cout << ",定速巡航功能" ;}
private:Car* _pCar;
};//装饰器1 定速刹车的功能
class ConcreteDecorate02 :public Car {
public:ConcreteDecorate02(Car* p) :_pCar(p) { }void show() {_pCar->show();cout << ",定速刹车" ;}
private:Car* _pCar;
};
在使用的时候,使用方法如下,对于已有的类型,可以进行任意的装饰,任意的嵌套套娃。
Car* p1 = new ConcreteDecorate01(new Bmw());
//可进行任意的嵌套
p1 = new ConcreteDecorate02(p1);
Car* p2 = new ConcreteDecorate02(new Audi());p1->show();
p2->show();
6.适配器模式
适配器模式一般是为了兼容第三方库,从而要写很多适配器类,让不兼容的接口可以在一起工作。
在公司中为了兼容第三方库:有条件,重构代码。无条件,适配器模式,让不兼容的接口在一起工作。
在我们的生活中也会经常有类似的场景,比如我们的电脑和(TV)投影仪接口之间的转换。假如我们现在有一个只支持VGA接口的电脑和一个只支持VGA接口的投影仪:
//VGA接口类
class VGA{
public:virtual void play() = 0;
};//TV01表示支持VGA接口的投影仪
class TV01 :public VGA {
public:void play() {cout << "通过VGA接口连接投影仪,进行视频播放" << endl;}
};//实现一个电脑类(只支持VGA接口)
class Computer {
public: void playVideo(VGA* pVGA) {pVGA->play();}
};
由于电脑只支持VGA接口,所以该方法的参数也只能支持VGA接口的指针/引用,假如现在引进了一批新的投影仪,但是新的投影仪都是只支持HDMI接口,那该怎么办呢?
- 换一个支持HDMI接口的电脑,这个就叫代码重构。即在电脑类中实现一个
void playVideo(HMDI* pVGA)
方法。 - 买一个转换头,能把VGA信号转成HDMI信号,这个转换头就叫适配器类
我们先定义出新引进的HDMI接口的显示器TV02
class HDMI {
public:virtual void play() = 0;
};class TV02 :public HDMI {
public:void play() {cout << "通过HDMI接口连接投影仪,进行视频播放" << endl;}
};
由于电脑(VGA接口)和投影仪(HDMI接口)无法直接相连,所以需要添加适配器类,让VGA可以连接HDMI,我们定义出下面这个类:
class VGAToHMDIAdapter :public VGA {
public:VGAToHMDIAdapter(HDMI* p) :pHdmi(p) {}void play() {pHdmi->play();}
private:HDMI* pHdmi;
};
这个类里面的void play()
,该方法就是相当于转换头,做不同信号接口的转换的,有了这个转换头,我们该如何去使用呢?
Computer computer;computer.playVideo(new VGAToHMDIAdapter(new TV02()));
因为VGAToHMDIAdapter的构造函数中,需要传入一个HDMI类型的对象,但是这个类又是VGA的子类,所以可以被computer的void playVideo(VGA* pVGA)
方法所接收,发生多态,在这个playVideo里面,调用pVGA->play()
,实际上调用到的是适配器里面的play方法,这个play方法,去调用适配器里面传去HDMI对象的play,最后调到了TV02里面的paly方法!总结一句就是适配器模式,真的是充分的利用了多态的性质去解决问题,非常的巧妙!
7.观察者模式
观察者模式是一种行为型模式:主要关注的是对象之间的通信。观察者-监听者模式(发布-订阅模式)设计模式:主要关注的是对象的一对多的关系,也就是多个对象都依赖一个对象,当该对象的状态发生改变时,其他对象都能够接收到相应的通知。
一组数据(对象)=> 通过这一组数据 => 曲线图(对象1)/柱状图(对象2)/ 圆饼图(对象3)。当数据发生改变时,另外三个对象及时收到通知,做出相应的改变。我们不想去关注,我们只需要订阅后就做自己的事情。再举一个实际生活中的例子:当出版社出版报纸后,因为我们已经订阅过,报纸就会通知订阅了的对象。
在代码中,我们定义出Observer1 Observer2 Observer3三个观察者类,当Subject(主题) 主题有更改,应该及时通知相应的观察者,去处理相应的事件。代码如下:
// 观察者抽象类
class Observer {
public:virtual void handle(int msgid) = 0;
};
//第一个观察者实例,对1号和2号消息感兴趣
class Observer1 :public Observer {
public:void handle(int msgid) {switch (msgid) {case 1:cout << "Observer1 recv 1 msg!" << endl;break;case 2:cout << "Observer1 recv 2 msg!" << endl;break;default:cout << "Observer1 recv unknown msg!" << endl;break;}}
};
//第二个,对2号消息感兴趣
class Observer2 :public Observer {};
//第三个,对3号消息感兴趣
class Observer3 :public Observer {};
接下来我们创建主题Subject类:
class Subject {
public:void addObserver(Observer* obser, int msgid) {_subMap[msgid].push_back(obser);}void dispathch(int msg) {auto it = _subMap.find(msg);if (it != _subMap.end()) {for (auto pObser : it->second) {pObser->handle(msg);}}}
private:unordered_map<int, list<Observer*>> _subMap;
};
在主题类中,放着一个哈希表,key:消息id, value:观察者基类的指针。 通过addObserver函数接口给主题增加观察者对象,通过dispathch接口,主题检测发生改变,通知相应的观察者对象处理事件
在主函数中,我们应该这样使用,首先定义出观察者和监听者对象,并且通过观察着提供的addObserver方法,注册每个观察者所感兴趣的消息id。
Subject subject;
Observer* p1 = new Observer1();
Observer* p2 = new Observer2();
Observer* p3 = new Observer3();subject.addObserver(p1, 1);
subject.addObserver(p1, 2);
subject.addObserver(p2, 2);
subject.addObserver(p3, 3);
然后如果相应的id触发,主题类通过dispatch类通知到观察者类,通过观察者的指针调用到观察者相应的方法进行处理!
int msgid = 0;
while (true) {cout << "输入消息id:";cin >> msgid;subject.dispathch(msgid);
}
以上就是观察者模式的整体架构。
相关文章:
设计模式【cpp实现版本】
文章目录 设计模式1.单例模式代码设计1.饿汉式单例模式2.懒汉式单例模式 2.简单工厂和工厂方法1.简单工厂2.工厂方法 3.抽象工厂模式4.代理模式5.装饰器模式6.适配器模式7.观察者模式 设计模式 1.单例模式代码设计 为什么需要单例模式,在我们的项目设计中&…...
Python数据分析案例74——基于内容的深度学习推荐系统(电影推荐)
背景 之前都是标准的表格建模和时间序列的预测,现在做一点不一样的数据结构的模型方法。 推荐系统一直是想学想做的,以前读研时候想学没多少相关代码,现在AI资源多了,虽然上班没用到这方面的知识,但是还是想熟悉一下…...
C PRIMER PLUS——第8节:字符串和字符串函数
目录 1. 字符串的定义与表示 2. 获取字符串的两种方式 3.字符串数组 4. 字符串输入函数 4.1 gets()(不推荐使用,有缓冲区溢出风险) 4.2 fgets()(推荐使用) 4.3 scanf() 4.4 gets_s()(C11 标准&…...
Dia浏览器:AI驱动浏览网页,究竟怎么样?(含注册申请体验流程)
名人说:博观而约取,厚积而薄发。——苏轼《稼说送张琥》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、Dia浏览器简介1. 什么是Dia浏览器2. 开发背景与公司简介3. 与传统浏览器的区别 …...
milvus+flask山寨复刻《从零构建向量数据库》第7章
常规练手,图片搜索山寨版。拜读罗云大佬著作,结果只有操作层的东西可以上上手。 书中是自己写的向量数据库,这边直接用python拼个现成的milvus向量数据库。 1. 创建一个向量数据库以及对应的相应数据表: # Milvus Setup Argume…...
【大数据技术-HBase-关于Hmaster、RegionServer、Region等组件功能和读写流程总结】
Hmaster的作用 负责命名空间、表的创建和删除等一些DDL操作、region分配和负载均衡,并不参与数据读写,相比与其他大数据组件,如hdfs的namenode,在hbase中,Hmaster的作用是比较弱化的,即使挂掉,也暂时不影响现有表的读写。 RegionServer的作用 一个机器上一个regionse…...
用c语言实现——一个交互式的中序线索二叉树系统,支持用户动态构建、线索化、遍历和查询功能
知识补充:什么是中序线索化 中序遍历是什么 一、代码解释 1.结构体定义 Node 结构体: 成员说明: int data:存储节点的数据值。 struct Node* lchild:该节点的左孩子 struct Node* rchild:该节点的右孩子…...
Pale Moon:速度优化的Firefox定制浏览器
Pale Moon是一款基于Firefox浏览器的定制版浏览器,专为追求速度和性能的用户设计。它使用开放源代码创建,经过高度优化,适用于现代处理器,提供了更快的页面加载速度和更高效的脚本处理能力。Pale Moon不仅继承了Firefox的安全性和…...
广东省省考备考(第七天5.10)—言语:逻辑填空(每日一练)
错题 解析 第一空,搭配“各个环节”,根据“我国已经形成了相对完善的中药质量标准控制体系”可知,横线处应体现“包含”之意,C项“涵盖”指包括、覆盖,D项“囊括”指把全部包罗在内,均与“各个环节”搭配得…...
Gartner《Container发布与生命周期管理最佳实践》学习心得
近日,Gartner发布了《Best Practices for Container Release and Life Cycle Management》, 报告为技术专业人士提供了关于容器发布和生命周期管理的深入指导。这份报告强调了容器在现代应用开发和部署中的核心地位,并提供了一系列最佳实践&…...
内存、磁盘、CPU区别,Hadoop/Spark与哪个联系密切
1. 内存、磁盘、CPU的区别和作用 1.1 内存(Memory) 作用: 内存是计算机的短期存储器,用于存储正在运行的程序和数据。它的访问速度非常快,比磁盘快几个数量级。在分布式计算中,内存用于缓存中间结果、存储…...
SpringCloud之Eureka基础认识-服务注册中心
0、认识Eureka Eureka 是 Netflix 开源的服务发现组件,后来被集成到 Spring Cloud 生态中,成为 Spring Cloud Netflix 的核心模块之一。它主要用于解决分布式系统中服务注册与发现的问题。 Eureka Server 有必要的话,也可以做成集群…...
MySQL 中如何进行 SQL 调优?
在MySQL中进行SQL调优是一个系统性工程,需结合索引优化、查询改写、性能分析工具、数据库设计及硬件配置等多方面策略。以下是具体优化方法及案例说明: 一、索引优化:精准提速的关键 索引类型选择 普通索引:加速频繁查询的列&…...
Linux平台下SSH 协议克隆Github远程仓库并配置密钥
目录 注意:先提前配置好SSH密钥,然后再git clone 1. 检查现有 SSH 密钥 2. 生成新的 SSH 密钥 3. 将 SSH 密钥添加到 ssh-agent 4. 将公钥添加到 GitHub 5. 测试 SSH 连接 6. 配置 Git 使用 SSH 注意:先提前配置好SSH密钥,然…...
Android平台FFmpeg音视频开发深度指南
一、FFmpeg在Android开发中的核心价值 FFmpeg作为业界领先的多媒体处理框架,在Android音视频开发中扮演着至关重要的角色。它提供了: 跨平台支持:统一的API处理各种音视频格式完整功能链:从解码、编码到滤镜处理的全套解决方案灵…...
QSFP+、QSFP28、QSFP-DD接口分别实现40G、100G、200G/400G以太网接口
常用的光模块结构形式: 1)QSFP等效于4个SFP,支持410Gbit/s通道传输,可通过4个通道实现40Gbps传输速率。与SFP相比,QSFP光模块的传输速率可达SFP光模块的四倍,在部署40G网络时可直接使用QSFP光模块…...
MySQL事务和JDBC中的事务操作
一、什么是事务 事务是数据库操作的最小逻辑单元,具有"全有或全无"的特性。以银行转账为例: 典型场景: 从A账户扣除1000元 向B账户增加1000元 这两个操作必须作为一个整体执行,要么全部成功,要么全部失败…...
Linux系统下安装mongodb
1. 配置MongoDB的yum仓库 创建仓库文件 sudo vi /etc/yum.repos.d/mongodb-org.repo添加仓库配置 根据系统版本选择配置(以下示例为CentOS 7和CentOS 9的配置): CentOS 7(安装MongoDB 5.0/4.2等旧版本): In…...
JavaScript篇:async/await 错误处理指南:优雅捕获异常,告别失控的 Promise!
大家好,我是江城开朗的豌豆,一名拥有6年以上前端开发经验的工程师。我精通HTML、CSS、JavaScript等基础前端技术,并深入掌握Vue、React、Uniapp、Flutter等主流框架,能够高效解决各类前端开发问题。在我的技术栈中,除了…...
智能时代下,水利安全员证如何引领行业变革?
当 5G、AI、物联网等技术深度融入水利工程,传统安全管理模式正经历颠覆性变革。在这场智能化浪潮中,水利安全员证扮演着怎样的角色?又将如何重塑行业人才需求格局? 水利工程智能化转型对安全管理提出新挑战。无人机巡检、智能监测…...
使用FastAPI和React以及MongoDB构建全栈Web应用03 全栈开发快速入门
一、什么是全栈开发 A full-stack web application is a complete software application that encompasses both the frontend and backend components. It’s designed to interact with users through a web browser and perform actions that involve data processing and …...
NHANES稀有指标推荐:HALP score
文章题目:Associations of HALP score with serum prostate-specific antigen and mortality in middle-aged and elderly individuals without prostate cancer DOI:10.3389/fonc.2024.1419310 中文标题:HALP 评分与无前列腺癌的中老年人血清…...
软考错题集
一个有向图具有拓扑排序序列,则该图的邻接矩阵必定为()矩阵。 A.三角 B.一般 C.对称 D.稀疏矩阵的下三角或上三角部分包含非零元素,而其余部分为零。一般矩阵这个术语太过宽泛,不具体指向任何特定性 质的矩阵。对称矩阵…...
llama.cpp无法使用gpu的问题
使用cuda编译llama.cpp后,仍然无法使用gpu。 ./llama-server -m ../../../../../model/hf_models/qwen/qwen3-4b-q8_0.gguf -ngl 40 报错如下 ggml_cuda_init: failed to initialize CUDA: forward compatibility was attempted on non supported HW warning: n…...
[面试]SoC验证工程师面试常见问题(五)TLM通信篇
SoC验证工程师面试常见问题(五) 摘要:UVM (Universal Verification Methodology) 中的 TLM (Transaction Level Modeling) 通信是一种用于在验证组件之间传递事务(Transaction)的高层次抽象机制。它通过端口(Port)和导出(Export)实现组件间的解耦通信,避免了信…...
Spring循环依赖问题
个人理解,有问题欢迎指正。 Spring 生命周期中,首先使用构造方法对 bean 实例化,实例化完成之后才将不完全的 bean放入三级缓存中提前暴露出 bean,然后进行属性赋值,此时容易出现循环依赖问题。 由此可见,…...
AtCoder Beginner Contest 405(CD)
C - Sum of Product 翻译: 给你一个长为N的序列。 计算的值。 思路: 可使用前缀和快速得到区间和,在遍历 i 即可。(前缀和) 实现: #include<bits/stdc.h> using namespace std; using ll long lon…...
MindSpore框架学习项目-ResNet药物分类-模型优化
目录 5.模型优化 5.1模型优化 6.结语 参考内容: 昇思MindSpore | 全场景AI框架 | 昇思MindSpore社区官网 华为自研的国产AI框架,训推一体,支持动态图、静态图,全场景适用,有着不错的生态 本项目可以在华为云modelar…...
C. scanf 函数基础
scanf 函数 1. scanf 函数基础1.1 函数原型与头文件1.2 格式化输入的基本概念2.1 常见格式说明符整数格式说明符浮点数格式说明符字符和字符串格式说明符其他格式说明符2.2 格式说明符的高级用法宽度修饰符精度修饰符跳过输入字段宽度组合修饰符对齐修饰符实际应用示例3.2 精度…...
《C++探幽:模板从初阶到进阶》
文章目录 :red_circle:一、模板基础:开启泛型编程之门(一)泛型编程的必要性(二)函数模板1. 函数模板概念2. 函数模板定义格式3. 函数模板原理4. 函数模板实例化5. 模板参数匹配原则 (三)类模板1…...
画立方体软件开发笔记 js three 投影 参数建模 旋转相机 @tarikjabiri/dxf导出dxf
gitee: njsgcs/njsgcs_3d mainwindow.js:4 Uncaught SyntaxError: The requested module /3dviewport.js does not provide an export named default一定要default吗 2025-05-10 14-27-58 专门写了个代码画立方体 import{ scene,camera,renderer} from ./3dviewp…...
LVGL图像导入和解码
LVGL版本:8.1 概述 在LVGL中,可以导入多种不同类型的图像: 经转换器生成的C语言数组,适用于页面中不常改变的固定图像。存储系统中的外部图像,比较灵活,可以通过插卡或从网络中获取,但需要配置…...
Win10无法上网:Windows 无法访问指定设备、路径或文件。你可能没有适当的权限访问该项目找不到域 TEST 的域控制器DNS 解析存在问题
目录 一.先看问题 二.解决问题 三.补充备用 一.先看问题 Win08有网且已经加入域 Win10无网并且找不到域(说明:Win10我之前已经加入过域的,并且能够上网,但每次在宿舍和教室切换校园网,就会导致只有Win10无网&#…...
开疆智能Canopen转Profinet网关连接工博士GBS20机器人配置案例
本案例是介绍将支持canopen通信协议的机器人机器人接入到西门子Profinet网络中,由于两种协议不能直接通讯,故选择了canopen转Profinet网关进行通讯协议转换。 配置过程: 首先打开Profinet主站配置软件,新建项目并导入网关GSD文件…...
物业企业绩效考核制度与考核体系
物业企业绩效考核制度旨在通过建立科学、公正的绩效管理体系,提升员工的工作效率、激发团队的潜力,并通过对绩效结果的合理运用来推动公司可持续发展。该制度覆盖了公司全体员工,并明确规定了不同岗位、不同部门的考核内容、周期以及绩效考核的标准操作流程。通过月度、季度…...
expo多网络请求设定。
在使用 npx expo start 启动 Expo 开发服务器时,你可以通过设置网络模式来控制你的应用如何连接到开发服务器。Expo 提供了几种网络模式供你选择: LAN (Default): 这是默认模式。在这种模式下,你的应用会通过本地局域网 (LAN) 连接到你的开发…...
M0基础篇之ADC
本节课使用到的例程 一、例程基本配置的解释 在例程中我们只使用到了PA25这一个通道,因此我们使用的是Single这个模式,也就是我们在配置模式的时候使用的是单一转换。 进行多个通道的测量我们可以使用Sequence这个模式。 二、例程基本代码讲解 DL_ADC12_…...
Cadence 高速系统设计流程及工具使用三
5.8 约束规则的应用 5.8.1 层次化约束关系 在应用约束规则之前,我们首先要了解这些约束规则是如何作用在 Cadence 设计对象上的。Cadence 中对设计对象的划分和概念,如表 5-11 所示。 在 Cadence 系统中,把设计对象按层次进行了划分&#…...
gitkraken 使用教程
一、安装教程 安装6.5.3,之后是收费的,Windows版免安装 二、使用教程 0. 软件说明 gitkraken是一个git本地仓库管理软件,可以管理多个仓库,并且仓库可以属于多个网站多个账户。 1. 克隆仓库 选择要克隆到什么位置࿰…...
抖音视频上传功能测试全维度拆解——从基础功能到隐藏缺陷的深度挖掘
一、核心功能测试(Happy Path) 文件基础验证 支持格式:MP4/MOV/AVI等(含H.264/H.265编码组合验证) 分辨率兼容性:720p→8K的渐进式测试(重点验证竖屏9:16适配) 时长边界࿱…...
基于PE环境搭建及调试S32K312
0、简介 本文基于S32K312 介绍PE的使用流程,主要是记录开发流程: MCU:NXP S32k312-100pin 编辑器:S32 Design Studio for S32 Platform 3.5 仿真器:PE USB Multilink Universal REV-E PE和jlink不一样,…...
Autoware播放提示音
播放提示音 1、修改sound_player.yaml src/autoware/utilities/sound_player/scripts/sound_player.yaml start : ~/Autoware/install/sound_player/share/sound_player/start.wav stop : ~/Autoware/install/sound_player/share/sound_player/stop.wav red …...
学习黑客5 分钟深入浅出理解cron [特殊字符]
5 分钟深入浅出理解cron 🕒 大家好!今天我们将探索Linux系统中的cron——这个强大的定时任务调度工具,它允许用户自动执行周期性任务。在网络安全领域,尤其是在TryHackMe平台上的CTF挑战中,理解cron不仅是系统管理的基…...
Qt解决自定义窗口样式不生效问题
方法一: this->setAttribute(Qt::WA_StyledBackground, true); 方法二: 将类继承QWidget 改成继承 QFrame class MyWidget : public QFrame {} 方法三:重新实现QWidget的paintEvent函数时,使用QStylePainter绘制。 void p…...
redis未授权访问
redis是高速缓存型数据库,主要用户缓存一些频繁使用的数据来缓解数据库的访问压力。而redis未授权访问漏洞是因为redis数据库使用的过程中没有设定密码,任何人都可以直接连接数据库,这既是未授权访问,这是个通用漏洞,部…...
.Net HttpClient 使用准则
HttpClient 使用准则 System.Net.Http.HttpClient 类用于发送 HTTP 请求以及从 URI 所标识的资源接收 HTTP 响应。 HttpClient 实例是应用于该实例执行的所有请求的设置集合,每个实例使用自身的连接池,该池将其请求与其他请求隔离开来。 从 .NET Core …...
Eclipse 插件开发 6 右键菜单
Eclipse 插件开发 6 右键菜单 1 plugin.xml2 SampleHandler.java3 Activator.java 1 plugin.xml <?xml version"1.0" encoding"UTF-8"?> <?eclipse version"3.4"?> <plugin><!-- 定义命令 --><extension point&…...
MGP-STR:用于场景文本识别的多粒度预测
摘要 场景文本识别(Scene Text Recognition,简称STR)多年来一直是计算机视觉领域的研究热点。为了解决这一具有挑战性的问题,研究者们陆续提出了许多创新方法,近期将语言知识引入STR模型已成为一项重要趋势。在本研究…...
DAMA语境关系图汇总及考前须知
写在前面 1.考前须知 2.梳理彩色详细的语境关系图,方便理解与深化 1.考前须知 单选题10道题,每题1分,满分10分, 多选题15道题,每题2分,满分30分, 解答题6道,每题10分ÿ…...
Vue.js框架的优缺点
别再让才华被埋没,别再让github 项目蒙尘!github star 请点击 GitHub 在线专业服务直通车GitHub赋能精灵 - 艾米莉,立即加入这场席卷全球开发者的星光革命!若你有快速提升github Star github 加星数的需求,访问taimili…...