观察者模式、订阅者发布者模式、vtk中的观察者模式

文章目录

  • 什么是观察者模式
  • vtk是如何实现的观察者模式.AddObserver
  • 什么时候使用观察者模式?什么使用订阅发布者模式?
  • 观察者模式的实现
  • 订阅发布者的实现
  • 总结
  • 知识补充:

什么是观察者模式

用于在对象之间建立一对多的依赖关系,当一个对象的状态发生变化时,其所依赖的所有对象都会收到通知并自动更新。

vtk是如何实现的观察者模式.AddObserver

在VTK(Visualization Toolkit)中,观察者模式通过AddObserver方法来实现。AddObserver方法允许一个对象(观察者)注册自己以便接收另一个对象(被观察者)的通知。被观察者对象在发生特定事件或状态变化时会调用已注册的观察者的相应方法,以便进行相应的处理或更新。

#include <vtkSmartPointer.h>
#include <vtkObject.h>
#include <vtkCommand.h>
#include <iostream>// 自定义观察者类
class CustomObserver : public vtkCommand
{
public:static CustomObserver* New(){return new CustomObserver;}virtual void Execute(vtkObject* caller, unsigned long eventId, void* callData){if (caller){std::cout << "Received event: " << caller->GetClassName() << ", Event ID: " << eventId << std::endl;}}
};int main()
{// 创建一个被观察者对象vtkSmartPointer<vtkObject> observedObject = vtkSmartPointer<vtkObject>::New();// 创建一个观察者对象vtkSmartPointer<CustomObserver> observer = vtkSmartPointer<CustomObserver>::New();// 注册观察者对象到被观察者对象observedObject->AddObserver(vtkCommand::AnyEvent, observer);// 触发事件,通知观察者对象observedObject->InvokeEvent(vtkCommand::ModifiedEvent);return 0;
}
/*
我们首先创建了一个被观察者对象observedObject和一个自定义的观察者类CustomObserver。然后,使用AddObserver方法将观察者对象observer注册到被观察者对象上,通过指定事件ID(vtkCommand::AnyEvent表示任何事件)和观察者对象来建立它们之间的关联。最后,我们调用InvokeEvent方法来触发事件并通知观察者对象。当事件被触发时,执行观察者类中的Execute方法,打印出相关信息。
*/

什么时候使用观察者模式?什么使用订阅发布者模式?

  • 使用观察者模式的情况包括:

1.当一个对象的改变需要同时影响其他多个对象,并且不希望显式地指定这些对象时,可以使用观察者模式。

2.当一个对象的改变需要通知其他对象,但是不需要知道具体的接收者时,可以使用观察者模式。

3.当一个对象需要在改变时通知一组对象而无需知道具体对象时,可以使用观察者模式。

订阅发布者模式定义了一种一对多的依赖关系,允许多个订阅者(观察者)订阅一个发布者(被观察者),并在发布者状态改变时接收通知。与观察者模式不同的是,发布者和订阅者之间没有直接的依赖关系,它们通过消息队列、事件中心或者代理来进行通信。

  • 使用订阅发布者模式的情况包括:

当存在多个发布者和订阅者之间的松散关系时,可以使用订阅发布者模式。

当发布者和订阅者之间需要解耦,以便灵活地添加或移除发布者和订阅者时,可以使用订阅发布者模式。

当需要支持多对多的关系,即一个发布者可以有多个订阅者,一个订阅者也可以订阅多个发布者时,可以使用订阅发布者模式。

观察者模式的实现

