优化C++设计模式:用模板代替虚函数与多态机制

news2024/11/17 9:14:49

文章目录

    • 0. 引言
    • 1. 模板编程替换虚函数和多态的必要性
      • 1.1. MISRA C++对类型转换和虚函数的规定
      • 1.2. 虚函数与多态问题的影响及如何适应MISRA C++要求
      • 1.3. 模板编程的优势:替代虚函数和多态机制
    • 2. 设计模式改进
      • 2.1. 单例模式的改进
        • 与静态局部变量的对比(第二种实现)
      • 2.2. 工厂模式的改进
      • 2.3. 观察者模式的改进
      • 2.4. 适配器模式的改进
      • 2.5. 桥接模式的改进
      • 2.6. 装饰器模式的改进
      • 2.7. 责任链模式的改进

0. 引言

MISRA C++ 标准对类型转换和虚函数使用做出严格规定,尤其在嵌入式系统中,以确保代码的可预测性和高效性。传统设计模式中虚函数和多态性引入会增加运行时开销,增加系统负担。然而,模板编程通过编译时决策代替运行时动态绑定,可以提升性能并降低内存消耗。
本文将尝试使用C++11改进常用的几个设计模式。

1. 模板编程替换虚函数和多态的必要性

1.1. MISRA C++对类型转换和虚函数的规定

MISRA C++ 标准对于类型转换和虚函数的有严格的使用要求,要求包括:

  • Rule 5-0-1(MISRA C++ 2008):禁止使用不安全的类型转换,如 reinterpret_castconst_caststatic_castdynamic_cast
  • Rule 5-0-2:要求类型转换必须明确指定转换类型,确保转换符合预期行为,避免使用不安全的转换方式。
  • Rule 5-1-1:在进行类型转换时,必须确保转换类型合法,不破坏对象的常量性或类型。

同时,虚函数和多态机制在嵌入式系统中也有劣势:

  • 运行时开销:虚函数表(vtable)的查找会带来额外的运行时开销,增加程序的资源需求。
  • RTTI(运行时类型信息)开销:启用 RTTI 会增加内存使用,尤其是在 dynamic_casttypeid 被使用时。
  • MISRA C++的限制:标准要求避免使用虚函数和多态机制,特别是在没有严格控制的情况下,可能会导致代码行为不可预测,增加出错的风险。

1.2. 虚函数与多态问题的影响及如何适应MISRA C++要求

虚函数和多态机制在嵌入式系统中,往往面临以下问题:

  • 运行时性能问题:虚函数表查找、RTTI机制的引入,都会增加程序的运行时开销,尤其是在资源受限的嵌入式系统中,可能会显著影响系统性能。
  • 内存开销:启用 RTTI 会使每个多态类占用额外的内存,这对于内存有限的嵌入式系统是一个不小的负担。
  • MISRA C++的限制:虚函数和多态机制的使用在 MISRA C++ 中受到限制,因为它们增加了代码的复杂性,降低了可预测性,并且可能违反一些规则,如避免不安全的类型转换。

为了适应 MISRA C++ 的要求,可以考虑以下几点:

  • 避免虚函数和多态:尽量避免使用运行时多态和虚函数,减少 RTTI 开销,避免使用 dynamic_casttypeid
  • 设计时决策:通过明确的类型接口和结构体来实现类型转换,确保每个类型转换都能被清晰审查和验证。
  • 使用强类型系统:避免使用 void* 等不明确类型的指针,确保类型清晰,从而减少不必要的类型转换。

1.3. 模板编程的优势:替代虚函数和多态机制

模板编程通过在编译时绑定函数和类,避免运行时的虚函数查找和多态性问题。使用模板编程的优势如下:

  • 编译时绑定:模板代码在编译时就确定了函数调用和对象行为,避免了虚函数表的查找。
  • 内联优化:编译器能够对模板代码进行内联优化,直接将模板代码嵌入到调用点。
  • 提高执行效率:由于没有动态绑定和运行时类型信息,模板编程能够显著降低程序的运行时开销,提升性能。

模板编程避免了虚函数和多态机制的开销,并且在遵循 MISRA C++ 标准时,能够更容易地实现高效且符合规范的设计模式。

2. 设计模式改进

