C++设计模式笔记

news2024/11/16 16:36:59

设计模式

如何解决复杂性?

分解

核心思想:分而治之,将大问题分解为多个小问题,将复杂问题分解为多个简单的问题。

抽象

核心思想:从高层次角度讲,人们处理复杂性有一个通用的技术,及抽象。由于不能掌握全部的复杂对象,选择护士他的非本质细节,而去处理泛化和理想化的对象模型。

总结来说就是,复用最好的代码就是复用性高。

面向对象设计原则

对象的概念

  • 从语言实现层面来看,对象封装了代码和数据。
  • 从规格层面讲,对象是一系列可被使用的公共接口。
  • 从概念层面讲,对象时某种拥有责任的抽象。

设计原则

依赖倒置原则(DIP):

  • 高层模块(稳定)不应该依赖于低层模块(变化)二者都应该依赖于抽象(稳定)。
  • 抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)。

开放封闭原则(OCP):

  • 对扩展开放,对更改封闭。
  • 类模块应该是可扩展的,但是不可修改。

单一职责原则(SRP):

  • 一个类应该仅有一个引起它变化的原则。
  • 变化的方向隐含着类的责任。

Liskov替换原则(LSP):

  • 子类必须能够替换他们的基类。
  • 继承表达类型抽象

接口隔离原则(ISP):

  • 不应该强迫客户程序依赖他们不用的方法。
  • 接口应该小而完备。

优先使用对象组合,而不是类继承

  • 类继承通常为“白箱复用”,对象组合通常为“黑箱复用”。
  • 继承在某种程度上破坏了封装性,子类父类耦合度很高。
  • 而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。

封装变化点

  • 使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良影响,从而实现层次间的松耦合。

针对接口与编码,而不是针对实现编程产业强盛的标志

  • 不将变量类型声明为某个特定的具体类,而是声明为某个接口
  • 客户程序无需获知对象的具优类型,只需要知道对象所具有的接口。
  • 减少系统中各部分的依赖关系从而实现“高内聚、松耦合’的类型设计方案。

GOF-23模式分类

目的来看:

  • 创建型模式:将对象的部分创建工作延迟到子类或者其他对象,从而应对需求变化为对象创建时具体类型实现引来的冲击。
  • 结构型模式:通过类继承或者对象组合获得更灵活的结构,从而应对需求变化为对象的结构带来的冲击。
  • 行为型模式:通过类继承或者对象组合来划分类与对象间的职责,从而应对需求变化为多个交互的对象带来的冲击。

范围来看:

  • 类模式处理类与子类的静态关系。
  • 对象模式处理对象间的动态关系。

C++设计模式

工厂模式

工厂模式:主要是封装了对象的创建。

简单工厂(Simple Factory)

优点:

  1. 把对象的创建封装在一个接口函数里面,通过传入不同的标识,返回创建的对象。
  2. 使用者不需要自己负责new对象,不用了解对象创建的详细过程。

缺点:

  1. 提供创建对象实例的接口函数不闭合,不能对修改关闭。

工厂方法(Factory Method)

优点:

  1. 使用Factory基类,提供一个纯虚函数(创建产品),定义派生类(具体产品的工厂)负责创建对应的产品,可以做到不同的产品在不同的工厂进行创建。能对现有工厂以及产品的修改关闭

缺点:

  1. 忽略了产品之间的关联关系,属于一个产品簇的不应该放到不同的工厂里面去创建。不符合产品对象创建逻辑;
  2. 工厂类太多,难以维护。

抽象工厂(Abstact Factory)

优点:

  1. 把现有关联关系、属于同一个产品簇的所有产品创建的接口函数放在一个抽象工厂里面,派生类(具体产品的工厂)应该负责创建该产品簇里面的所有产品。

这里使用了一个汽车生产的工厂示例,列举了简单工厂和工厂方法,并使用智能指针进行对象空间优化回收。

简单工厂和工厂方法

#include <iostream>
#include <string>
#include<memory>

using namespace std;

