C++ 设计模式 —— 桥接模式

C++ 设计模式 —— 桥接模式

0. 引用连接

本文主要的思路和代码,来自于对以下连接的学习和实现:

桥接模式

1. 引言

1.1 什么是桥接模式?

  • 桥接模式的定义
  • 桥接模式的作用

桥接模式,顾名思义,就像是一座连接两岸的桥梁。在软件开发中,我们可以将桥接模式看作是一座连接抽象部分和实现部分的“桥”,通过这座“桥”,我们可以方便地在抽象部分和实现部分之间进行切换,而不需要关心它们之间的实现细节。

桥接模式的核心作用就是降低系统的耦合度,提高扩展性和可维护性。想象一下你正在修建一座连接两个城市的大桥,如果没有这座桥,你需要绕行很远的距离才能到达对岸。同样地,在软件开发中,如果没有桥接模式,抽象部分和实现部分之间的依赖关系可能会变得非常复杂,导致系统难以扩展和维护。

1.2 桥接模式与其他设计模式的关系

  • 桥接模式和其他设计模式的关系就像是一组不同的工具,每个工具都有其独特的用途。就像在修建一座桥梁时,我们可以选择使用各种不同的材料和工具来完成任务,而不仅仅是一种。同样地,在软件开发中,我们可以结合使用不同的设计模式来解决不同的问题,而桥接模式通常会于开发前期进行设计。
  • 桥接模式、状态模式和策略模式(在某种程度上包括适配器)都具有相似的接口,它们都基于组合模式——即将工作委派给其他对象。但是它们各自解决了不同的问题。例如,如果我们需要将一个类与它的子类进行关联,并且希望在运行时动态地创建子类的实例,那么桥接模式就非常适合。而如果我们需要对一个类的多个行为进行控制,那么状态模式就是一个不错的选择。
  • 我们可以将抽象工厂模式和桥接搭配使用。如果由桥接定义的抽象只能与特定实现合作,这一模式搭配就非常有用。在这种情况下,抽象工厂可以对这些关系进行封装,并且对客户端代码隐藏其复杂性。
  • 生成器模式和桥接模式也可以很好地结合使用:主管类负责抽象工作,各种不同的生成器负责实现工作。这就好比我们在修建桥梁时,需要不同的工人来完成不同的任务一样,生成器模式可以帮助我们更好地管理代码的生成过程。

桥接模式和其他设计模式都是非常有用的工具,它们可以帮助我们更好地组织和管理代码,提高系统的可维护性和可扩展性。在使用这些工具时,我们需要根据具体的需求和场景来选择合适的模式,以最大程度地发挥它们的效用。

1.3 桥接模式适用的场景

桥接模式的应用场景也非常广泛。比如我们正在开发一个电商网站,其中有一个订单管理系统和一个支付系统。这两个系统分别负责订单的处理和支付的操作,但是它们之间的关系非常紧密。如果没有桥接模式,我们需要在订单管理系统和支付系统中都实现相同的逻辑,这显然是不合理的。而如果我们使用桥接模式,可以将订单管理系统和支付系统分别抽象成两个独立的类,并通过一个桥接器来协调它们之间的交互,这样就可以避免代码冗余和耦合度过高的问题。如果我们概括一下桥接模式的使用场景的话,主要有以下几个:

  • 系统中存在多个独立但相互关联的类时,可以使用桥接模式来解耦它们的依赖关系。
  • 当需要动态地创建一系列具有相似特性的对象时,可以使用桥接模式来实现对象的创建和初始化过程。
  • 当需要在一个系统中灵活地切换不同的算法或策略时,可以使用桥接模式来实现算法或策略的切换和组合。
  • 需要通过组合的方式在运行时对算法进行选择时,可以使用桥接模式来避免使用多重条件语句。
  • 需要创建一个可以与多种类型接口进行交互的类时,可以使用桥接模式来实现这个抽象。

2. 桥接模式的实现