在传统的设计模式中,许多模式(如单例模式、工厂模式、观察者模式等)依赖于虚函数和继承来实现多态和灵活的行为切换。然而,这种方式虽然灵活,但会带来运行时的开销,包括虚函数表查找、内存使用的增加以及与运行时类型信息(RTTI)相关的性能损失。

模板编程静态多态 通过在编译时进行决策,避免了虚函数调用和动态绑定的需要。通过使用模板,我们可以在不牺牲灵活性的情况下,提高代码的执行效率,并且能够使代码更加符合 MISRA C++ 等标准的要求。

以下是一些常见设计模式的改进方式,尽量在不违背设计原则的情况下提升代码的性能。

2.1. 单例模式的改进

传统的单例模式通过 static 变量和线程同步机制来保证只创建一个实例。然而,如果使用虚函数或者多态,在某些情况下可能会影响线程安全性,增加程序的复杂度。在 C++11/14 中,可以利用 std::once_flagstd::call_once 来实现线程安全的单例,而无需依赖虚函数。

通过模板和 std::unique_ptr 来管理单例实例,可以避免虚函数的使用,同时保证线程安全和内存管理。

#include <iostream>
#include <memory>  // for std::unique_ptr
#include <mutex>

class Singleton {
 public:
  // 获取唯一实例
  static Singleton& getInstance() {
    std::call_once(initFlag_, &Singleton::initSingleton);
    return *instance_;
  }

  // 示例方法
  void doSomething() {
    std::cout << "Singleton instance is doing something!" << std::endl;
  }

 private:
  Singleton() {
    std::cout << "Singleton initialized!" << std::endl;
  }

  // 禁止拷贝构造和赋值操作
  Singleton(const Singleton&) = delete;
  Singleton& operator=(const Singleton&) = delete;

  // 使用 std::unique_ptr 管理单例实例
  static std::unique_ptr<Singleton> instance_;
  static std::once_flag initFlag_;

  // 初始化实例的私有方法
  static void initSingleton() {
    instance_ = std::make_unique<Singleton>();
  }
};

// 初始化静态成员
std::unique_ptr<Singleton> Singleton::instance_ = nullptr;
std::once_flag Singleton::initFlag_;

int main() {
  // 获取并使用单例对象
  Singleton& singleton1 = Singleton::getInstance();
  singleton1.doSomething();

  // 再次获取单例对象,确保不会重新初始化
  Singleton& singleton2 = Singleton::getInstance();
  singleton2.doSomething();

  return 0;
}

执行结果

Singleton initialized!
Singleton instance is doing something!
Singleton instance is doing something!
与静态局部变量的对比(第二种实现)

请看如下的实现实现:

class Singleton {
 public:
  static Singleton& getInstance() {
    static Singleton instance;
    return instance;
  }

 private:
  Singleton() = default;
};

区别对比

特性第一种实现第二种实现
线程安全性手动使用 std::once_flag 实现静态局部变量自动线程安全
复杂性较高,需维护多个静态成员和初始化方法较低,逻辑直接嵌入函数内
灵活性更灵活,适合复杂初始化逻辑不够灵活,构造函数绑定初始化逻辑
析构时机使用 std::unique_ptr,支持析构依赖静态变量销毁,时机不可控
适用场景更适合需要复杂初始化逻辑的场景适合大多数简单单例模式需求

2.2. 工厂模式的改进

工厂模式通常通过继承和虚函数来实例化不同的产品对象。而在使用模板的工厂模式中,我们通过将产品类型作为模板参数来实现静态多态,从而避免了使用虚函数。这种方式通过编译时的类型推导,确保了工厂方法能够在编译时确定,而不依赖于运行时的动态绑定。

CRTP(Curiously Recurring Template Pattern)是一种常见的技巧,它通过将子类作为模板参数,允许在基类中静态地定义行为,从而避免了虚函数的开销。通过这样的改进,我们可以在不牺牲灵活性的前提下,减少运行时的性能损耗。

#include <iostream>
#include <memory>
#include <type_traits>

// 基础产品接口
template <typename ProductType>
class Product {
 public:
  void operation() const {
    static_assert(std::is_base_of<Product, ProductType>::value, "ProductType must be derived from Product");
    static_cast<const ProductType*>(this)->operationImpl();
  }

 private:
  // 使用 CRTP (Curiously Recurring Template Pattern) 进行静态多态
  void operationImpl() const = delete;
};