class Car
{
public:
    Car(string name):_name(name){}
    virtual void show() = 0; //定义纯虚函数,子类必须实现

protected:
    string _name;

private:
};
class BMW:public Car
{
public:
    // 使用Car(name)类似于_name(name),还是用了Car的_name,
    // 子类继承父类的构造函数可以参考 https://blog.csdn.net/aquapisces/article/details/104371658
    BMW(string name) : Car(name){};
    void show()
    {
        cout << "获取了一辆宝马汽车" << _name << endl;
    } 
protected : 
private:
};
class Audi : public Car
{
public:
    // 使用Car(name)类似于_name(name),还是用了Car的_name,
    // 子类继承父类的构造函数可以参考 https://blog.csdn.net/aquapisces/article/details/104371658
    Audi(string name) : Car(name){};
    void show()
    {
        cout << "获取了一辆奥迪汽车" << _name << endl;
    }

protected:
private:
};

// ------------------------------------------------------------------------------------------
/*
简单工厂存在以下问题:开闭原则,新增一个汽车则需要修改很多代码;BMW类和Audi类都在一个工厂里面生产
*/
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:
            cout << "传入工厂的参数不正确:" << ct << endl;
            break;
        }
        return nullptr;
    }
};
// ------------------------------------------------------------------------------------------
/*
工厂方法
*/
class Factory
{
public:
    virtual Car* createCar(string name) = 0;
};
class BMWFactory:public Factory
{
public:
    Car* createCar(string name)
    {
        return new BMW(name);
    }
};
class AudiFactory : public Factory
{
public:
    Car *createCar(string name)
    {
        return new Audi(name);
    }
};
// ------------------------------------------------------------------------------------------

int main()
{
    // unique_ptr无法进行普通拷贝构造和赋值unique_ptr<Car> p1=new BMW("X1");是错误的
    // 最原始的对象产生
    unique_ptr<Car> p1(new BMW("X1"));
    unique_ptr<Car> p2(new Audi("A6"));

    // 简单工厂
    // unique_ptr<SimpleFactory> factory(new SimpleFactory());
    // unique_ptr<Car> p1(factory->createCar(bmw));
    // unique_ptr<Car> p2(factory->createCar(audi));

    // 工厂方法
    // unique_ptr<Factory> bmwFactory(new BMWFactory);
    // unique_ptr<Factory> audiFactory(new AudiFactory);
    // unique_ptr<Car> p1(bmwFactory->createCar("X6"));
    // unique_ptr<Car> p2(audiFactory->createCar("A8"));

    p1->show();
    p2->show();

    return 0;
}

抽象工厂方法

上一节中的工厂方法会存在一个问题,在定义了一个Factory抽象类后,每一个类将对应一个类的工厂,如BWM->BMWFactoryAudi->AudiFactory,这样会导致一个问题,在生产一类产品时,如手机充电器和充电线,那么就又需要两个工厂来实现,所以这样就会导致工厂冗余。所以,提出了抽象工厂来解决这个问题,这里以汽车的生产部件为例进行说明:

#include <iostream>
#include <string>
#include<memory>

using namespace std;

class Car
{
public:
    Car(string name):_name(name){}
    virtual void show() = 0; //定义纯虚函数,子类必须实现

protected:
    string _name;

private:
};
class BMW:public Car
{
public:
    // 使用Car(name)类似于_name(name),还是用了Car的_name,
    // 子类继承父类的构造函数可以参考 https://blog.csdn.net/aquapisces/article/details/104371658
    BMW(string name) : Car(name){};
    void show()
    {
        cout << "获取了一辆宝马汽车" << _name << endl;
    } 
protected : 
private:
};
class Audi : public Car
{
public:
    // 使用Car(name)类似于_name(name),还是用了Car的_name,
    // 子类继承父类的构造函数可以参考 https://blog.csdn.net/aquapisces/article/details/104371658
    Audi(string name) : Car(name){};
    void show()
    {
        cout << "获取了一辆奥迪汽车" << _name << endl;
    }

protected:
private:
};

class Light
{
public:
    virtual void show() = 0;
};

class BMWLight:public Light
{
public:
    void show()
    {
        cout << "BMW light!" << endl;
    }
};

class AudiLight : public Light
{
public:
    void show()
    {
        cout << "Audi light!" << endl;
    }
};
/*
抽象工厂,对一组关联关系的产品簇提供产品对象的统一创建。
有点类似于工厂方法
*/
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 AudiLight();
    }
};