桥接模式的实现方式就像是一座连接不同平台的桥梁,它通过抽象部分和实现部分之间的关联来实现。如果以连接桥梁为例,其大致的步骤为:

  1. 需要明确类中独立的维度,也就是确定抽象部分和实现部分之间的独立关系。这些独立的概念可以是抽象/平台、域/基础设施、前端/后端或接口/实现等。
  2. 要了解客户端的业务需求,并在抽象基类中定义它们。这就像在修建一座桥梁时,我们需要考虑不同的交通需求,比如车辆、行人等,并设计出满足这些需求的桥梁结构。
  3. 要确定在所有平台上都可执行的业务,并在通用实现接口中声明抽象部分所需的业务。这就像是在选择材料和工具时,我们需要选择那些能够适用于不同平台的通用材料和工具。
  4. 可以为域内的所有平台创建实现类,但需确保它们遵循实现部分的接口。这就像是在建造桥梁时,我们需要根据设计图纸来建造每个平台的结构。
  5. 在抽象类中添加指向实现类型的引用成员变量,抽象部分会将大部分工作委派给该成员变量所指向的实现对象。这就像是在建造桥梁时,将具体的施工任务委托给各个工人来完成。
  6. 如果高层逻辑有多个变体,则可通过扩展抽象基类为每个变体创建一个精确抽象。这就像是在不同的路段上修建不同类型的桥梁,以满足不同的交通需求。

2.1 抽象部分

抽象部分在桥接模式中起到了至关重要的连接作用。它通过定义接口、封装逻辑、委派工作等方式,将不同的平台进行连接,并帮助客户端与实现部分进行交互。

抽象部分就像是一座桥梁的桥墩,它具体负责了下面的一些内容:

  • 定义和封装与实现部分相关的接口和逻辑。
  • 需要承受来自不同平台的负荷,并将这些负荷传递到实现部分。同样地,抽象部分也需要承载客户端的业务需求,并将这些需求传递给实现部分来完成具体的操作。
  • 定义一些通用的方法和属性,这些方法和属性可以被多个实现类共享和使用。这就好比我们在建造一座桥梁时,需要使用一些通用的材料和工具,以便在不同的地方建造出相同的结构。
  • 通过添加引用成员变量来委派具体的实现对象。这样,抽象部分就可以将大部分的工作交给具体实现来完成,而自己只需要关注与客户端的交互即可。这就好比我们选择了一个优秀的工人来建造桥梁的一部分,让他来完成整个工作,而自己则专注于监督和管理。

2.2 具体部分

  • 定义具体的子类,这些子类实现了抽象部分定义的接口
  • 这些子类可以在运行时动态地被创建,并通过抽象部分提供的接口与客户端代码进行交互

具体部分在桥接模式中同样起到了至关重要的作用。它通过实现抽象部分所定义的方法和属性,完成具体的操作,并帮助客户端与实现部分进行交互。同时,具体部分还可以根据抽象部分的定义,使用不同的实现方式来完成不同的业务逻辑。

具体部分就像是一座桥梁的桥面和支撑结构,它具体负责了下面的一些内容:

  • 实现抽象部分定义的接口和逻辑。
  • 承载客户端的业务需求,并将这些需求实现成具体的操作。
  • 通过添加额外的实现类来实现不同的业务逻辑。这就好比我们在建造一座桥梁时,需要使用不同的材料和工具来建造出不同的结构。
  • 具体部分也需要根据抽象部分的定义,使用不同的实现方式来完成不同的业务逻辑。
  • 通过扩展抽象基类为每个变体创建一个精确抽象。这样,我们就可以针对不同的平台创建不同的具体实现,以满足不同的业务需求。这就好比我们在不同的路段上修建不同类型的桥梁,以满足不同的交通需求。

3. 桥接模式的优点和缺点