// 具体产品A
class ConcreteProductA : public Product<ConcreteProductA> {
 public:
  void operationImpl() const {
    std::cout << "ConcreteProductA operation." << std::endl;
  }
};

// 具体产品B
class ConcreteProductB : public Product<ConcreteProductB> {
 public:
  void operationImpl() const {
    std::cout << "ConcreteProductB operation." << std::endl;
  }
};

// 工厂类模板
template <typename ProductType>
class Factory {
 public:
  std::unique_ptr<Product<ProductType>> create() const {
    return std::make_unique<ProductType>();
  }
};

int main() {
  Factory<ConcreteProductA> factoryA;
  auto productA = factoryA.create();
  productA->operation();

  Factory<ConcreteProductB> factoryB;
  auto productB = factoryB.create();
  productB->operation();

  return 0;
}

执行结果

ConcreteProductA operation.
ConcreteProductB operation.

2.3. 观察者模式的改进

在传统的观察者模式中,观察者通常通过继承和虚函数来注册和通知其他对象。而在模板化的观察者模式中,我们使用 std::function 来替代虚函数,利用模板和类型擦除来实现更加高效和灵活的观察者模式。这样,我们避免了虚函数的调用和继承结构的复杂性,同时提高了类型安全性和灵活性。

#include <functional>
#include <iostream>
#include <map>
#include <string>
#include <vector>

template <typename EventHandler>
class Events {
 public:
  Events() : nextKey_(0) {
  }

  // 注册事件处理函数
  uint32_t addObserver(EventHandler&& handler) {
    uint32_t key = nextKey_++;
    storeHandler(key, std::forward<EventHandler>(handler));
    return key;
  }

  uint32_t addObserver(EventHandler& handler) {
    uint32_t key = nextKey_++;
    storeHandler(key, handler);
    return key;
  }

  // 移除事件处理函数
  void removeObserver(uint32_t key) {
    handlers_.erase(key);
  }

  // 通知所有注册的事件处理函数
  template <typename... Args>
  void notifyAll(Args&&... args) {
    for (auto& it : handlers_) {
      for (auto& handler : it.second) {
        handler(std::forward<Args>(args)...);
      }
    }
  }

  // 触发事件,通知所有注册的事件处理器
  template <typename... Args>
  inline void trigger(Args&&... args) {
    notifyAll(std::forward<Args>(args)...);
  }

  // 删除拷贝构造和赋值操作
  Events(const Events&) = delete;
  Events& operator=(const Events&) = delete;

  ~Events() = default;

  // 通过 += 操作符添加事件处理器
  uint32_t operator+=(EventHandler&& handler) {
    return addObserver(std::forward<EventHandler>(handler));
  }

  uint32_t operator+=(EventHandler& handler) {
    return addObserver(handler);
  }

  // 通过 -= 操作符移除事件处理器
  Events& operator-=(uint32_t key) {
    removeObserver(key);
    return *this;
  }

  // 清除所有事件处理器
  void clear() {
    handlers_.clear();
  }

 private:
  // 存储事件处理函数
  template <typename Handler>
  void storeHandler(uint32_t key, Handler&& handler) {
    handlers_[key].emplace_back(std::forward<Handler>(handler));
  }

 private:
  uint32_t nextKey_;                                        // 唯一的事件处理函数键值
  std::map<uint32_t, std::vector<EventHandler>> handlers_;  // 存储事件处理函数,支持多个处理器
};

//
// 事件处理函数1
void handler1(int data) {
  std::cout << "Handler1 received data: " << data << std::endl;
}

// 事件处理函数2
void handler2(int data) {
  std::cout << "Handler2 received data: " << data << std::endl;
}

int main() {
  // 使用 int 作为事件的键类型,事件处理函数是 std::function<void(int)>
  Events<std::function<void(int)>> events;

  // 注册两个事件处理器到同一个事件
  auto eventKey1 = events += handler1;
  auto eventKey2 = events += handler2;

  // 触发事件,所有绑定到该事件的处理器都会被调用
  events.trigger(100);  // 输出:
  // Handler1 received data: 100
  // Handler2 received data: 100

  // 移除事件处理器
  events -= eventKey1;

  std::cout << "After removing handler1:" << std::endl;

  // 再次触发事件,只有 handler2 会被调用
  events.trigger(200);  // 输出:Handler2 received data: 200

  return 0;
}

执行结果:

Handler1 received data: 100
Handler2 received data: 100
After removing handler1:
Handler2 received data: 200

2.4. 适配器模式的改进

适配器模式用于解决接口不兼容的问题。假设我们有一个基于激光雷达的数据接口,但我们需要将这个接口适配到自动驾驶控制系统。

#include <iostream>
#include <memory>

class RadarSensor {
 public:
  void readData() {
    std::cout << "Reading data from Radar Sensor.\n";
  }
};

class CameraSensor {
 public:
  void captureImage() {
    std::cout << "Capturing image from Camera Sensor.\n";
  }
};

// 适配器模板类
template <typename T>
class SensorAdapter {
 public:
  void operate() {
    static_assert(std::is_base_of<SensorAdapter, T>::value, "T must be derived from SensorAdapter");
    static_cast<T*>(this)->operateImpl();
  }
private:
 void operateImpl() = delete;
};

class RadarAdapter : public SensorAdapter<RadarAdapter> {
 private:
  RadarSensor radar_;

 public:
  void operateImpl() {
    radar_.readData();
  }
};

class CameraAdapter : public SensorAdapter<CameraAdapter> {
 private:
  CameraSensor camera_;

 public:
  void operateImpl() {
    camera_.captureImage();
  }
};

int main() {
  RadarAdapter radarAdapter;
  CameraAdapter cameraAdapter;

  radarAdapter.operate();   // Reading data from Radar Sensor
  cameraAdapter.operate();  // Capturing image from Camera Sensor

  return 0;
}

执行结果:

Reading data from Radar Sensor.
Capturing image from Camera Sensor.

2.5. 桥接模式的改进

在桥接模式中,模板类可以在编译时根据实现的类型来选择适当的操作,从而避免了虚函数的调用开销。

#include <iostream>
#include <memory>

// 实现接口(通过模板类实现)
template <typename T>
class Implementor {
 public:
  void operationImpl() const {
    static_assert(std::is_base_of<Implementor, T>::value, "T must be derived from Implementor");
    static_cast<const T*>(this)->operationImpl();
  }
};

// 具体实现A
class ConcreteImplementorA : public Implementor<ConcreteImplementorA> {
 public:
  void operationImpl() const {
    std::cout << "ConcreteImplementorA operation." << std::endl;
  }
};

// 具体实现B
class ConcreteImplementorB : public Implementor<ConcreteImplementorB> {
 public:
  void operationImpl() const {
    std::cout << "ConcreteImplementorB operation." << std::endl;
  }
};

// 抽象类,模板方法和组合
template <typename T>
class Abstraction {
 public:
  explicit Abstraction(std::shared_ptr<Implementor<T>> implementor) : implementor_(implementor) {
  }

  void operation() const {
    implementor_->operationImpl();  // 调用实现类的操作
  }

 protected:
  std::shared_ptr<Implementor<T>> implementor_;
};

// 扩展抽象类A
template <typename T>
class RefinedAbstractionA : public Abstraction<T> {
 public:
  explicit RefinedAbstractionA(std::shared_ptr<Implementor<T>> implementor) : Abstraction<T>(implementor) {
  }

  void operation() const {
    std::cout << "RefinedAbstractionA operation: ";
    Abstraction<T>::operation();
  }
};

// 扩展抽象类B
template <typename T>
class RefinedAbstractionB : public Abstraction<T> {
 public:
  explicit RefinedAbstractionB(std::shared_ptr<Implementor<T>> implementor) : Abstraction<T>(implementor) {
  }

  void operation() const {
    std::cout << "RefinedAbstractionB operation: ";
    Abstraction<T>::operation();
  }
};

// 使用模板的桥接模式
int main() {
  // 创建具体实现
  auto implementorA = std::make_shared<ConcreteImplementorA>();
  auto implementorB = std::make_shared<ConcreteImplementorB>();

  // 创建抽象类A和B
  RefinedAbstractionA<ConcreteImplementorA> abstractionA(implementorA);
  RefinedAbstractionB<ConcreteImplementorB> abstractionB(implementorB);

  // 调用操作
  abstractionA.operation();  // 输出: RefinedAbstractionA operation: ConcreteImplementorA operation.
  abstractionB.operation();  // 输出: RefinedAbstractionB operation: ConcreteImplementorB operation.

  return 0;
}

执行结果:

RefinedAbstractionA operation: ConcreteImplementorA operation.
RefinedAbstractionB operation: ConcreteImplementorB operation.

2.6. 装饰器模式的改进

装饰器模式通过动态地组合不同的功能来扩展对象的行为。传统的装饰器模式依赖于继承和虚函数来实现动态行为的扩展,而模板化的装饰器模式通过组合和模板参数来实现类似的功能,避免了虚函数的调用和继承层次的复杂性。

#include <iostream>
#include <memory>

// 基本组件接口
template <typename T>
class Component {
 public:
  virtual void operation() = 0;
  virtual ~Component() = default;
};

// 具体组件
class ConcreteComponent : public Component<ConcreteComponent> {
 public:
  void operation() override {
    std::cout << "ConcreteComponent operation\n";
  }
};

// 装饰器基类
template <typename T>
class Decorator : public Component<T> {
 protected:
  std::unique_ptr<Component<T>> component_;  // 装饰的组件
 public:
  explicit Decorator(std::unique_ptr<Component<T>> component) : component_(std::move(component)) {
  }

  void operation() override {
    component_->operation();
  }
};

// 具体装饰器A
class ConcreteDecoratorA : public Decorator<ConcreteComponent> {
 public:
  explicit ConcreteDecoratorA(std::unique_ptr<Component<ConcreteComponent>> component)
      : Decorator<ConcreteComponent>(std::move(component)) {
  }

  void operation() override {
    std::cout << "ConcreteDecoratorA operation\n";
    Decorator<ConcreteComponent>::operation();  // 调用原始组件的操作
  }
};

// 具体装饰器B
class ConcreteDecoratorB : public Decorator<ConcreteComponent> {
 public:
  explicit ConcreteDecoratorB(std::unique_ptr<Component<ConcreteComponent>> component)
      : Decorator<ConcreteComponent>(std::move(component)) {
  }

  void operation() override {
    std::cout << "ConcreteDecoratorB operation\n";
    Decorator<ConcreteComponent>::operation();  // 调用原始组件的操作
  }
};

int main() {
  // 创建基本组件
  auto component = std::make_unique<ConcreteComponent>();

  // 使用装饰器A包装组件
  auto decoratorA = std::make_unique<ConcreteDecoratorA>(std::move(component));

  // 使用装饰器B包装装饰器A
  auto decoratorB = std::make_unique<ConcreteDecoratorB>(std::move(decoratorA));

  // 调用最终的操作
  decoratorB->operation();

  return 0;
}

执行结果:

ConcreteDecoratorB operation
ConcreteDecoratorA operation
ConcreteComponent operation

2.7. 责任链模式的改进

责任链模式通过一系列的处理器对象来处理请求,每个处理器对象可以选择处理请求或将请求传递给下一个处理器。传统的责任链模式通常使用虚函数来决定请求的处理逻辑,而模板化的责任链模式通过模板和静态多态来替代虚函数。

#include <iostream>
#include <memory>

// 请求类型
struct Request {
  int value;
  explicit Request(int v) : value(v) {
  }
};

// 处理器基类
class HandlerBase {
 public:
  virtual void handleRequest(Request& request) = 0;
  void setNext(std::shared_ptr<HandlerBase> next) {
    next_ = next;
  }

 protected:
  std::shared_ptr<HandlerBase> next_;
};

// 具体处理器A
class ConcreteHandlerA : public HandlerBase {
 public:
  void handleRequest(Request& request) override {
    if (request.value < 10) {
      std::cout << "ConcreteHandlerA handled request with value " << request.value << std::endl;
    } else if (next_) {
      std::cout << "ConcreteHandlerA passing request to next handler." << std::endl;
      next_->handleRequest(request);
    }
  }
};

// 具体处理器B
class ConcreteHandlerB : public HandlerBase {
 public:
  void handleRequest(Request& request) override {
    if (request.value >= 10 && request.value < 20) {
      std::cout << "ConcreteHandlerB handled request with value " << request.value << std::endl;
    } else if (next_) {
      std::cout << "ConcreteHandlerB passing request to next handler." << std::endl;
      next_->handleRequest(request);
    }
  }
};

// 具体处理器C
class ConcreteHandlerC : public HandlerBase {
 public:
  void handleRequest(Request& request) override {
    std::cout << "ConcreteHandlerC handled request with value " << request.value << std::endl;
  }
};

