C++设计模式笔记

设计模式

如何解决复杂性?

分解

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

抽象

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

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

面向对象设计原则

对象的概念

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

设计原则

依赖倒置原则(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/104371658BMW(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/104371658Audi(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/104371658BMW(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/104371658Audi(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 0x407030LazySingleton *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.mzph.cn/news/12984.shtml

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

相关文章

ShardingSphere-Proxy水平分片详解与实战

&#x1f680; ShardingSphere &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&…

Python爬虫时遇到SSL证书验证错误解决办法汇总

在进行Python爬虫任务时&#xff0c;遇到SSL证书验证错误是常见的问题之一。SSL证书验证是为了确保与服务器建立的连接是安全和可信的&#xff0c;但有时候可能会由于证书过期、不匹配或未受信任等原因导致验证失败。为了解决这个问题&#xff0c;本文将提供一些实用的解决办法…

【电路效应】信号处理和通信系统模型中的模拟电路效应研究(SimulinkMatlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f308;4 Matlab代码、Simulink仿真实现 &#x1f4a5;1 概述 在信号处理和通信系统模型中&#xff0c;模拟电路效应研究是指考虑到实际电路的特性对信号进行建模和分析的过程。模拟电路效应…

ffplay播放器剖析(6)----音视频同步分析

文章目录 1. 音视频同步基础1.1 音视频同步策略1.2 音视频同步概念1.3 FFmpeg中的时间单位1.4 不同结构体的time_base/duration分析1.5 不同结构体的pts/dts分析1.6 ffplay中Frame结构体分析1.7 Vidoe Frame PTS获取及矫正1.8 Audio Frame PTS的获取 2.以音频为基准3.以视频为基…

excel绘制折线图或者散点图

一、背景 假如现在通过代码处理了一批数据&#xff0c;想看数据的波动情况&#xff0c;是不是还需要写个pyhon代码&#xff0c;读取文件&#xff0c;绘制曲线&#xff0c;看起来也简单&#xff0c;但是还有更简单的方法&#xff0c;就是直接生成csv文件&#xff0c;csv文件就是…

【MySQL】事务之MVCC(多版本并发控制)

【MySQL】事务-MVCC 一、数据库并发的三种场景二、MVCC2.1 3个记录隐藏字段2.2 undo log&#xff08;撤销日志&#xff09;2.3 模拟MVCC---update2.3.1 delete2.3.2 insert2.3.3 select 2.4 Read View2.5 整体流程 三、RR&#xff08;可重复读&#xff09;与RC&#xff08;读提…

【3-D深度学习:肺肿瘤分割】创建和训练 V-Net 神经网络,并从 3D 医学图像中对肺肿瘤进行语义分割研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

sql server表值函数

一、创建测试表 Employees 二、创建表值函数 -- DROP FUNCTION TableIntSplit;CREATE FUNCTION TableIntSplit(Text NVARCHAR(4000),Sign NVARCHAR(4000)) RETURNS tempTable TABLE(Id INT ) AS BEGIN DECLARE StartIndex INT DECLARE FindIndex INT DECLARE Content VARCHAR(…

阿里云盘自动每日签到无需部署无需服务器(仅限学习交流使用)

一、前言 阿里云盘自动每日签到&#xff0c;无需部署&#xff0c;无需服务器 执行思路&#xff1a;使用金山文档的每日定时任务&#xff0c;执行阿里云盘签到接口。 二、效果展示&#xff1a; 三、步骤&#xff1a; 1、进入金山文档网页版 金山文档官网&#xff1a;https:…

Verilog语法学习——LV7_求两个数的差值

LV7_求两个数的差值 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 描述 根据输入信号a,b的大小关系&#xff0c;求解两个数的差值&#xff1a;输入信号a,b…

【飞书】飞书导出md文档 | 飞书markdown文档导出 | 解决飞书只能导出pdf word

一、飞书导出markdown github地址&#xff1a;https://github.com/Wsine/feishu2md 这是一个下载飞书文档为 Markdown 文件的工具&#xff0c;使用 Go 语言实现。 请看这里&#xff1a;招募有需求和有兴趣的开发者&#xff0c;共同探讨开发维护&#xff0c;有兴趣请联系。 二、…

MySQL数据库分库分表备份(shell脚本)

创建目录 mkdir /server/scripts 一、使用脚本实现分库备份 1、创建脚本并编写 [rootlocalhost scripts]# vim bak_db_v1.sh #!/bin/bash ######################################### # File Name:bak_db_v1.sh # Version: V1.0 # Author:Shen QL # Email:17702390000163.co…

干翻Dubbo系列第四篇:Dubbo3第一个应用程序细节补充

前言 不从恶人的计谋&#xff0c;不站罪人的道路&#xff0c;不坐亵慢人的座位&#xff0c;惟喜爱耶和华的律法&#xff0c;昼夜思想&#xff0c;这人便为有福&#xff01;他要像一棵树栽在溪水旁&#xff0c;按时候结果子&#xff0c;叶子也不枯干。凡他所做的尽都顺利。 如…

短视频矩阵系统源码开发流程​

一、视频矩阵系统源码开发流程分为以下几个步骤&#xff1a; 四、技术开发说明&#xff1a; 产品原型PRD需求文档产品交互流程图部署方式说明完整源代码源码编译方式说明三方框架和SDK使用情况说明和代码位置平台操作文档程序架构文档 一、抖音SEO矩阵系统源码开发流程分为以…

【Linux】进程通信 — 共享内存

文章目录 &#x1f4d6; 前言1. 共享内存2. 创建共享内存2.1 ftok()创建key值&#xff1a;2.2 shmget()创建共享内存&#xff1a;2.3 ipcs指令&#xff1a;2.4 shmctl()接口&#xff1a;2.5 shmat()/shmdt()接口:2.6 共享内存没有访问控制&#xff1a;2.7 通过管道对共享内存进…

Python进行数据分析(详细教程)

1.为什么选择Python进行数据分析&#xff1f; Python是一门动态的、面向对象的脚本语言&#xff0c;同时也是一门简约&#xff0c;通俗易懂的编程语言。Python入门简单&#xff0c;代码可读性强&#xff0c;一段好的Python代码&#xff0c;阅读起来像是在读一篇外语文章。Pyth…

Jenkins搭建最简教程

纠结了一小会儿&#xff0c;到底要不要写这个&#xff0c;最终还是决定简单记录一下&#xff0c;因为Jenkins搭建实在是太简单了&#xff0c;虽然也有坑&#xff0c;但是坑主要在找稳定的版本上。 先学一个简称&#xff0c;LTS (Long Term Support) 属实是长见识了&#xff0c…

docker 搭建jenkins

1、拉取镜像 docker pull jenkins/jenkins:2.4162、创建文件夹 mkdir -p /home/jenkins_mount chmod 777 /home/jenkins_mount3、运行并构建容器 docker run --restartalways -d -p 10240:8080 -p 10241:50000 -v /home/jenkins_mount:/var/jenkins_home -v /etc/localtime:…

如何选择台式还是便携式多参数水质检测仪呢

选择台式还是便携式多参数水质检测仪主要取决于具体的使用需求和场景。 1.便携式多参数水质检测仪适用于需要在不同地点进行水质检测的情况&#xff0c;例如户外采样、实地调查等。它具有小巧轻便的特点&#xff0c;方便携带和操作&#xff0c;适合需要频繁移动或需要灵活使用的…

如何维护你的电脑:提升性能和延长使用寿命

如何维护你的电脑&#xff1a;提升性能和延长使用寿命 &#x1f607;博主简介&#xff1a;我是一名正在攻读研究生学位的人工智能专业学生&#xff0c;我可以为计算机、人工智能相关本科生和研究生提供排忧解惑的服务。如果您有任何问题或困惑&#xff0c;欢迎随时来交流哦&…