3.1 优点

  • 允许我们创建与平台无关的类和程序。这意味着我们可以将不同的平台抽象出来,形成一个统一的高层接口。这样,无论我们在哪个平台上进行开发,都可以使用相同的类和接口来进行交互,而不需要关心具体的平台细节。
  • 客户端代码仅与高层抽象部分进行互动,不会接触到平台的详细信息。这就好比我们通过一座桥梁来连接两个不同的地点,我们只需要关注桥梁上的路径和交通情况,而不需要了解每个地点的具体信息。
  • 桥接模式还遵循了开闭原则。我们可以新增抽象部分和实现部分,并且它们之间不会相互影响。这就好比我们可以通过添加新的桥梁来连接更多的地点,而不需要修改已有的桥梁结构。这种灵活性使得我们的系统可以更容易地适应新的需求和变化。
  • 桥接模式也符合单一职责原则。抽象部分专注于处理高层逻辑,实现部分处理平台细节。这就好比一个桥梁的设计者只需要负责设计出合适的桥梁结构和路径规划,而不需要关心具体的材料和施工细节。这种分离使得各个部分的职责更加明确,易于维护和扩展。

综上所述,桥接模式的优点在于它可以帮助我们更好地组织和管理代码,提高系统的可维护性和可扩展性。通过创建与平台无关的类和程序、客户端代码仅与高层抽象部分进行互动、遵循开闭原则以及单一职责原则等特性,桥接模式为我们提供了一个灵活、高效且易于维护的设计方案。

3.2 缺点

  • 对于高内聚的类来说,使用桥接模式可能会让代码变得更加复杂。这是因为在桥接模式中,我们需要将抽象部分和实现部分分离开来,这可能会增加代码的复杂度。就像建造一座复杂的桥梁需要更多的设计和施工工作一样,使用桥接模式也需要更多的思考和编码工作。
  • 使用桥接模式可能会导致抽象部分的代码量增加,从而影响系统的性能。这是因为抽象部分需要承担更多的职责和功能,可能需要处理更多的细节和逻辑。就像一座桥梁上的支撑结构需要承受更大的负荷一样,抽象部分也需要处理更多的业务逻辑和操作。
  • 使用桥接模式可能会增加系统的复杂性,使得理解和修改代码变得更加困难。这是因为桥接模式将抽象部分和实现部分分离开来,可能会增加代码的耦合度和依赖关系。就像建造一座复杂的桥梁需要更多的协调和管理一样,使用桥接模式也需要更多的关注和维护。
  • 客户端代码不正确地使用了抽象部分提供的接口,可能会导致系统出现错误或异常行为。这是因为抽象部分需要负责定义和封装接口,如果客户端代码没有正确地调用这些接口,就会导致系统的错误和异常。就像使用错误的桥梁路径会导致交通拥堵一样,客户端代码的错误使用也会给系统带来问题。

综上所述,桥接模式是一种灵活的设计模式,可以帮助我们更好地组织和管理代码。然而,在使用该模式时也需要注意一些潜在的问题和挑战。就像建造一座桥梁需要考虑许多因素一样,使用桥接模式也需要综合考虑各种因素,以确保系统的正确性和可靠性。

4. 代码实现

4.1 真实案例伪代码描述

// “抽象部分”定义了两个类层次结构中“控制”部分的接口。它管理着一个指向“实
// 现部分”层次结构中对象的引用,并会将所有真实工作委派给该对象。
class RemoteControl isprotected field device: Deviceconstructor RemoteControl(device: Device) isthis.device = devicemethod togglePower() isif (device.isEnabled()) thendevice.disable()elsedevice.enable()method volumeDown() isdevice.setVolume(device.getVolume() - 10)method volumeUp() isdevice.setVolume(device.getVolume() + 10)method channelDown() isdevice.setChannel(device.getChannel() - 1)method channelUp() isdevice.setChannel(device.getChannel() + 1)// 你可以独立于设备类的方式从抽象层中扩展类。
class AdvancedRemoteControl extends RemoteControl ismethod mute() isdevice.setVolume(0)// “实现部分”接口声明了在所有具体实现类中通用的方法。它不需要与抽象接口相
// 匹配。实际上,这两个接口可以完全不一样。通常实现接口只提供原语操作,而
// 抽象接口则会基于这些操作定义较高层次的操作。
interface Device ismethod isEnabled()method enable()method disable()method getVolume()method setVolume(percent)method getChannel()method setChannel(channel)// 所有设备都遵循相同的接口。
class Tv implements Device is// ……class Radio implements Device is// ……// 客户端代码中的某个位置。
tv = new Tv()
remote = new RemoteControl(tv)
remote.togglePower()radio = new Radio()
remote = new AdvancedRemoteControl(radio)