int main() {
  // 创建处理器实例
  auto handlerA = std::make_shared<ConcreteHandlerA>();
  auto handlerB = std::make_shared<ConcreteHandlerB>();
  auto handlerC = std::make_shared<ConcreteHandlerC>();

  // 设置责任链
  handlerA->setNext(handlerB);
  handlerB->setNext(handlerC);

  // 测试请求
  Request request1(5);
  handlerA->handleRequest(request1);

  Request request2(15);
  handlerA->handleRequest(request2);

  Request request3(25);
  handlerA->handleRequest(request3);

  return 0;
}

执行结果:

ConcreteHandlerA handled request with value 5
ConcreteHandlerA passing request to next handler.
ConcreteHandlerB handled request with value 15
ConcreteHandlerA passing request to next handler.
ConcreteHandlerB passing request to next handler.
ConcreteHandlerC handled request with value 25

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

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

相关文章

基于java Springboot高校失物招领平台

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

[C++] 智能指针

文章目录 智能指针的使用原因及场景分析为什么需要智能指针&#xff1f;异常抛出导致的资源泄漏问题分析 智能指针与RAIIC常用智能指针 使用智能指针优化代码优化后的代码优化点分析 析构函数中的异常问题解决方法 RAII 和智能指针的设计思路详解什么是 RAII&#xff1f;RAII 的…

Git回到某个分支的某次提交

1.切换到需要操作的分支&#xff08;<branch-name>是分支名称&#xff09;。 命令如下&#xff1a; git checkout <branch-name> 2.获取代码的提交记录 。命令如下&#xff1a; git log 按q退出当前命令对话。 获取到某次提交或者合并的hash值&#xff08;下文…

掌握 Spring Boot 的最佳方法 – 学习路线图

在企业界&#xff0c;人们说“Java 永垂不朽&#xff01;”。但为什么呢&#xff1f;Java 仍然是开发企业应用程序的主要平台之一。大型公司使用企业应用程序来赚钱。这些应用程序具有高可靠性要求和庞大的代码库。根据Java开发人员生产力报告&#xff0c;62% 的受访开发人员使…

1. Django中的URL调度器 (项目创建与简单测试)

1. 创建 Django 项目 运行以下命令创建一个名为 blog_project 的 Django 项目&#xff1a; django-admin startproject blog_project2. 创建博客应用 Django 中&#xff0c;项目可以包含多个应用。创建一个名为 blog 的应用&#xff1a; cd blog_project python manage.py …

frp内网穿透介绍安装教程

文章目录 前言一、安装二、测试使用总结 前言 内网穿透&#xff08;Port Forwarding&#xff09;是将公网上的IP地址映射到内部网络中的一台计算机的某个端口上&#xff0c;以便外部网络可以访问该计算机中运行的应用程序。内网穿透技术可以通过一些开源工具来实现&#xff0c…

主界面获取个人信息客户端方

主界面获取个人信息客户端方 前言 上一集我们完成了websocket身份验证的内容&#xff0c;那么这一集开始我们将要配合MockServer来完成主界面获取个人信息的内容。 需求分析 我们这边是完成客户端那方的内容&#xff0c;当客户端登录成功之后&#xff0c;我们就要从服务器获…

redis实现消息队列的几种方式

一、了解 众所周知&#xff0c;redis是我们日常开发过程中使用最多的非关系型数据库&#xff0c;也是消息中间件。实际上除了常用的rabbitmq、rocketmq、kafka消息队列&#xff08;大家自己下去研究吧~模式都是通用的&#xff09;&#xff0c;我们也能使用redis实现消息队列。…

单片机智能家居火灾环境安全检测

目录 前言 一、本设计主要实现哪些很“开门”功能&#xff1f; 二、电路设计原理图 电路图采用Altium Designer进行设计&#xff1a; 三、实物设计图 四、程序源代码设计 五、获取资料内容 前言 在现代社会&#xff0c;火灾安全始终是人们关注的重点问题。随着科技的不…

【目标检测】用YOLOv8-Segment训练语义分割数据集(保姆级教学)

前言 这篇教程会手把手带你用 YOLOv8-Segment 搭建一个属于自己的分割任务项目。从环境配置到数据集准备&#xff0c;再到模型训练和测试&#xff0c;所有步骤都有详细说明&#xff0c;适合初学者使用。你将学会如何安装必要的软件&#xff0c;标注自己的数据&#xff0c;并使…