// 观察者模式
/*** Observer Design Pattern** Intent: Lets you define a subscription mechanism to notify multiple objects* about any events that happen to the object they're observing.** Note that there's a lot of different terms with similar meaning associated* with this pattern. Just remember that the Subject is also called the* Publisher and the Observer is often called the Subscriber and vice versa.* Also the verbs "observe", "listen" or "track" usually mean the same thing.*/#include <iostream>
#include <list>
#include <string>
// 抽象观察者
/*
定义了析构函数,和update函数接口
*/
class IObserver
{
public:virtual ~IObserver(){};virtual void Update(const std::string &message_from_subject) = 0;
};
// 抽象被观察者
/*
定义了析构函数,和attach,detach,notify三个函数接口*/
class ISubject
{
public:virtual ~ISubject(){};virtual void Attach(IObserver *observer) = 0;virtual void Detach(IObserver *observer) = 0;virtual void Notify() = 0;
};// 具体被观察者
/*建议先看main函数,然后再看观察者类,然后再看被观察者类,
不然的话,你就不会知道:为什么被观察者类被称为:"tfboys类"或者是"时代少年团类"或者是"蔡徐坤类"
*/
// 现在咱们来看一下:一个完整的"tfboys"类是如何分装的?
class Subject : public ISubject
{
public:// 重写析构函数virtual ~Subject(){std::cout << "Goodbye, I was the Subject.\n"; // 再见了,我的粉丝们,tfboys于2017年9月正式宣布解散}/*** The subscription management methods.*/void Attach(IObserver *observer) override{list_observer_.push_back(observer); // tfboys将粉丝收集到他的个人列表中,也就是说tfboys会记每一位ta的粉丝!}void Detach(IObserver *observer) override{list_observer_.remove(observer); // 某个粉丝不再对ta进行关注,tfboys就将人家移除个人列表,这个私有列表是用list实现的,是一个双向链表,可以高效的添加与删除每位粉丝,但是查找的复制度是O(N)}void Notify() override // 看到下面的遍历,大家应该猜到了,这是tfboys在举办演唱会之前,对每一位粉丝进行通知,当然这函数不是单独调用的,因为这个函数还有一个message_变量{std::list<IObserver *>::iterator iterator = list_observer_.begin();HowManyObserver();while (iterator != list_observer_.end()){(*iterator)->Update(message_);++iterator;}}// 没错,这个是通知每一位粉丝的真正业务函数void CreateMessage(std::string message = "Empty"){this->message_ = message; // 先把演唱会的相关消息进行编写保存Notify();                 // 然后将消息散发给粉丝}// 这是一个debug的函数,用来输出tfboys的私人列表中,到底有几百万粉丝?void HowManyObserver(){std::cout << "There are " << list_observer_.size() << " observers in the list.\n";}/*** Usually, the subscription logic is only a fraction of what a Subject can* really do. Subjects commonly hold some important business logic, that* triggers a notification method whenever something important is about to* happen (or after it).*/// 这个函数其实和CreateMessage一样,只不过这个内置了信息// 不太清楚这个干嘛用的,我可以理解为每次都要发送一些固定的消息,然后我们懒得用CreateMessage编辑了void SomeBusinessLogic(){this->message_ = "Tomorrow will be a holiday!!!";//比如:重要通知:明天放假!!![doge]Notify();std::cout << "I'm about to do some thing important\n";}private:std::list<IObserver *> list_observer_;std::string message_;
};// 具体观察者
class Observer : public IObserver
{
public:// 重写了自己的构造函数,他是用一个被观察者类来初始化,这样一来,被观察者有任何消息,观察者都会收到消息// 就好比很多人都热爱追星: tfboys就是被观察者,他的粉丝就是观察者,所以理论上来说,被观察者干的活应该会更多一点//(所以,为了好记:观察者你可以看成"粉丝类",被观察者你可以看成"tfboys类")// 所以说,下面咱们来看一下,粉丝类是如何封装的Observer(Subject &subject) : subject_(subject) // 这里,我们的粉丝们比较专一,只能选择"时代少年团","tfboys","蔡徐坤"等等,其中的一位明星作为自己的偶像(被观察者){this->subject_.Attach(this);                                                     // tfboys的微博收到了订阅请求,恭喜tfboys收获一枚新粉丝!std::cout << "Hi, I'm the Observer \"" << ++Observer::static_number_ << "\".\n"; // 蔡徐坤,你好,我是你的一个新粉丝this->number_ = Observer::static_number_;                                        // 这是记录tfsboys粉丝的总数量,每位粉丝都可以看到                                        //}virtual ~Observer(){std::cout << "Goodbye, I was the Observer \"" << this->number_ << "\".\n"; // 粉丝被析构了}void Update(const std::string &message_from_subject) override // 这个函数是有tfboys调用的,他要去哪里开演唱会?{message_from_subject_ = message_from_subject; // 将信息保存到粉丝的手机里PrintInfo();                                  // 通知粉丝,tfboys要去哪里开演唱会了}void RemoveMeFromTheList(){subject_.Detach(this);std::cout << "Observer \"" << number_ << "\" removed from the list.\n"; // gege太令人失望了,一位粉丝默默地离开了ta的应援团}void PrintInfo() // 打印收到了什么新消息{std::cout << "Observer \"" << this->number_ << "\": a new message is available --> " << this->message_from_subject_ << "\n";}private:std::string message_from_subject_;Subject &subject_;static int static_number_;int number_;
};int Observer::static_number_ = 0;void ClientCode()
{// 一个被观察者 subject有"主体"的意思,可以理解为"被观察的主体"Subject *subject = new Subject;// 多个观察者Observer *observer1 = new Observer(*subject); // Hi, I'm the Observer "1".Observer *observer2 = new Observer(*subject); // Hi, I'm the Observer "2".Observer *observer3 = new Observer(*subject); // Hi, I'm the Observer "3".Observer *observer4;Observer *observer5;// subject->SomeBusinessLogic();subject->CreateMessage("Hello World! :D"); // 此时observer1,observer2,observer3都收到了被观察者发送的信息observer3->RemoveMeFromTheList();          // observer3不再观察subject->CreateMessage("The weather is hot today! :p"); // 此时只有observe1,observer2收到了消息observer4 = new Observer(*subject);                     // observer4加入了观察者observer2->RemoveMeFromTheList();   // observer2不再观察observer5 = new Observer(*subject); // observer5加入观察subject->CreateMessage("My new car is great! ;)"); // 此时只有observe1,observer2,observer5收到了消息observer5->RemoveMeFromTheList();                  // observer5不再观察observer4->RemoveMeFromTheList(); // observer4不再观察observer1->RemoveMeFromTheList(); // observer1不再观察delete observer5;delete observer4;delete observer3;delete observer2;delete observer1;delete subject;
}int main()
{ClientCode();return 0;
}
/*
Hi, I'm the Observer "1".
Hi, I'm the Observer "2".
Hi, I'm the Observer "3".
There are 3 observers in the list.
Observer "1": a new message is available --> Hello World! :D
Observer "2": a new message is available --> Hello World! :D
Observer "3": a new message is available --> Hello World! :D
Observer "3" removed from the list.
There are 2 observers in the list.
Observer "1": a new message is available --> The weather is hot today! :p
Observer "2": a new message is available --> The weather is hot today! :p
Hi, I'm the Observer "4".
Observer "2" removed from the list.
Hi, I'm the Observer "5".
There are 3 observers in the list.
Observer "1": a new message is available --> My new car is great! ;)
Observer "4": a new message is available --> My new car is great! ;)
Observer "5": a new message is available --> My new car is great! ;)
Observer "5" removed from the list.
Observer "4" removed from the list.
Observer "1" removed from the list.
Goodbye, I was the Observer "5".
Goodbye, I was the Observer "4".
Goodbye, I was the Observer "3".
Goodbye, I was the Observer "2".
Goodbye, I was the Observer "1".
Goodbye, I was the Subject.
*/