4.2 概念示例代码

#include <iostream>
#include <string>/*** The Implementation defines the interface for all implementation classes. It* doesn't have to match the Abstraction's interface. In fact, the two* interfaces can be entirely different. Typically the Implementation interface* provides only primitive operations, while the Abstraction defines higher-* level operations based on those primitives.*/
class Implementation
{
public:virtual ~Implementation() {}virtual std::string OperationImplementation() const = 0;
};/*** Each Concrete Implementation corresponds to a specific platform and* implements the Implementation interface using that platform's API.*/
class ConcreteImplementationA : public Implementation
{
public:std::string OperationImplementation() const override{return "ConcreteImplementationA: Here's the result on the platform A.\n";}
};
class ConcreteImplementationB : public Implementation
{
public:std::string OperationImplementation() const override{return "ConcreteImplementationB: Here's the result on the platform B.\n";}
};/*** The Abstraction defines the interface for the "control" part of the two class* hierarchies. It maintains a reference to an object of the Implementation* hierarchy and delegates all of the real work to this object.*/
class Abstraction
{/*** @var Implementation*/
protected:Implementation *implementation_;public:Abstraction(Implementation *implementation) : implementation_(implementation){}virtual ~Abstraction(){}virtual std::string Operation() const{return "Abstraction: Base operation with:\n" +this->implementation_->OperationImplementation();}
};/*** You can extend the Abstraction without changing the Implementation classes.*/
class ExtendedAbstraction : public Abstraction
{
public:ExtendedAbstraction(Implementation *implementation) : Abstraction(implementation){}std::string Operation() const override{return "ExtendedAbstraction: Extended operation with:\n" +this->implementation_->OperationImplementation();}
};/*** Except for the initialization phase, where an Abstraction object gets linked* with a specific Implementation object, the client code should only depend on* the Abstraction class. This way the client code can support any abstraction-* implementation combination.*/
void ClientCode(const Abstraction &abstraction)
{// ...std::cout << abstraction.Operation();// ...
}/*** The client code should be able to work with any pre-configured abstraction-* implementation combination.*/
void BridgeExample()
{Implementation *implementation = new ConcreteImplementationA;Abstraction *abstraction = new Abstraction(implementation);ClientCode(*abstraction);std::cout << std::endl;delete implementation;delete abstraction;implementation = new ConcreteImplementationB;abstraction = new ExtendedAbstraction(implementation);ClientCode(*abstraction);delete implementation;delete abstraction;
}

4.3 真实案例代码