int main()
{

    // 抽象工厂方法
    unique_ptr<AbstractFactory> bmwFactory(new BMWFactory);
    unique_ptr<AbstractFactory> audiFactory(new AudiFactory);
    unique_ptr<Car> p1(bmwFactory->createCar("X6"));
    unique_ptr<Car> p2(audiFactory->createCar("A8"));
    unique_ptr<Light> l1(bmwFactory->createCarLight());
    unique_ptr<Light> l2(audiFactory->createCarLight());

    p1->show();
    p2->show();
    l1->show();
    l2->show();

    return 0;
}

代理模式

代理(proxy)模式:通过代理类,来控制实际对象的访问权限。其实也就是定义一个虚基类,子类继承虚基类实现不同级别对象的实现,可以看以下类似代码:

#include <iostream>
#include <string>
#include<memory>

using namespace std;

class VideoSite
{
public:
    virtual void freeMovie() = 0;
    virtual void vipMovie() = 0;
    virtual void ticketMovie() = 0;
};
class FixBugVideoSite:public VideoSite
{
    // 不同的用户的访问权限可能不够,所以下面三个函数有些对象不能调用
    void freeMovie()
    {
        cout << "观看免费电影" << endl;
    }
    void vipMovie()
    {
        cout << "观看vip电影" << endl;
    }
    void ticketMovie()
    {
        cout << "观看电影券电影" << endl;
    }
};

// 代理类,用于代理FixBugVideoSite
class FreeVideoSiteProxy:public VideoSite
{
public:
    FreeVideoSiteProxy()
    {
        pVideo = new FixBugVideoSite();
    }
    ~FreeVideoSiteProxy()
    {
        if(pVideo!=nullptr)
        {
            delete pVideo;
        }
        pVideo = nullptr;
    }
    void freeMovie()
    {
        pVideo->freeMovie();
    }
    void vipMovie()
    {
        cout << "您目前只是普通用户,需要升级成vip才能观看vip电影" << endl;
    }
    void ticketMovie()
    {
        cout << "您目前只是普通用户,需要购买电影券才能观看电影" << endl;
    }

private:
    VideoSite *pVideo;
};
class VipVideoSiteProxy : public VideoSite
{
public:
    VipVideoSiteProxy()
    {
        pVideo = new FixBugVideoSite();
    }
    ~VipVideoSiteProxy()
    {
        if (pVideo != nullptr)
        {
            delete pVideo;
        }
        pVideo = nullptr;
    }
    void freeMovie()
    {
        pVideo->freeMovie();
    }
    void vipMovie()
    {
        pVideo->vipMovie();
    }
    void ticketMovie()
    {
        cout << "您目前只是普通用户,需要购买电影券才能观看电影" << endl;
    }

private:
    VideoSite *pVideo;
};
int main()
{
    // unique_ptr<VideoSite> p1(new FreeVideoSiteProxy());
    unique_ptr<VideoSite> p1(new VipVideoSiteProxy());
    p1->freeMovie();
    p1->vipMovie();
    p1->ticketMovie();

    return 0;
}

观察者模式

行为型模式:主要关注的是对象之间的通信。

以观察者-监听者模式(发布-订阅模式)设计模式:主要关注的是对象的一对多的关系,也就是多个对象都依赖一个对象,当该对象的状态发生改变时,其他对象都能够结束到相应的通知。

ps:一组数据改变(数据对象)->通过这一组数据->曲线图(对象1)/柱状图(对象2)/圆饼图(对象3)。当数据对象改变时,对象1、对象2、对象3应该及时的收到相应的通知。

#include <iostream>
#include <string>
#include<list>
#include<unordered_map>