订阅发布者的实现

以下代码并没有用到异步,当然,异步才是正宗的订阅者发布者模式,我暂时是想不到如何改为异步通知,不过,下面的代码也暂时够我们理解 : 在订阅者发布者模式里面的中介是干什么的了

// 订阅者发布者模式
/** 有了上观察者模式的基础,我们先可以尝试编写一下他的升级版--订阅者发布者模式* 在这个模式中,随着tyboys名声大噪,"tfboys",再不需要自己主动通知粉丝了,* 他有了自己的助理,所有大小事宜都交给自己的助理去做*/
#include <iostream>
#include <vector>// 提前声明助理类,不然tfboys不认(其实就是cpp语法,声明前后顺序的问题)
/** 当我们提前声明一个类时,在这个类被完全定义之前,只可以定义该类的变量,但是不能使用该类的方法。*/
class Mediator;// 抽象观察者类
// 抽象粉丝类
class Observer
{
protected:Mediator *_mediator; // 中介者指针public:explicit Observer(Mediator *mediator) : _mediator(mediator){}virtual ~Observer() {}virtual void update() = 0;
};// 具体观察者类
/**具体粉丝类*/
class ConcreteObserver : public Observer
{
public:// 调用基类Observer的构造函数来初始化基类中的mediator_成员变量explicit ConcreteObserver(Mediator *mediator);~ConcreteObserver();void update() override;private:static int static_number_;int number_;
};
int ConcreteObserver::static_number_ = 0;// 抽象发布者类
// 抽象tfboys类
class Publisher
{
protected:Mediator *mediator; // 中介者指针public:explicit Publisher(Mediator *mediator) : mediator(mediator){std::cout << "Hi! I'm a Publisher" << std::endl;}virtual ~Publisher(){std::cout << "Goodbye! I'm a Publisher" << std::endl;}void publish();void setMediator(Mediator *mediator){this->mediator = mediator;}
};// 中介者类
class Mediator
{
private:// 放粉丝的std::vector<Observer *> observers; // 存储观察者指针public:Mediator(){std::cout << "Hi! I'm a Mediator" << std::endl; // 中介的诞生};~Mediator(){std::cout << "Goodbye! I'm a Mediator" << std::endl; // 中介的消亡};// 加粉丝void attach(Observer *observer){observers.push_back(observer);}// 清空某个粉丝void detach(Observer *observer){for (auto it = observers.begin(); it != observers.end(); ++it){if (*it == observer){observers.erase(it);break;}}}// 通知所有粉丝void notify(Publisher *publisher){for (auto observer : observers){observer->update();}}
};/********下面是对两个函数的定义*************/
void ConcreteObserver::update()
{std::cout << "Received an update from the publisher!" << std::endl;
}void Publisher::publish()
{std::cout << "Publishing an update..." << std::endl;mediator->notify(this);
}// 先声明后实现,有效的解决了循环依赖的问题!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ConcreteObserver::ConcreteObserver(Mediator *mediator) : Observer(mediator)
{this->_mediator->attach(this);std::cout << "Hi! I'm a ConcreteObserver" << ++ConcreteObserver::static_number_ << std::endl;this->number_ = ConcreteObserver::static_number_;
}
ConcreteObserver::~ConcreteObserver()
{std::cout << "Goodbye! I'm a ConcreteObserver" << this->number_ << std::endl;ConcreteObserver::static_number_--;this->_mediator->detach(this);
}int main()
{// tfboys的助理Mediator *mediator = new Mediator(); // 先来一个中介// tfboys绑定一个助理Publisher *publisher = new Publisher(mediator);// tfboys的粉丝Observer *observer1 = new ConcreteObserver(mediator); // 现在粉丝直接和助理进行关联Observer *observer2 = new ConcreteObserver(mediator);Observer *observer3;Observer *observer4;// 下面两行可以搞成异步的,但是我不知道怎么搞// mediator->attach(observer1);// mediator->attach(observer2);publisher->publish();observer3 = new ConcreteObserver(mediator);observer4 = new ConcreteObserver(mediator);publisher->publish();delete observer1;publisher->publish();delete observer2;delete observer3;delete observer4;delete publisher;delete mediator;return 0;
}
/*
Hi! I'm a Mediator
Hi! I'm a Publisher
Hi! I'm a ConcreteObserver1
Hi! I'm a ConcreteObserver2
Publishing an update...
Received an update from the publisher!
Received an update from the publisher!
Hi! I'm a ConcreteObserver3
Hi! I'm a ConcreteObserver4
Publishing an update...
Received an update from the publisher!
Received an update from the publisher!
Received an update from the publisher!
Received an update from the publisher!
Goodbye! I'm a ConcreteObserver1
Publishing an update...
Received an update from the publisher!
Received an update from the publisher!
Received an update from the publisher!
Goodbye! I'm a ConcreteObserver2
Goodbye! I'm a ConcreteObserver3
Goodbye! I'm a ConcreteObserver4
Goodbye! I'm a Publisher
Goodbye! I'm a Mediator
*/