mac2019环境 Airflow+hive+spark+hadoop本地环境安装

1 环境介绍 本地安装可分为两个部分&#xff0c;mac软件环境&#xff0c; python开发环境 ps: 安装过程参考chatgpt、csdn文章 1.1 mac软件环境 目标安装的的软件是hive、apache-spark、hadoop&#xff0c;但是这三个软件又依赖java(spark依赖&#xff09;、ssh&#xff08…

1.7 JS性能优化

从输入url到页面加载完成都做了些什么 输入 URL - 资源定位符 http://www.zhaowa.com - http 协议 域名解析 https://www.zhaowa.com > ip 1. 切HOST&#xff1f; > 浏览器缓存映射、系统、路由、运营商、根服务器 2. 实际的静态文件存放&#xff1f; 大流量 > 多个…

【Ansible常用命令+模块+Playbook+Roles】

Ansible 一、命令1.1 常用命令 二、模块2.1 shell模块2.2 复制模块2.3 用户模块2.4 软件包管理2.5 服务模块2.6 文件模块2.7 收集模块2.8 fetch2.9 cron2.10 group2.11 script2.12 unarchive 三、YAML Roles3.1 目录结构3.2 文件内容tasks/main.yamlnginx.conf.j2vars/main.yam…

Oracle19C AWR报告分析之Wait Classes by Total Wait Time

Oracle19C AWR报告分析之Wait Classes by Total Wait Time 一、分析数据二、详细分析2.1 指标参数介绍2.2 数据库性能分析2.3 综合性能评估 在 Oracle 数据库的 AWR 报告中&#xff0c;Wait Classes by Total Wait Time 是评估数据库性能的重要部分。本篇文章主要是介绍指标参数…

嵌入式硬件电子电路设计(五)MOS管详解(NMOS、PMOS、三极管跟mos管的区别)

引言&#xff1a;在我们的日常使用中&#xff0c;MOS就是个纯粹的电子开关&#xff0c;虽然MOS管也有放大作用&#xff0c;但是几乎用不到&#xff0c;只用它的开关作用&#xff0c;一般的电机驱动&#xff0c;开关电源&#xff0c;逆变器等大功率设备&#xff0c;全部使用MOS管…

问题大集-01-kafka问题

1、问题&#xff1a;Windows下启动单机kafka出现&#xff1a;系统找不到指定路径 解决&#xff1a; 是kafka不能识别本机的java环境&#xff08;JVM&#xff09;&#xff0c;故需要指定java路径&#xff0c; 进入kafka路径下的\bin\windows&#xff0c;找到&#xff1a;kafk…

C++ 的发展

目录 C 的发展总结&#xff1a;​编辑 1. C 的早期发展&#xff08;1979-1985&#xff09; 2. C 标准化过程&#xff08;1985-1998&#xff09; 3. C 标准演化&#xff08;2003-2011&#xff09; 4. C11&#xff08;2011年&#xff09; 5. C14&#xff08;2014年&#xf…

Ubuntu问题 -- 允许ssh使用root用户登陆

目的 新重装的系统, 普通用户可以使用ssh登陆服务器, 但是root不能使用ssh登陆 方法 vim 编辑ssh配置文件 sudo vim /etc/ssh/sshd_config找到 PermitRootLogin 这一行, 把后面值改成 yes 重启ssh sudo service sshd restart然后使用root账号登陆即可

HarmonyOS4+NEXT星河版入门与项目实战--------开发工具与环境准备

文章目录 1、熟悉鸿蒙官网1、打开官网2、下载 DevEco Studio3、HarmonyOS 资源库4、开发指南与API 2、安装 DevEco Studio1、软件安装2、配置开发工具 1、熟悉鸿蒙官网 1、打开官网 百度搜索 鸿蒙开发者官网 点击进入开发者官网&#xff0c;点击开发&#xff0c;可以看到各种…

使用 start-local 脚本在本地运行 Elasticsearch

警告&#xff1a;请勿将这些说明用于生产部署 本页上的说明仅适用于本地开发。请勿将此配置用于生产部署&#xff0c;因为它不安全。请参阅部署选项以获取生产部署选项列表。 使用 start-local 脚本在 Docker 中快速设置 Elasticsearch 和 Kibana 以进行本地开发或测试。 此设…