4.3.1 remote_exmaple.h
#ifndef _REMOTE_EXAMPLE_H_
#define _REMOTE_EXAMPLE_H_// The abstract Device class.
class Device
{
public:virtual bool isEnabled() = 0;virtual void enable() = 0;virtual void disable() = 0;virtual int getVolume() = 0;virtual void setVolume(int volume) = 0;virtual int getChannel() = 0;virtual void setChannel(int channel) = 0;protected:virtual bool checkIsValidVolume(int volume) const = 0;virtual bool checkIsValidChannel(int channel) const = 0;
};class RemoteControl
{
protected:Device* m_pDevice = nullptr;
public:RemoteControl(Device* pDevice);void togglePower();void volumeDown();void volumeUp();void channelDown();void channelUp();
};class AdvancedRemoteControl : public RemoteControl
{
public:void mute();
};class TVDevice : public Device
{
public:TVDevice(bool isEnabled = false, int volume = 1, int channel = 0);bool isEnabled() override;void enable() override;void disable() override;int getVolume() override;void setVolume(int volume) override;int getChannel() override;void setChannel(int channel) override;void printDeviceInfo();private:bool checkIsValidVolume(int volume) const override;bool checkIsValidChannel(int channel) const override;private:bool m_isEnabled = false;int m_volume = 1;int m_channel = 0;
};class RadioDevice : public Device
{
public:RadioDevice(bool isEnabled = false, int volume = 1, int channel = 0);bool isEnabled() override;void enable() override;void disable() override;int getVolume() override;void setVolume(int volume) override;int getChannel() override;void setChannel(int channel) override;void printDeviceInfo();private:bool checkIsValidVolume(int volume) const override;bool checkIsValidChannel(int channel) const override;private:bool m_isEnabled = false;int m_volume = 1;int m_channel = 0;
};class RemoteExample
{
public:static void TVControlCase();static void RadioControlCase();
};#endif // _REMOTE_EXAMPLE_H_
4.3.2 remote_example.cpp
#include "remote_example.h"#include <iostream>namespace
{constexpr int TV_VOLUME_MAX = 100;constexpr int TV_CHANNEL_MAX = 256;constexpr int RADIO_VOLUME_MAX = 200;constexpr int RADIO_CHANNEL_MAX = 512;
}RemoteControl::RemoteControl(Device* pDevice): m_pDevice(pDevice)
{
}void RemoteControl::togglePower()
{if (m_pDevice->isEnabled()) {m_pDevice->disable();} else {m_pDevice->enable();}
}void RemoteControl::volumeDown()
{m_pDevice->setVolume(m_pDevice->getVolume() - 10);
}void RemoteControl::volumeUp()
{m_pDevice->setVolume(m_pDevice->getVolume() + 10);
}void RemoteControl::channelDown()
{m_pDevice->setChannel(m_pDevice->getChannel() - 1);
}void RemoteControl::channelUp()
{m_pDevice->setChannel(m_pDevice->getChannel() + 1);
}void AdvancedRemoteControl::mute()
{m_pDevice->setVolume(0);
}TVDevice::TVDevice(bool isEnabled /*= true*/, int volume /*= 1*/, int channel /*= 0*/): m_isEnabled(isEnabled), m_volume(volume), m_channel(channel)
{
}bool TVDevice::isEnabled()
{return m_isEnabled;
}void TVDevice::enable()
{m_isEnabled = true;
}void TVDevice::disable()
{m_isEnabled = false;
}int TVDevice::getVolume()
{return m_volume;
}void TVDevice::setVolume(int volume)
{if (checkIsValidVolume(volume)){m_volume = volume;}else{std::cout << "Error volume for this TV Device!" << std::endl;}
}int TVDevice::getChannel()
{return m_channel;
}void TVDevice::setChannel(int channel)
{if (checkIsValidChannel(channel)){m_channel = channel;}else{std::cout << "Error channel for this TV Device!" << std::endl;}
}void TVDevice::printDeviceInfo()
{std::cout << "TVDevice Info : Volume :"<< m_volume << '\t' << "Channel : " << m_channel << std::endl;
}bool TVDevice::checkIsValidVolume(int volume) const
{return (volume >= 0 && volume <= TV_VOLUME_MAX) ? true : false;
}bool TVDevice::checkIsValidChannel(int channel) const
{return (channel >= 0 && channel <= TV_CHANNEL_MAX) ? true : false;
}RadioDevice::RadioDevice(bool isEnabled /*= true*/, int volume /*= 1*/, int channel /*= 0*/): m_isEnabled(isEnabled), m_volume(volume), m_channel(channel)
{
}bool RadioDevice::isEnabled()
{return m_isEnabled;
}void RadioDevice::enable()
{m_isEnabled = true;
}void RadioDevice::disable()
{m_isEnabled = false;
}int RadioDevice::getVolume()
{return m_volume;
}void RadioDevice::setVolume(int volume)
{if (checkIsValidVolume(volume)){m_volume = volume;}else{std::cout << "Error volume for this TV Device!" << std::endl;}
}int RadioDevice::getChannel()
{return m_channel;
}void RadioDevice::setChannel(int channel)
{if (checkIsValidChannel(channel)){m_channel = channel;}else{std::cout << "Error channel for this TV Device!" << std::endl;}
}bool RadioDevice::checkIsValidVolume(int volume) const
{return (volume >= 0 && volume <= RADIO_VOLUME_MAX) ? true : false;
}bool RadioDevice::checkIsValidChannel(int channel) const
{return (channel >= 0 && channel <= RADIO_CHANNEL_MAX) ? true : false;
}void RadioDevice::printDeviceInfo()
{std::cout << "RadioDevice Info : Volume :"<< m_volume << '\t' << "Channel : " << m_channel << std::endl;
}void RemoteExample::TVControlCase()
{std::cout << "\nTV Device example case start." << std::endl;TVDevice tv;RemoteControl remoteTv(&tv);remoteTv.togglePower();tv.printDeviceInfo();remoteTv.channelUp();std::cout << "After TV channel up:" << std::endl;tv.printDeviceInfo();remoteTv.volumeUp();std::cout << "After TV volume Up:" << std::endl;tv.printDeviceInfo();AdvancedRemoteControl adRemote(&tv);adRemote.mute();std::cout << "After TV is muted:" << std::endl;tv.printDeviceInfo();std::cout << "\nTV Device example case end." << std::endl;
}void RemoteExample::RadioControlCase()
{std::cout << "\nRadio Device example case start." << std::endl;RadioDevice radio;RemoteControl remoteRadio(&radio);remoteRadio.togglePower();radio.printDeviceInfo();remoteRadio.channelUp();std::cout << "After Radio channel up:" << std::endl;radio.printDeviceInfo();remoteRadio.volumeUp();std::cout << "After Radio volume Up:" << std::endl;radio.printDeviceInfo();AdvancedRemoteControl adRemote(&radio);adRemote.mute();std::cout << "After Radio is muted:" << std::endl;radio.printDeviceInfo();std::cout << "\nRadio Device example case end." << std::endl;
}