总结

观察者模式中,观察者与被观察者有着密切的联系,耦合度高;
为了解决这个问题,程序员们发明了订阅者发布者模式,在这个模式下,
订阅者与发布者没有直接关系,大大的降低了耦合。

上面是我自己简单的见解,下面来看看高级玩家是如何描述的:

观察者模式(Observer Pattern)是一种行为型设计模式,其中存在一个被观察者(也称为主题或可观察者)和多个观察者之间的关系。当被观察者的状态发生变化时,它会通知所有观察者,并调用相应的方法来处理这些变化。这种模式中,被观察者和观察者之间是直接关联的,因此耦合度较高。
订阅者-发布者模式(Pub-Sub Pattern)是一种消息传递模式,其中存在一个发布者和多个订阅者之间的关系。发布者负责发送消息,而订阅者则订阅感兴趣的消息并进行处理。在这种模式中,发布者和订阅者之间没有直接的关联,它们通过消息队列、事件总线等机制进行通信,从而降低了耦合度。
下面是观察者模式和订阅者-发布者模式的一些关键区别:
1.关系类型:观察者模式是一种对象之间的一对多关系,即一个被观察者可以有多个观察者。而订阅者-发布者模式是一种消息传递机制,发布者和订阅者之间没有直接关联。
2.耦合度:观察者模式中,被观察者需要维护一个观察者列表,并将通知直接发送给观察者。这导致了较高的耦合度。而在订阅者-发布者模式中,发布者不需要知道谁订阅了它的消息,也无需维护订阅者列表,只需将消息发布到消息队列或事件总线中即可。
3.灵活性:由于观察者模式中被观察者和观察者之间的直接联系,可能会导致紧密耦合的设计。而在订阅者-发布者模式中,发布者和订阅者之间解耦,发布者和订阅者的数量和类型可以更加灵活地变化。
总的来说,订阅者-发布者模式通过引入一个中介(如消息队列、事件总线)来降低组件之间的耦合度,使系统更加灵活和可扩展。这种模式在处理大规模分布式系统、异步通信等方面非常有用。然而,在某些情况下,观察者模式仍然是一个简单而有效的解决方案,特别是当只需要维护少量对象之间的关系时。