using namespace std;
/**
观察者模式/订阅模式:当一个对象发生改变时,会通知其他对象去处理对象的事件。
*/
class Observer
{
public:
    virtual void handle(int msgid) = 0;
};
class Observer1:public Observer
{
    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 unknow msg." << endl;
            break;
        }
    }
};
class Observer2 : public Observer
{
    void handle(int msgid)
    {
        switch (msgid)
        {
        case 3:
            cout << "observer2 recv 3 msg." << endl;
            break;
        case 2:
            cout << "observer2 recv 2 msg." << endl;
            break;
        default:
            cout << "observer2 recv unknow msg." << endl;
            break;
        }
    }
};
class Observer3 : public Observer
{
    void handle(int msgid)
    {
        switch (msgid)
        {
        case 1:
            cout << "observer3 recv 1 msg." << endl;
            break;
        case 3:
            cout << "observer3 recv 3 msg." << endl;
            break;
        default:
            cout << "observer3 recv unknow msg." << endl;
            break;
        }
    }
};
class Subjects
{
public:
    // 给主题添加观察者对象
    void addObserver(Observer* obser,int msgid)
    {
        // 和下面的操作一致,如果存在msgid为key的情况就直接插入,没有就创建一个并插入
        _submap[msgid].push_back(obser);
        /**
        // 查找map中是否存在有key为msgid的值
        auto it = _submap.find(msgid);
        if(it != _submap.end())
        {
            it->second.push_back(obser);
        }
        else
        {
            list<Observer *> olist;
            olist.push_back(obser);
            _submap.insert(make_pair(msgid, olist));
        }
        */
    }
    // 主题检测发生改变,通知相应的观察者对象处理事件
    void dispatch(int msg)
    {
        auto it = _submap.find(msg);
        if (it != _submap.end())
        {
            for(Observer* pObser:it->second)
            {
                pObser->handle(msg);
            }
        }
    }
private:
    unordered_map<int, list<Observer *>> _submap;
};
int main()
{
    Subjects sub;
    Observer *p1 = new Observer1();
    Observer *p2 = new Observer2();
    Observer *p3 = new Observer3();

    sub.addObserver(p1, 1);
    sub.addObserver(p2, 3);
    sub.addObserver(p3, 1);
    sub.addObserver(p3, 2);
    sub.addObserver(p1, 3);

    sub.dispatch(2);
    
    return 0;
}

适配器模式

适配器模式:让不兼容的接口可以在一起工作。

下方使用C++实现了一个电脑播放投影视频的例子,电脑仅仅支持VGA接口,如何实现HDMI接口转化成VGA来适配电脑使用。也就是如何实现一个VGA和HEMI类的转化接口。

#include <iostream>
#include <string>

using namespace std;

class VGA
{
public:
    virtual void play() = 0;
};
class TV01:public VGA
{
public:
    void play() { cout << "通过VGA接口连接投影仪,进行视频播放!" << endl; }
};

class HDMI
{
public:
    virtual void play() = 0;
};
class TV02 : public HDMI
{
public:
    void play() { cout << "通过HDMI接口连接投影仪,进行视频播放!" << endl; }
};

// 一定要继承原来的类
class VGA2HDMIAdapter:public VGA
{
public:
    VGA2HDMIAdapter(HDMI* p):phdmi(p){}
    void play() { phdmi->play(); }

private:
    HDMI *phdmi;
};
class Computer
{
public:
    // 由于电脑只支持VGA接口,所以该方法的参数也只支持VGA接口的指针/引用
    void playVideo(VGA *pVGA) { pVGA->play(); }
};

int main()
{
    // HDMI和VGA无法进行互相转化,电脑如何使用HDMI的play方法?
    /*
    1、换一个支持HDMI的电脑,也就是对Computer类进行重构,叫做代码重构。
    2、使用一个适配器(转化头),把VGA类转化为HDMI类,叫做添加适配器类。
    */

    Computer computer;
    computer.playVideo(new TV01());
    computer.playVideo(new VGA2HDMIAdapter(new TV02()));

    return 0;
}

装饰器模式

装饰器(decorate)模式和代理模式类似。代理模式也就是定义委托类和代理类继承抽象类,并重写函数方法,通过在代理类中使用委托类的对象调用不同级别代理的方法访问。

装饰器主要是增加现有类的功能,但是添加现有类的一个方法还有一个方法,就是新建一个子类。如果每次新建一个功能都需要新建一个子类的话,那么代码中就会有很多的子类被添加进来。

以下实现了一个汽车装饰类的代码实现:

#include <iostream>
#include <string>

using namespace std;

class Car
{
public:
    virtual void show() = 0;
};
class BMW : public Car
{
public:
    void show() { cout << "这是一辆宝马汽车!有基本配置"; }
};
class Audi : public Car
{
public:
    void show() { cout << "这是一辆奥迪汽车!有基本配置"; }
};
class Benc : public Car
{
public:
    void show() { cout << "这是一辆奔驰汽车!有基本配置"; }
};
// 装饰器
class CarDecorator01 : public Car
{
public:
    CarDecorator01(Car *p) : pCar(p){};
    void show()
    {
        pCar->show();
        cout << ",辅助驾驶";
    }
private: 
    Car *pCar;
};
class CarDecorator02 : public Car
{
public:
    CarDecorator02(Car *p) : pCar(p){};
    void show()
    {
        pCar->show();
        cout << ",自动校正";
    }

private:
    Car *pCar;
};
class CarDecorator03 : public Car
{
public:
    CarDecorator03(Car *p) : pCar(p){};
    void show()
    {
        pCar->show();
        cout << ",车道偏离";
    }

private:
    Car *pCar;
};
int main()
{
    Car *p1 = new CarDecorator01(new BMW());
    p1 = new CarDecorator02(p1);
    p1->show();
    cout << endl;

    Car *p2 = new CarDecorator02(new Audi());
    p2->show();
    cout << endl;

    Car *p3 = new CarDecorator03(new Benc());
    p3->show();
    cout << endl;

    return 0;
}