4.4 代码分析

4.4.1 概念代码分析

相关代码由以下类组成:

  1. Implementation:这是一个抽象基类,定义了所有实现类的接口。它有一个纯虚函数 OperationImplementation()

  2. ConcreteImplementationAConcreteImplementationB:这两个类是 Implementation 的具体实现,分别对应平台A和平台B。它们实现了 OperationImplementation() 方法,返回特定平台的结果。

  3. Abstraction:这个类定义了控制部分的接口,并维护了一个对 Implementation 类的对象的引用。它有一个虚函数 Operation(),该函数通过委托给 implementation_ 对象来执行实际工作。

  4. ExtendedAbstraction:这是 Abstraction 的子类,扩展了其功能。它在 Operation() 中添加了一些额外的操作。

  5. ClientCodeBridgeExample:这两个函数/方法用于客户端代码,与抽象和实现类进行交互。

这些类的角色如下:

  • Implementation:定义了所有实现类的接口。
  • ConcreteImplementationAConcreteImplementationB:实现了 Implementation 接口,提供了特定平台的实现。
  • Abstraction:作为控制部分的接口,封装了对 Implementation 对象的引用,并提供了基本的操作。
  • ExtendedAbstraction:扩展了 Abstraction 的功能。
  • ClientCodeBridgeExample:客户端代码,与抽象和实现类进行交互。

模式中的各个元素以以下方式相互关联:

  • Abstraction 类包含一个指向 Implementation 类的指针,并通过该指针调用 OperationImplementation() 方法。
  • ExtendedAbstraction 类继承自 Abstraction 类,并重写了 Operation() 方法,增加了额外的操作。
  • ClientCode 函数接受一个 Abstraction 类的引用,并通过该引用调用 Operation() 方法。
  • BridgeExample 函数演示了如何使用不同的 AbstractionImplementation 组合创建对象,并通过 ClientCode 函数调用相应的操作。
4.4.2 真实案例代码分析

相关代码由以下类组成:

  1. Device:这是一个抽象类,定义了一些设备共有的属性和方法,如检查设备是否启用、获取和设置音量和频道等。

  2. RemoteControl:这是一个基类,它有一个指向Device类的指针,并定义了一些远程控制设备的基本操作,如切换电源、调整音量和频道等。

  3. AdvancedRemoteControl:这是RemoteControl的子类,增加了静音功能。

  4. TVDeviceRadioDevice:这两个类都继承自Device类,分别代表电视设备和无线电设备。它们都实现了Device类中的虚函数,并添加了一些特定于设备的属性和方法,如打印设备信息等。

  5. RemoteExample:这是一个包含两个静态方法的类,用于在其他地方调用以执行特定的远程控制案例。