知识补充:

#include <iostream>using namespace std;class A
{
public:A(){cout << "A" << endl;}private:
};int main(int argc, char **argv)
{A a;//调用构造函数A *a2;//不调用构造函数,仅仅是个指针而已A *a3 = new A();//调用构造函数return 0;
}
/*
A
A
*/

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

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

相关文章

用一个结构去分割二维空间

( A, B )---5*30*2---( 1, 0 )( 0, 1 ) 让网络的输入只有5个节点&#xff0c;AB训练集各由5张二值化的图片组成&#xff0c;让A 中有5个点&#xff0c;B全是0&#xff0c;排列组合&#xff0c;统计迭代次数并排序。 其中5-x有3组数据 4-x 差值结构 迭代次数 5-x 差值结构 …

JAVA之多线程

文章目录 进程与线程多线程的生命周期多线程的实现方式 进程与线程 进程是指运行中的应用程序&#xff0c;每一个进程都有自己独立的内存空间&#xff1b;线程是指进程中的一个执行流程&#xff0c;有时也称为执行情景&#xff1b;一个进程可以由多个线程组成&#xff0c;即在…

springboot中如何加载测试专用属性

测试 加载测试专用属性 加载测试专用配置 Web环境模拟测试 数据层测试回滚 测试用例数据设定 1.在启动测试环境时可以通过properties参数设置测试环境专用的属性 SpringBootTest(properties {"test.proptestValue1"}) public class PropertiesAndArgsTest {Value(…

Web应用-Thinkphp框架-开发指南

Thinkphp框架 二级导航分类&#xff0c;模板继承&#xff0c;内置标签Public 修改MVC模块化 ——访问机制传参加载模版模版引入 分离Runtime 缓存文件管理员添加数据验证及验证场景 控制器 validate 在sql执行&#xff08;敏感操作&#xff09;之前验证数据模板 分页数据表连接…

[MoeCTF 2023] web题解

文章目录 webhttpcookie彼岸的flagmoe图床大海捞针夺命十三枪 web http 连接到本地后&#xff0c;题目给了我们任务 第一个是要求我们GET传参UwUu第二个是要求我们POST传参Luvu第三个是要求我们cookie值为admin第四个是要求我们来自127.0.0.1第五个是要求我们用MoeBrowser浏…

ajax实现原理

网页应用能够快速地将增量更新呈现在用户界面上&#xff0c;而不需要重载&#xff08;刷新&#xff09;整个页面。这使得程序能够更快地回应用户的操作 Ajax的实现原理 创建Ajax对象 传入请求方式和请求地址 发送请求 获取服务器与客户端的响应数据 xhr.responseText // 1…

C语言-程序环境和预处理(1)编译、连接介绍以及预处理函数,预处理符号详解及使用说明。

前言 本篇文章讲述了程序的翻译环境和执行环境&#xff0c;编译、连接&#xff0c;预定义符号&#xff0c;#define&#xff0c;#符号和##符号的相关知识。 文章目录 前言1.程序的翻译环境和执行环境2.编译链接2.1 翻译环境2.2 运行环境 3.预处理详解&#xff08;各预处理符号使…

基于微服务+Java+Spring Cloud开发的建筑工地智慧平台源码 云平台多端项目源码

建筑工地智慧平台源码&#xff0c;施工管理端、项目监管端、数据大屏端、移动APP端全套源码 技术架构&#xff1a;微服务JavaSpring Cloud VueUniApp MySql自主版权实际应用案例演示 建筑工地智慧平台已应用于线上巡查、质量管理、实名制管理、危大工程管理、运渣车管理、绿色…

windows系统使用cmd执行.py文件并且传入参数 | 神经网络模型训练 | 主打能用就行

0. 本文概括/ 需求 这个文章cover了一下需求&#xff1a; python环境由annaconda (conda同理)控制指定3个random seed&#xff0c;并且使用cmd命令依次执行train.pytrain.py文件需要传入参数seed&#xff0c;train.py文件中&#xff0c;需要import位于其他.py文件中自定义的cla…

凯百斯纳米盛装亮相2024济南生物发酵展专注于高压均质解决方案

凯百斯纳米技术&#xff08;上海&#xff09;有限公司盛装亮相2024济南生物发酵展&#xff0c;专注于高压均质、破碎、乳化、分散、粉碎等解决方案&#xff01; 2024第12届国际生物发酵产品与技术装备展&#xff08;济南展&#xff09;将于2024年3月5-7日在山东国际会展中心盛…

创建一个react项目 create-next-app

之前是用的creact-react-app来创建的&#xff1a;npx create-react-app my-app 现在官网却推荐使用creact-next-app: npx create-next-app 我们把他所有的选项都选上 这里的css使用了tailwind 项目结构很奇怪啊&#xff0c;没找到.html文件&#xff0c;只在src/app/layout.t…

第4章 选择结构程序设计

if语句&#xff08;if单分支、if-else双分支、else-if嵌套&#xff09;&#xff1b;switch语句;选择结构的嵌套;选择结构程序设计方法。 1.基本要求 (1)理解三种if语句的语法结构,掌握使用if语句设计选择结构程序的方法。 (2)理解switch语句的语法结构,掌握使用switch语句设计…

竞赛 深度学习LSTM新冠数据预测

文章目录 0 前言1 课题简介2 预测算法2.1 Logistic回归模型2.2 基于动力学SEIR模型改进的SEITR模型2.3 LSTM神经网络模型 3 预测效果3.1 Logistic回归模型3.2 SEITR模型3.3 LSTM神经网络模型 4 结论5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 …

centos离线安装telnet、traceroute工具

安装包下载地址 安装包下载地址在这里 直接输入包名&#xff0c;筛选系统&#xff0c;根据自己系统版本确定该下哪个包 centos离线安装telnet 准备三个安装包 xinetd-2.3.15-14.el7.x86_64.rpmtelnet-server-0.17-65.el7_8.x86_64.rpmtelnet-0.17-65.el7_8.x86_64.rpm 三个…

latex如何对.pdf格式的图片实现裁剪

目录 问题描述&#xff1a; 问题解决&#xff1a; 问题描述&#xff1a; 在使用draw.io进行绘图&#xff0c;导出的时候不知道为什么周围会有留白&#xff0c;比如下图&#xff1a; 在导入latex的时候&#xff0c;会因为两侧的留白导致整张图片缩小。 如果直接进行裁剪.pdf&a…

学习记忆——数学篇——案例——代数——不等式——一元二次不等式

重点记忆法 归类记忆法 解一元二次不等式的步骤 1.先化成标准型&#xff1a; a x 2 b x c > 0 ( 或 < 0 ) ax^2bxc>0(或<0) ax2bxc>0(或<0)&#xff0c;且a >0&#xff1b; 2.计算对应方程的判别式 △ △ △&#xff1b; 3.求对应方程的根&#xff1b…

Apache_Log4j2查找功能JNDI注入_CVE-2021-44228

Apache_Log4j2查找功能JNDI注入_CVE-2021-44228 文章目录 Apache_Log4j2查找功能JNDI注入_CVE-2021-442281 在线漏洞解读:2 环境搭建3 影响版本&#xff1a;4 漏洞复现4.1 访问页面4.2 poc漏洞验证 4.3 在dnslog获取个域名4.4 使用bp抓包进行分析4.5 通信成功&#xff0c;此处可…

有外媒称,Linux 发行版Ubuntu 23.10也将正式支持树莓派 5

据了解&#xff0c;在树莓派 4 单板计算机推出 4年后&#xff0c;树莓派 5也在上月末正式发布&#xff0c;并且两者对比之后可以发现&#xff0c;树莓派 5主要提升性能是添加了对 PCIe 2.0的支持。 Multiable万达宝医疗ERP(www.multiable.com.cn/solutions_yl)具备严格的保质期…

Windows中将tomcat以服务的形式安装,然后在服务进行启动管理

Windows中将tomcat以服务的形式安装,然后在服务进行启动管理 第一步&#xff1a; 在已经安装好的tomcat的bin目录下&#xff1a; 输入cmd&#xff0c;进入命令窗口 安装服务&#xff1a; 输入如下命令&#xff0c;最后是你的服务名&#xff0c;避免中文和特殊字符 service.…

cartographer中的扫描匹配

cartographer中的扫描匹配 cartographer中使用了两种扫描匹配方法&#xff1a;CSM&#xff08;相关性扫描匹配方法&#xff08;暴力匹配&#xff09;&#xff09;、ceres优化匹配方法 CSM可以简单地理解为暴力搜索&#xff0c;即每一个激光数据与子图里的每一个位姿进行匹配&…