单例模式

常见的类创建一个对象,便会得到一个新的对象。而单例模式则有些不同。

单例模式:一个类不管创建多少次对象,永远只能得到该类型一个对象的实例。可以查看C++实现MySQL数据库连接池中的线程池获取代码,将线程池定义为静态成员方法和静态成员变量,导致其处于静态变量区,只需要初始化一次。类似的日志模块应该也可以使用单例模式。

饿汉式单例模式:还没有获取实例对象,实例对象就已经产生了。
懒汉式单例模式:唯一的实例对象,直到第一次获取它的时候才产生。

下面介绍了两种单例模式的代码设计,其中提供了两种懒汉式单例模式的示例:

#include <iostream>
#include <string>
#include<mutex>
using namespace std;

// 饿汉式单例模式,线程安全的
class HungrySingleton
{
public:
    // 因为其不能依赖对象,所以要定义成静态成员方法,获取类的唯一实例对象的方法
    static HungrySingleton* getSingleton()
    {
        return &instance;
    }
private:
    // 单例模式,不允许用户new对象,所以需要使用构造函数私有化
    HungrySingleton()
    {

    }
    static HungrySingleton instance;
    // 将拷贝构造函数和赋值操作符重载进行删除,使其不能完成对象创建,只能依赖getSingleton过去唯一的实例化对象
    HungrySingleton& operator=(const HungrySingleton &) = delete;
    HungrySingleton(const HungrySingleton &) = delete;
};
// 类内定义,类外初始化,更多static初始化查看 https://blog.csdn.net/sevenjoin/article/details/81772792
// 静态成员在数据段,在main函数之前就创建了,如果不使用的话,这个资源就浪费了
HungrySingleton HungrySingleton::instance;

mutex lazySingletonMtx;

// 懒汉式单例模式,线程安全的
class LazySingleton
{
public:
    // 因为其不能依赖对象,所以要定义成静态成员方法,获取类的唯一实例对象的方法
    // 可重入函数的概念?一个线程还没执行完,该函数不能被另一个线程执行,也就是线程互斥
    static LazySingleton *getSingleton()
    {   
        if(instance==nullptr)
        {
            lock_guard<mutex> lck(lazySingletonMtx); // 所以多线程环境下需要加锁和双重判断
            /*
            不是线程安全,下面这个instance = new LazySingleton()需要执行-:开辟内存、构造对象、instance赋值
            */
            if(instance==nullptr)
            { 
                // 双重判断,防止两个线程同时进入上层的if判断中
                instance = new LazySingleton();
            }
        }
        return instance;
    }

private:
    // 单例模式,不允许用户new对象,所以需要使用构造函数私有化
    LazySingleton()
    {
    }
    static LazySingleton*volatile instance;
    // 将拷贝构造函数和赋值操作符重载进行删除,使其不能完成对象创建,只能依赖getSingleton过去唯一的实例化对象
    LazySingleton &operator=(const LazySingleton &) = delete;
    LazySingleton(const LazySingleton &) = delete;
};
// 类内定义,类外初始化,更多static初始化查看 https://blog.csdn.net/sevenjoin/article/details/81772792
// 静态成员在数据段,在main函数之前就创建了,如果不使用的话,这个资源就浪费了
LazySingleton*volatile LazySingleton::instance=nullptr;