这些类的角色如下:

  • Device:作为所有设备的基类,定义了通用的设备属性和方法。
  • RemoteControl:作为远程控制的基类,提供了一些设备的基本操作。
  • AdvancedRemoteControl:扩展了远程控制的功能,包括静音。
  • TVDeviceRadioDevice:这两个类代表了具体的设备类型,它们继承了Device类,并实现了Device类中的方法,添加了特定于设备的属性和方法。
  • RemoteExample:包含了两个静态方法,用于在其他地方执行特定的远程控制案例。

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

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

相关文章

bochs 对 Linux0.11 进行调试 (TODO: 后面可以考虑集成 vscode+gdb+qemu)

我在阅读 Linux0.11 源码时&#xff0c;对一个指令 LDS 感到困惑。 看了下 intel 指令集手册&#xff0c;能猜到 LDS 的功能&#xff0c;但不确定。 于是决定搭建调试环境&#xff0c;看看 LDS 的功能是否真如自己猜测。 首先 make debug 运行 qemu-Linux0.11&#xff0c;命…

基于Java+SpringBoot+Vue在线家具商城系统的设计与实现 前后端分离【Java毕业设计·文档报告·代码讲解·安装调试】

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

Ubuntu20.04 配置 yolov5_ros 功能包记录

文章目录 本文参考自博主源801,结合自己踩坑后修改 项目地址:https://github.com/mats-robotics/yolov5_ros 1.新建工作空间 新建一个工作空间 yolo_ros(名字可自定义),在 yolo_ros 下新建文件夹 src 并catkin_make进行编译 2. 安装相机驱动,可以选用较为主流的 usb_cam 或…

.net中用标志位解决socket粘包问题

以下为wpf中, 用标志位"q" 解决粘包问题 using MyFrameWorkWpf.Entities; using System.Collections.ObjectModel; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.…

代码随想录算法训练营第四十八天| LeetCode 70 爬楼梯、LeetCode 322 零钱兑换、LeetCode 279 完全平方数

1 LeetCode 70 爬楼梯 题目链接&#xff1a;LeetCode 70 爬楼梯 文章讲解&#xff1a;代码随想录(programmercarl.com) 2 LeetCode 322 零钱兑换 题目链接&#xff1a;LeetCode 322 零钱兑换 文章讲解&#xff1a;代码随想录(programmercarl.com) 视频讲解&#xff1a;装满背包…

Oracle关联机制

目录 1. 关联机制 1.1 循环嵌套关联 ( NESTED LOOPS JOIN) :大小表关联 1.2 哈希关联 (HASH JOIN )&#xff1a;两张大表 1.3 排序合并关联 (SORT MERGE JOIN)&#xff1a;有索引的两张表. 2. HINTS (提示符) 优化 --高水位线 1. 关联机制 1.1 循环嵌套关联 ( NESTED LO…

pytorch 如何训练一个模型

定义网络结构&#xff1a; 确定深度学习网络的架构&#xff0c;包括卷积层、池化层、全连接层等组件的设计。 准备数据集&#xff1a; 使用 DataLoader 从数据集中读取数据&#xff0c;也可使用现有的数据集。 定义损失函数和优化器&#xff1a; 选择合适的损失函数来衡量模…

python经典百题之根据值打印*

题目:读取7个数&#xff08;1—50&#xff09;的整数值&#xff0c;每读取一个值&#xff0c;程序打印出该值个数的 &#xff0a;。 程序分析 我们需要读取7个整数值&#xff0c;每个值在范围1到50之间&#xff0c;然后根据每个值的大小打印相应数量的星号&#xff08;*&…

ios safari 浏览器跳转页面没有自适应

今天开发遇到了一个问题&#xff0c;当用户点击浏览器中的表单进行注册时&#xff0c;表单元素会放大&#xff0c;随后跳转页面无法还原到初始状态。 这是因为如果 的 font-size 被设定为 16px 或更大&#xff0c;那么 iOS 上的 Safari 将正常聚焦到输入表单中。但是&#xff…

2023版 STM32实战8 独立看门狗(IWDG)