// 懒汉式单例模式,线程安全的
class LazySingleton2
{
public:
    // 因为其不能依赖对象,所以要定义成静态成员方法,获取类的唯一实例对象的方法
    // 可重入函数的概念?一个线程还没执行完,该函数不能被另一个线程执行,也就是线程互斥
    static LazySingleton2 *getSingleton()
    {
        // 静态对象,运行到这个函数才开始初始化
        // 函数静态局部变量的初始化,在汇编指令上已经自动添加线程互斥指令
        static LazySingleton2 instance;
        return &instance;
    }

private:
    // 单例模式,不允许用户new对象,所以需要使用构造函数私有化
    LazySingleton2()
    {
    }
    // 将拷贝构造函数和赋值操作符重载进行删除,使其不能完成对象创建,只能依赖getSingleton过去唯一的实例化对象
    LazySingleton2 &operator=(const LazySingleton2 &) = delete;
    LazySingleton2(const LazySingleton2 &) = delete;
};


int main()
{
    HungrySingleton *p1 = HungrySingleton::getSingleton();
    HungrySingleton *p2 = HungrySingleton::getSingleton();
    HungrySingleton *p3 = HungrySingleton::getSingleton();
    cout << p1 << " " << p2 << " " << p3 << endl; // 0x407030 0x407030 0x407030

    LazySingleton *p4 = LazySingleton::getSingleton();
    LazySingleton *p5 = LazySingleton::getSingleton();
    LazySingleton *p6 = LazySingleton::getSingleton();
    cout << p4 << " " << p5 << " " << p6 << endl; // 0x25d2430 0x25d2430 0x25d2430

    // HungrySingleton t = *p1; // 拷贝构造函数私有化之后就不能完成拷贝构造了

    return 0;
}

一般情况下第二种懒汉式单例模式用的比较多,也更简单,在静态成员方法中定义静态成员对象。

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

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

相关文章

现在运动耳机什么牌子的好用、最好的运动耳机推荐

对于注重身体健康的小伙伴来说&#xff0c;每周必然都少不了有规律的运动&#xff0c;而运动的时候耳边没有音乐的陪伴总是稍显枯燥无味&#xff0c;很难让人提起干劲来。有些小伙伴觉得运动的时候戴着耳机&#xff0c;稍微跳动几下耳机就开始松动&#xff0c;随时都要分心提防…

【LeetCode】124.二叉树中的最大路径和

题目 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#xff0c;且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二叉树的根节点 root &…

SQLserver 查询数据库表结构和说明简介信息

DECLARE tableName NVARCHAR(MAX ) SET tableName‘TK_Cargoowner’;–表名!!! SELECT CASE WHEN col.colorder 1 THEN obj.name ELSE ‘’ END AS 表名, col.colorder AS 序号 , col.name AS 列名 , ISNULL(ep.[value], ‘’) AS 列说明 , t.name AS 数据类型 , col.length A…

第十章:queue类

系列文章目录 文章目录 系列文章目录前言queue的介绍queue的使用成员函数使用queue 总结 前言 queue是容器适配器&#xff0c;底层封装了STL容器。 queue的介绍 queue的文档介绍 队列是一种容器适配器&#xff0c;专门用于在FIFO上下文(先进先出)中操作&#xff0c;其中从容器…

微信小程序实现日历功能、日历转换插件、calendar

文章目录 演示htmlJavaScript 演示 效果图 微信小程序实现交互 html <view wx:if"{{calendarArr.length}}"><view class"height_786 df_fdc_aic"><view class"grid_c7_104"><view class"font_weight_800 text_align…

多分类问题-Softmax Classifier分类器

概率分布&#xff1a;属于每一个类别的概率总和为0&#xff0c;且都>0&#xff0c;n组类别需要n-1个参数就能算出结果 数据预处理 loss函数 crossentropyloss()函数 CrossEntropyLoss <> LogSoftmax NLLLoss。也就是说使用CrossEntropyLoss最后一层(线性层)是不需要做…

Pytorch深度学习-----神经网络的卷积操作

系列文章目录 PyTorch深度学习——Anaconda和PyTorch安装 Pytorch深度学习-----数据模块Dataset类 Pytorch深度学习------TensorBoard的使用 Pytorch深度学习------Torchvision中Transforms的使用&#xff08;ToTensor&#xff0c;Normalize&#xff0c;Resize &#xff0c;Co…

软件外包开发测试管理工具

测试是软件工程中非常重要的一个环节&#xff0c;在上线前必须需要经过严格的测试才能确保上线后软件系统长时间运行。有大量的软件开发和测试管理工具&#xff0c;每一个工具都有自己的特点&#xff0c;今天和大家分享一些常见的工具&#xff0c;希望对大家有所帮助。北京木奇…

STM32 LWIP UDP 一对一 一对多发送

STM32 LWIP UDP通信 前言设置 IP 地址UDP函数配置实验结果单播发送&#xff0c;一对一发送广播发送&#xff0c;一对多发送 可能遇到的问题总结 前言 之前没有接触过网络的通信&#xff0c;工作需要 UDP 接收和发送通信&#xff0c;在网上没有找到一对一、一对多的相关例程&am…

Visual C++中的虚函数和纯虚函数(以外观设计模式为例)

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天来说说Visual C中的虚函数和纯虚函数。该系列帖子全部使用我本人自创的对比学习法。也就是当C学不下去的时候&#xff0c;就用JAVA实现同样的代码&#xff0c;然后再用对比的方法把C学会。 直接说虚函数…

Redis学习---大数据技术之Redis(NoSQL简介、Redis简介、Redis安装、五大数据类型、相关配置、持久化)

星光下的赶路人star的个人主页 毅力是永久的享受 文章目录 1、NoSQL1.1 NoSQL数据库1.1.1 NoSQL是什么1.1.2 NoSQL的特点1.1.3 NoSQL的适用场景1.1.4 NoSQL的不适场景 1.2 NoSQL家族 2、Redis简介2.1 Redis官网2.2 Redis是什么2.3 Redis的应用场景2.3.1 配合关系型数据库做高速…

java复盘

这一题中外部类是Outer。外部类不能直接使用内部类的成员变量和方法&#xff0c;需要内部类的对象来访问他的成员变量和方法。所以该题只是new了一个外部类&#xff0c;内部类无法访问&#xff0c;所以没有输出。 wait后进入等待锁定池&#xff0c;只有针对此对象发出notify或者…

TCP网络通信编程之字节流

目录 【TCP字节流编程】 // 网络编程中&#xff0c;一定是server端先运行 【案例1】 【思路分析】 【客户端代码】 【服务端代码】 【结果展示】 【案例2】 【题目描述】 【注意事项】 【服务端代码】 【客户端代码】 【代码结果】 【TCP字节流编程】 // 网络编程中&a…

list与流迭代器stream_iterator

运行代码&#xff1a; //list与流迭代器 #include"std_lib_facilities.h" //声明Item类 struct Item {string name;int iid;double value;Item():name(" "),iid(0),value(0.0){}Item(string ss,int ii,double vv):name(ss),iid(ii),value(vv){}friend ist…

【计算机视觉 | 图像分割】arxiv 计算机视觉关于图像分割的学术速递(7 月 26 日论文合集)

文章目录 一、分割|语义相关(7篇)1.1 Learning Transferable Object-Centric Diffeomorphic Transformations for Data Augmentation in Medical Image Segmentation1.2 Optical Flow boosts Unsupervised Localization and Segmentation1.3 Spectrum-guided Multi-granularity…

latex论文----写作代码

一般来说论文机构会给定latex模板代码&#xff0c;我们只需要知道怎么写就行&#xff0c;格式机构都给你调好了 1 各类标题 section是最大的标题&#xff0c;后边每一级小标题&#xff0c;都在前边加个sub就行 \section{Method} \subsection{Dataset} \subsubsection{Dataset…

2014年全国硕士研究生入学统一考试管理类专业学位联考写作试题——解析版

2014年1月真题: 四、写作:第56~57小题&#xff0c;共65分。其中论证有效性分析30 分&#xff0c;论说文35分。 56.论证有效性分析: 分析下述论证中存在的缺陷和漏洞&#xff0c;选择若干要点&#xff0c;写一篇600字左右的文章&#xff0c;对该论证的有效性进行分析和评论。…

error: cannot call member function ‘void me::sendMessage()‘ without object

error: cannot call member function void me::sendMessage&#xff08;&#xff09; without object 原因分析解决方案 原因分析 在connect中&#xff0c;传递函数地址不用带括号。&#xff08;参考函数指针的赋值&#xff09; #include <iostream> // 包含头…

redis线程模型

文章目录 一、redis单线程模型1.1 为什么redis命令处理是单线程&#xff0c;而不采用多线程1.2 单线程的局限及redis的优化方式 二、redis单线程为什么这么快2.1 采用的机制2.2 优化的措施 三、redis的IO多线程模型3.1 redis 为什么引入IO多线程模型3.2 配置io-threads-do-read…