IWDG简介 STM32F10xxx内置两个看门狗&#xff0c;提供了更高的安全性、时间的精确性和使用的灵活性。两个看门狗设备(独立看门狗和窗口看门狗)可用来检测和解决由软件错误引起的故障。 说人话就是能解决程序跑飞的问题。 编写代码思路 -1- 使用这个功能必须解除写保护 -2-…

FLIP动画做拖拽排序效果

先来看效果 index.html文件 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" /><meta name"viewport" content&quo…

H - 提瓦特之旅 2022CCPC女生赛

H - 提瓦特之旅 原题链接&#xff1a; https://vjudge.net/contest/532518#problem/H 题意&#xff1a; 一个有n个点&#xff0c;m条边的无向图&#xff0c;从u点到v点花费的时间和从v到u花费的时间都是C&#xff08;u&#xff0c;v&#xff09;&#xff0c;并且当经过路上的…

Netty深入浅出(无处不在的IO)

为什么要有Netty Netty是为了解决网络编程的复杂性和提供易于使用、高性能和可扩展的框架而开发的。它通过提供一组可重用的组件来处理网络通信的低级细节&#xff0c;例如套接字管理、线程和缓冲&#xff0c;简化了开发网络应用程序的过程。这使开发人员可以专注于应用程序逻…

自然语言处理 | WordNet

WordNet是词汇数据库,即英语词典,专为自然语言处理而设计。 Synset是一种特殊的简单接口,存在于 NLTK 中, 用于在 WordNet 中查找单词。同义词集实例是表达相同概念的同义词的分组。有些单词只有一个同义词集,有些则有多个。

C#对字典容器Dictionary<TKey, TValue>内容进行XML序列化或反序列化报错解决方法

一、问题描述 在使用C#对字典容器Dictionary<TKey, TValue>内容进行XML序列化报错【System.Exception:“不支持类型 System.Collections.Generic.Dictionary2[[System.String, mscorlib, Version2.0.0.0, Cultureneutral, PublicKeyTokenb77a5c561934e089],[System.Strin…

基于matlab统计Excel文件一列数据中每个数字出现的频次和频率

一、需求描述 如上表所示&#xff0c;在excel文件中&#xff0c;有一列数&#xff0c;统计出该列数中&#xff0c;每个数出现的次数和频率。最后&#xff0c;将统计结果输出到新的excel文件中。 二、程序讲解 第一步&#xff1a;选择excel文件&#xff1b; [Filename, Pathn…

C++笔记之信号量、互斥量与PV操作

C笔记之信号量、互斥量与PV操作 文章目录 C笔记之信号量、互斥量与PV操作1.信号量概念2.信号量例程一3.信号量例程二4.信号量例程三5.互斥量6.PV操作概念7.PV操作详解——抄自&#xff1a;https://mp.weixin.qq.com/s/vvjhbzsWQNRkU7-b_dURlQ8.PV操作的英文全称 1.信号量概念 …

HTTPS建立连接的过程

HTTPS 协议是基于 TCP 协议的&#xff0c;因而要先建立 TCP 的连接。在这个例子中&#xff0c;TCP 的连接是在手机上的 App 和负载均衡器 SLB 之间的。 尽管中间要经过很多的路由器和交换机&#xff0c;但是 TCP 的连接是端到端的。TCP 这一层和更上层的 HTTPS 无法看到中间的包…

拼多多API接口的使用方针如下:

了解拼多多API接口 拼多多API接口是拼多多网提供的一种应用程序接口&#xff0c;允许开发者通过程序访问拼多多网站的数据和功能。通过拼多多API接口&#xff0c;开发者可以开发各种应用程序&#xff0c;如店铺管理工具、数据分析工具、购物比价工具等。在本章中&#xff0c;我…

Hive面试常见基础问题

以下是一些Hive面试问题和答案&#xff1a; Hive是什么&#xff1f; 答&#xff1a;Hive是一个开源的数据仓库工具&#xff0c;用于处理和分析大规模结构化数据。它能够创建、修改和查询表结构&#xff0c;支持多种数据类型和查询操作&#xff0c;同时提供数据汇总和数据查询的…