1.设计模式

GitHub - FengJungle/DesignPattern: Design pattern demo code

(一)

① 简单工厂模式

再不学简单工厂模式,就真的要去工厂搬砖啦~_冯Jungle的博客-CSDN博客

通过以下的例子可见,只需要提供产品名称作为参数,传入工厂的方法中,即可得到对应产品。抽象产品类中并没有提供公共方法的实现,而是在各个具体产品类中根据各自产品情况实现。

当然,简单工厂模式存在明显的不足。假设有一天Jungle想玩网球了,该怎么办呢?你肯定会说,这还不容易吗?再从抽象产品类派生出一个Tennis类,并在工厂类的getSportProduct方法中增加“productName == "Tennis”的条件分支即可。的确如此,但是这明显违背了开闭原则(对扩展开放,对修改关闭),即在扩展功能时修改了已有的代码。另一方面,简单工厂模式所有的判断逻辑都在工厂类中实现,一旦工厂类设计故障,则整个系统都受之影响!

例1

SimpleFactory.h

#ifndef _SIMPLE_FACTORY_
#define _SIMPLE_FACTORY_#include <iostream>// 抽象类
class AbstractSportProduct{
public:virtual void printName() = 0;virtual void play() = 0;
};class Basketball : public AbstractSportProduct{
public:void printName(){std::cout << "Jungle get Basketball\n";}void play(){std::cout << "Jungle play Basketball\n";}
};class Football : public AbstractSportProduct{
public:Football(){printName();play();}void printName(){std::cout << "Jungle get Football\n";}void play(){std::cout << "Jungle play Football\n";}
};class Volleyball : public AbstractSportProduct{
public:void printName(){std::cout << "Jungle get Volleyball\n";}void play(){std::cout << "Jungle play Volleyball\n";}
};class Factory{
public:std::shared_ptr<AbstractSportProduct> getSportProduct(std::string productName){std::shared_ptr<AbstractSportProduct> pro;if(productName == "Basketball"){pro = std::make_shared<Basketball>();}else if (productName == "Football"){pro = std::make_shared<Football>();}else if (productName == "Volleyball"){pro = std::make_shared<Volleyball>();}return pro;}
};#endif

main.cpp

#include <iostream>
#include "SimpleFactory.h"int main(){std::shared_ptr<Factory> f = std::make_shared<Factory>();std::shared_ptr<AbstractSportProduct> pro = f->getSportProduct("Basketball");pro->printName();pro->play();std::cout << "-------------------------\n";pro = f->getSportProduct("Football");std::cout << "-------------------------\n";pro = f->getSportProduct("Volleyball");pro->printName();pro->play();return 0;
}

② 工厂模式

如果Jungle想玩网球(Tennis),只需要增加一个棒球工厂(TennisFacory),然后在客户端代码中修改具体工厂类的类名,而原有的类的代码无需修改。由此可看到,相较简单工厂模式,工厂方法模式更加符合开闭原则。工厂方法是使用频率最高的设计模式之一,是很多开源框架和API类库的核心模式。

例1

FactoryMethod.h

#ifndef __FACTORY_METHOD__
#define __FACTORY_METHOD__#include <iostream>// 抽象产品类
class AbstractSportProduct{
public:virtual void printName() = 0;virtual void play() = 0;
};class Basketball :public AbstractSportProduct{
public:void printName(){std::cout << "Jungle get Basketball\n";}void play(){std::cout << "Jungle play Basketball\n";}
};class Football :public AbstractSportProduct{
public:Football(){printName();play();}void printName(){std::cout << "Jungle get Football\n";}void play(){std::cout << "Jungle play Football\n";}
};class Volleyball :public AbstractSportProduct{
public:void printName(){std::cout << "Jungle get Volleyball\n";}void play(){std::cout << "Jungle play Volleyball\n";}
};// 抽象工厂类
class AbstractFactory{
public:virtual std::shared_ptr<AbstractSportProduct> getSportProduct() = 0;
};class BasketballFactory : public AbstractFactory{
public:std::shared_ptr<AbstractSportProduct> getSportProduct() {return std::make_shared<Basketball>();}
};class FootballFactory : public AbstractFactory{
public:std::shared_ptr<AbstractSportProduct> getSportProduct() {return std::make_shared<Football>();}
};class VolleyballFactory : public AbstractFactory{
public:std::shared_ptr<AbstractSportProduct> getSportProduct() {return std::make_shared<Volleyball>();}
};#endif

main.cpp

#include <iostream>
#include "FactoryMethod.h"int main(){std::shared_ptr<BasketballFactory> f = std::make_shared<BasketballFactory>();std::shared_ptr<AbstractSportProduct> pro = f->getSportProduct();pro->printName();pro->play();std::cout << "------------------------------\n";std::shared_ptr<FootballFactory> f1 = std::make_shared<FootballFactory>();pro = f1->getSportProduct();std::cout << "------------------------------\n";std::shared_ptr<VolleyballFactory> f2 = std::make_shared<VolleyballFactory>();pro = f2->getSportProduct();pro->printName();pro->play();return 0;
}

③ 抽象工厂模式

回顾之前的设计模式,简单工厂模式所有逻辑都封装在工厂类中,工厂根据客户提供的产品名字创建对应产品的对象实例;工厂方法模式将产品的创建过程放到了具体工厂类中,每一个工厂可以创建一个具体产品,由此可能会创建许多工厂类。很多时候,一个工厂不只是生产一种产品,而是生产一类产品,比如一个体育用品工厂,可以生产篮球、足球、排球等多种产品。此时我们可以把这些相关的产品归纳为一个“产品族”,由同一个工厂来生产,这即是Jungle今天要学习的抽象工厂模式。

例1

AbstractFactory.h

#ifndef __ABSTRACT_FACTORY__
#define __ABSTRACT_FACTORY__#include <iostream>// 抽象球类
class AbstractBall{
public:virtual void play() = 0;
};class Basketball : public AbstractBall{
public:void play() {std::cout << "Jungle play Basketball\n";}
};class Football : public AbstractBall{
public:void play() {std::cout << "Jungle play Football\n";}
};// 抽象衬衫类
class AbstractShirt{
public:virtual void wearShirt() = 0;
};class BasketballShirt : public AbstractShirt{
public:void wearShirt() {std::cout << "Jungle wear Basketball Shirt\n";}
};class FootballShirt : public AbstractShirt{
public:void wearShirt() {std::cout << "Jungle wear Football Shirt\n";}
};// 抽象工厂类
class AbstractFactory{
public:virtual std::shared_ptr<AbstractBall> getBall() = 0;virtual std::shared_ptr<AbstractShirt> getShirt() = 0;
};class BasketballFactory : public AbstractFactory{
public:std::shared_ptr<AbstractBall> getBall(){std::cout << "Jungle get Basketball\n";return std::make_shared<Basketball>();}std::shared_ptr<AbstractShirt> getShirt(){std::cout << "Jungle get Basketball Shirt\n";return std::make_shared<BasketballShirt>();}
};class FootballFactory : public AbstractFactory{
public:std::shared_ptr<AbstractBall> getBall(){std::cout << "Jungle get Football\n";return std::make_shared<Football>();}std::shared_ptr<AbstractShirt> getShirt(){std::cout << "Jungle get Football Shirt\n";return std::make_shared<FootballShirt>();}
};#endif

main.cpp

#include <iostream>
#include "AbstractFactory.h"int main(){std::shared_ptr<BasketballFactory> f = std::make_shared<BasketballFactory>();std::shared_ptr<AbstractBall> ab = f->getBall();    std::shared_ptr<AbstractShirt> as = f->getShirt();ab->play();as->wearShirt();std::cout << "---------------------------------\n";std::shared_ptr<FootballFactory> f1 = std::make_shared<FootballFactory>();ab = f1->getBall();    as = f1->getShirt();ab->play();as->wearShirt();return 0;
}

④ 建造者模式

例1

BuilderPattern.h

#ifndef __BUILDER_PATTERN__
#define __BUILDER_PATTERN__#include <iostream>// 产品类
class House{
public:void setFloor(std::string iFloor){this->floor = iFloor;}void setWall(std::string iWall){this->wall = iWall;}void setRoof(std::string iRoof){this->roof = iRoof;}void printfHouseInfo() {std::cout << "floor: " << floor << "\n";std::cout << "wall: "  << wall  << "\n";std::cout << "roof: "  << roof  << "\n";}private:std::string floor;std::string wall;std::string roof;
};// 抽象建造者
class AbstractBuilder{
public:AbstractBuilder(){house = std::make_shared<House>();}virtual void buildFloor() = 0;virtual void buildWall() = 0;virtual void buildRoof() = 0;virtual std::shared_ptr<House> getHouse() = 0;std::shared_ptr<House> house;
};// 具体建造者A
class ConcreteBuilderA : public AbstractBuilder{
public:void buildFloor() {house->setFloor("Floor_A");}void buildWall(){house->setWall("Wall_A");}void buildRoof(){house->setRoof("Roof_A");}std::shared_ptr<House> getHouse(){return house;}
};// 具体建造者B
class ConcreteBuilderB : public AbstractBuilder{
public:void buildFloor() {        house->setFloor("Floor_B");}void buildWall(){        house->setWall("Wall_B");}void buildRoof(){        house->setRoof("Roof_B");}std::shared_ptr<House> getHouse(){return house;}
};// 指挥者
class Director{
public:void setBuilder(std::shared_ptr<AbstractBuilder> ibuilder){        builder = ibuilder;}// 封装组装流程,返回建造结果    std::shared_ptr<House> construct(){builder->buildFloor();builder->buildRoof();builder->buildWall();return builder->getHouse();}private:    std::shared_ptr<AbstractBuilder> builder;
};#endif

main.cpp

#include <iostream>
#include "BuilderPattern.h"int main(){     // 指定具体建造者A  std::shared_ptr<Director> d = std::make_shared<Director>();std::shared_ptr<ConcreteBuilderA> cba = std::make_shared<ConcreteBuilderA>();d->setBuilder(cba);std::shared_ptr<House> h = d->construct();h->printfHouseInfo();std::cout << "---------------------------\n";// 指定具体建造者Bstd::shared_ptr<ConcreteBuilderB> cbb = std::make_shared<ConcreteBuilderB>();d->setBuilder(cbb);h = d->construct();h->printfHouseInfo();return 0;
}

⑤ 原型模式

原型模式通过复制一个已有对象来获取更多相同或者相似的对象。

例1

报错如下:Segmentation fault: 11 空指针问题

PrototypePattern.h

#ifndef __PROTOTYPE_PATTERN__
#define __PROTOTYPE_PATTERN__#include <iostream>class WorkModel{
public:void setWorkModelName(std::string imodelName){modelName = imodelName;}std::string getWorkModelName(){return modelName;}
private:std::string modelName;
};class ConcreteWork {
public:std::shared_ptr<ConcreteWork> clone(){std::shared_ptr<ConcreteWork> cw = std::make_shared<ConcreteWork>();cw->setIdNum(idNum);cw->setIdName(idName);cw->setIWorkModel(iWorkModel);return cw;}std::shared_ptr<WorkModel> getIWorkModel(){return iWorkModel;}void setIdNum(int iIdNum){idNum = iIdNum;}void setIdName(std::string iIdName){idName = iIdName;}void setIWorkModel(std::shared_ptr<WorkModel> iIWorkModel){iWorkModel = iIWorkModel;}void printWorkInfo(){std::cout << "Name: " << idName << "\n";std::cout << "Num: "  << idNum  << "\n";std::cout << "ModelName: " << iWorkModel->getWorkModelName() << "\n";}private:int idNum;std::string idName;std::shared_ptr<WorkModel> iWorkModel;
};#endif

main.cpp

#include <iostream>
#include "PrototypePattern.h"int main(){std::shared_ptr<ConcreteWork> singleWork = std::make_shared<ConcreteWork>();singleWork->setIdNum(1001);singleWork->setIdName("single");std::shared_ptr<WorkModel> singleWorkModel = singleWork->getIWorkModel();// 错:singleWorkModel是空指针,执行setWorkModelName()会报错!singleWorkModel->setWorkModelName("single_model"); std::cout << "single完成了作业:\n";return 0;
}

改正如下:

PrototypePattern.h

#ifndef __PROTOTYPE_PATTERN__
#define __PROTOTYPE_PATTERN__#include <iostream>class WorkModel{
public:void setWorkModelName(std::string imodelName){modelName = imodelName;}std::string getWorkModelName(){return modelName;}
private:std::string modelName;
};class ConcreteWork {
public:std::shared_ptr<ConcreteWork> clone(){std::shared_ptr<ConcreteWork> cw = std::make_shared<ConcreteWork>();cw->setIdNum(idNum);cw->setIdName(idName);cw->setIWorkModel(iWorkModel);return cw;}void setIdNum(int iIdNum){idNum = iIdNum;}void setIdName(std::string iIdName){idName = iIdName;}void setIWorkModel(std::shared_ptr<WorkModel> iIWorkModel){iWorkModel = iIWorkModel;}void printWorkInfo(){std::cout << "Name: " << idName << "\n";std::cout << "Num: "  << idNum  << "\n";std::cout << "ModelName: " << iWorkModel->getWorkModelName() << "\n";}private:int idNum;std::string idName;std::shared_ptr<WorkModel> iWorkModel;
};#endif

main.cpp

#include <iostream>
#include "PrototypePattern.h"int main(){std::shared_ptr<ConcreteWork> singleWork = std::make_shared<ConcreteWork>();singleWork->setIdNum(1001);singleWork->setIdName("single");std::shared_ptr<WorkModel> workModel = std::make_shared<WorkModel>();workModel->setWorkModelName("single_model");singleWork->setIWorkModel(workModel);singleWork->printWorkInfo();std::shared_ptr<ConcreteWork> jungleWork = singleWork->clone();std::cout << "jungle正在抄作业...\n";std::cout << "jungle抄完了,正在改名字和学号,否则会被老师查出来...\n";jungleWork->setIdNum(1002);jungleWork->setIdName("jungle");workModel = std::make_shared<WorkModel>();workModel->setWorkModelName("jungle_Model");jungleWork->setIWorkModel(workModel);std::cout << "jungle也完成了作业:\n";jungleWork->printWorkInfo();return 0;
}

⑥ 单例模式

c++ 单例模式_乒乒乓乓丫的博客-CSDN博客

Singleton.h

#ifndef __SINGLETON_H__
#define __SINGLETON_H__#include <iostream>
#include <mutex>std::mutex m_mutex;// 懒汉单例模式
class Singleton_Lazy{
public:static std::shared_ptr<Singleton_Lazy> getInstance(){std::cout << "singleton lazy mode.\n";if(instance == nullptr){m_mutex.lock();     // 加锁if(instance == nullptr){std::cout << "创建新的实例.\n";instance = std::make_shared<Singleton_Lazy>();}m_mutex.unlock();   // 解锁}return instance;}private:static std::shared_ptr<Singleton_Lazy> instance;
};std::shared_ptr<Singleton_Lazy> Singleton_Lazy::instance = nullptr;// 饿汉单例模式
class Singleton_Hungry{
public:static std::shared_ptr<Singleton_Hungry> getInstance(){std::cout << "singleton hungry mode.\n";instance = std::make_shared<Singleton_Hungry>();return instance;}
private:static std::shared_ptr<Singleton_Hungry> instance;
};std::shared_ptr<Singleton_Hungry> Singleton_Hungry::instance = nullptr;#endif

main.cpp

#include <iostream>
#include "Singleton.h"
#include <pthread.h>#define THREAD_NUM 6void* callSingleton_Lazy(void*){std::shared_ptr<Singleton_Lazy> sl = Singleton_Lazy::getInstance();std::cout << "线程编号: " << pthread_self() << "\n";
}void* callSingleton_Hungry(void*){std::shared_ptr<Singleton_Hungry> sh = Singleton_Hungry::getInstance();std::cout << "线程编号: " << pthread_self() << "\n";
}int main(){pthread_t threads_pool[THREAD_NUM];int tids[THREAD_NUM];// 线程具有属性,用pthread_attr_t表示,在对该结构进行处理之前必须进行初始化,在使用后需要对其去除初始化。pthread_attr_t attr;void* status;// 创建 pthread_attr_tpthread_attr_init(&attr);       // 线程的分离状态决定一个线程以什么样的方式来终止自己。在默认情况下线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束。// 只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。// PTHREAD _CREATE_JOINABLE(非分离线程)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);for(int i = 0; i < THREAD_NUM; i++){if(i < THREAD_NUM / 2){// 线程创建 pthread_createtids[i] = pthread_create(&threads_pool[i], nullptr, callSingleton_Lazy, (void*)&i);}else {std::cout << "-----------------\n";tids[i] = pthread_create(&threads_pool[i], nullptr, callSingleton_Hungry, (void*)&i);}if(tids[i]){std::cout << "Error: unable to create thread.\n";return 0;}}// 销毁 pthread_attr_tpthread_attr_destroy(&attr);    for(int i = 0; i < THREAD_NUM; i++){// pthread_join 主线程阻塞tids[i] = pthread_join(threads_pool[i], &status);if(tids[i]){std::cout << "Error: unable to join.\n";return 0;}}std::cout << "main exiting.\n";return 0;
}

⑦ 适配器模式

AdapterPattern.h

#ifndef __ADAPTERPATTERN_H__
#define __ADAPTERPATTERN_H__#include <iostream>// 适配者类DxfParser
class DxfParser{
public:void parseFile(){std::cout << "Parse dxf file\n";}
};// 适配者类PathPlanner
class PathPlanner{
public:void calculate(){std::cout << "calculate path\n";}
};class Adapter{
public:void pathPlanning(){std::cout << "pathPlanning\n";dxfParser->parseFile();pathPlanner->calculate();}
private:std::shared_ptr<DxfParser>   dxfParser;std::shared_ptr<PathPlanner> pathPlanner;
};#endif

main.cpp

#include <iostream>
#include "AdapterPattern.h"int main(){std::shared_ptr<Adapter> adapter = std::make_shared<Adapter>();adapter->pathPlanning();return 0;
}

⑧  桥接模式

BridgePattern.h

#ifndef __BRIDGE_PATTERN_H__
#define __BRIDGE_PATTERN_H__#include <iostream>// 抽象类Game
class Game{
public:virtual void play() = 0;
};class GameA : public Game {
public:void play(){std::cout << "Jungle玩游戏A\n";}
};class GameB : public Game {
public:void play(){std::cout << "Jungle玩游戏B\n";}
};// 抽象类Phone
class Phone{
public:virtual void setUpGame(std::shared_ptr<Game> igame) = 0;virtual void play() = 0;
private:std::shared_ptr<Game> game;
};class PhoneA : public Phone{
public:void setUpGame(std::shared_ptr<Game> igame){game = igame;}void play(){game->play();}
private:std::shared_ptr<Game> game;
};class PhoneB : public Phone{
public:void setUpGame(std::shared_ptr<Game> igame){game = igame;}void play(){game->play();}
private:std::shared_ptr<Game> game;
};#endif

main.cpp

#include <iostream>
#include "BridgePattern.h"int main(){// Jungle买了PhoneA品牌的手机,想玩游戏Astd::shared_ptr<PhoneA> phoneA = std::make_shared<PhoneA>();std::shared_ptr<GameA> gameA = std::make_shared<GameA>();phoneA->setUpGame(gameA);phoneA->play();std::cout << "----------------\n";// Jungle想在这个手机上玩游戏Bstd::shared_ptr<GameB> gameB = std::make_shared<GameB>();phoneA->setUpGame(gameB);phoneA->play();return 0;
}

⑨  组合模式

  • 派生类中如果没有完全实现基类的所有纯虚函数,则此时的派生类也是抽象类,不能实例化对象。换言之,抽象类的派生类是允许不实现基类的所有纯虚函数的。

CompositePattern.h

#ifndef __COMPOSITE_PATTERN_H__
#define __COMPOSITE_PATTERN_H__#include <iostream>
#include <vector>// 抽象构件
class Component{
public:Component(){}Component(std::string iName){name = iName;}// 增加一个部门或办公室virtual void add(std::shared_ptr<Component> com) = 0;// 撤销一个部门或办公室virtual void remove(std::shared_ptr<Component> com) = 0;//virtual std::shared_ptr<Component> getChild(int num) = 0;// 各部门操作virtual void operation() = 0;std::string getName(){return name;}private:std::string name;
};// 叶子构件:办公室
// 没实现operation(),所以抽象类Component的子类Office也是抽象类,不能实例化!
class Office : public Component{
public:Office(){}Office(std::string iName){name = iName;}void add(std::shared_ptr<Component> com){std::cout << "not support!\n";}void remove(std::shared_ptr<Component> com){std::cout << "not support!\n";}std::shared_ptr<Component> getChild(int num){std::cout << "not support!\n";return nullptr;}    private:std::string name;
};// 叶子构件:行政办公室
// 虽然没实现add()、remove()、getChild(),但其基类Office已经实现过了这部分 ——>
// 所以派生类AdminOffice虽然只实现了operation(), 但其仍然可以实例化!
class AdminOffice : public Office{
public:AdminOffice(){}AdminOffice(std::string iName){name = iName;}    void operation(){std::cout << "-----Administration Office: " << name << "\n";}private:std::string name;
};// 叶子构件:教务办公室
// 可以实例化,原因同AdminOffice
class DeanOffice : public Office{
public:DeanOffice(){}DeanOffice(std::string iName){name = iName;}void operation(){std::cout << "-----Dean Office: " << name << "\n";}private:std::string name;
};// 容器构件SubComponent
// 实现了Component的所有纯虚函数,所以SubComponent可以实例化!
class SubComponent : public Component{
public:SubComponent(){}SubComponent(std::string iName){name = iName;}void add(std::shared_ptr<Component> com){componentList.push_back(com);} void remove(std::shared_ptr<Component> com){for(int i = 0; i < componentList.size(); i++){// 遍历查找if(componentList[i]->getName() == com->getName()){componentList.erase(componentList.begin() + 1);break;}}}std::shared_ptr<Component> getChild(int num){return componentList[num];}void operation(){std::cout << name << "\n";for(int i = 0; i < componentList.size(); i++){componentList[i]->operation();}}private:std::string name;// 构件列表std::vector<std::shared_ptr<Component> > componentList;
};#endif

main.cpp

#include <iostream>
#include "CompositePattern.h"int main(){std::shared_ptr<SubComponent> head   = std::make_shared<SubComponent>("总部");std::shared_ptr<SubComponent> sichuanBranch = std::make_shared<SubComponent>("四川分部");    std::shared_ptr<AdminOffice> office1 = std::make_shared<AdminOffice>("行政办公室");    std::shared_ptr<DeanOffice>  office2 = std::make_shared<DeanOffice>("教务办公室");    std::shared_ptr<SubComponent> cdBranch = std::make_shared<SubComponent>("成都分部");std::shared_ptr<SubComponent> myBranch = std::make_shared<SubComponent>("绵阳分部");    std::shared_ptr<AdminOffice> office3 = std::make_shared<AdminOffice>("行政办公室");    std::shared_ptr<DeanOffice>  office4 = std::make_shared<DeanOffice>("教务办公室"); std::shared_ptr<AdminOffice> office5 = std::make_shared<AdminOffice>("行政办公室");    std::shared_ptr<DeanOffice>  office6 = std::make_shared<DeanOffice>("教务办公室"); std::shared_ptr<AdminOffice> office7 = std::make_shared<AdminOffice>("行政办公室");    std::shared_ptr<DeanOffice>  office8 = std::make_shared<DeanOffice>("教务办公室"); cdBranch->add(office5);cdBranch->add(office6);myBranch->add(office7);myBranch->add(office8);sichuanBranch->add(office3);sichuanBranch->add(office4);sichuanBranch->add(cdBranch);sichuanBranch->add(myBranch);head->add(office1);head->add(office2);    head->add(sichuanBranch);    head->operation();return 0;
}

⑩  装饰模式

DecoratorPattern.h

#ifndef __DECORATOR_PATTERN_H__
#define __DECORATOR_PATTERN_H__#include <iostream>// 抽象构件
class Component{
public:virtual void operation() = 0;
};// 具体构件
class Phone : public Component{
public:void operation(){std::cout << "手机\n";}
};class Decorator : public Component{
public:void operation(){component->operation();}void setComponent(std::shared_ptr<Component> iComponent){        component = iComponent;}std::shared_ptr<Component> getComponent(){return component;}private:std::shared_ptr<Component> component;
};// 具体装饰类:手机壳
class DecoratorShell : public Decorator{
public:void operation(){        getComponent()->operation();std::cout << "安装手机壳\n";}
};// 具体装饰类:手机贴纸
class DecoratorSticker : public Decorator{
public:void operation(){        getComponent()->operation();std::cout << "贴卡通贴纸ֽ\n";}
};//  具体装饰类:挂绳
class DecoratorRope : public Decorator{
public:void operation(){        getComponent()->operation();std::cout << "系手机挂绳\n";}
};#endif

main.cpp

#include <iostream>
#include "DecoratorPattern.h"int main(){std::cout << "Jungle's first phone\n";std::shared_ptr<Phone> p = std::make_shared<Phone>();    std::shared_ptr<DecoratorShell> ds = std::make_shared<DecoratorShell>();    ds->setComponent(p);ds->operation();std::cout << "\nJungle's second phone\n";std::shared_ptr<DecoratorSticker> dst = std::make_shared<DecoratorSticker>();   dst->setComponent(ds);dst->operation();std::cout << "\nJungle's third phone\n";std::shared_ptr<DecoratorRope> dr = std::make_shared<DecoratorRope>();   dr->setComponent(dst);dr->operation();return 0;
}

(二)

① 外观模式

FacadePattern.h

/** @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git* @Date: 2021-08-19 11:19:57* @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git* @LastEditTime: 2023-03-13 10:29:14* @FilePath: /cpp_test/FacadePattern.h* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE#*/
#ifndef __FACADE_PATTERN_H__
#define __FACADE_PATTERN_H__#include <iostream>class Memory{
public:void selfCheck(){std::cout << "memory selfchecking......\n";}
};class Cpu{
public:void run(){std::cout << "running cpu......\n";}
};class HardDisk{
public:void read(){std::cout << "reading hardDisk......\n";}
};class Os{
public:void load(){std::cout << "loading os.....\n";        }
};class Facade{
public:void powerOn(){std::cout << "power on……\n";  memory->selfCheck();cpu->run();hardDisk->read();os->load();        std::cout << "ready!\n";         }private:std::shared_ptr<Memory>   memory;std::shared_ptr<Cpu>      cpu;std::shared_ptr<HardDisk> hardDisk;std::shared_ptr<Os>       os;
};#endif

main.cpp

#include <iostream>
#include "FacadePattern.h"int main(){std::shared_ptr<Facade> f = std::make_shared<Facade>();f->powerOn();return 0;
}

② 享元模式

FlyweightPattern.h

/** @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git* @Date: 2021-08-19 11:19:57* @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git* @LastEditTime: 2023-03-13 11:23:46* @FilePath: /cpp_test/FlyweightPattern.h* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE*/
#ifndef __FLYPATTERN_PATTERN_H__
#define __FLYPATTERN_PATTERN_H__#include <iostream>
#include <vector>
#include <mutex>std::mutex m_mutex;// 抽象享元类
class NetDevice{
public:virtual std::string getName() = 0;void print(int portNum){std::cout << "NetDevice: " << getName() << ", port: " << portNum << "\n";}
};// 具体享元类:集线器
class Hub : public NetDevice{
public:std::string getName(){return "集线器";}
};// 具体享元类:交换机
class Switch : public NetDevice{
public:std::string getName(){return "交换机";}
};// 享元工厂类
class NetDeviceFactory{
public:NetDeviceFactory(){std::shared_ptr<Hub> hub = std::make_shared<Hub>();        std::shared_ptr<Switch> switcher = std::make_shared<Switch>();        devicePool.push_back(hub);devicePool.push_back(switcher);}std::shared_ptr<NetDevice> getNetDevice(char c){        if(c == 'S'){return devicePool[1];}else if (c == 'H'){return devicePool[0];}else{std::cout << "wrong input!\n";}    return nullptr;    }// 单例模式:返回享元工厂类的唯一实例static std::shared_ptr<NetDeviceFactory> getFactory(){if(instance == nullptr){m_mutex.lock();if(instance == nullptr){instance = std::make_shared<NetDeviceFactory>();}m_mutex.unlock();}return instance;}private:static std::shared_ptr<NetDeviceFactory> instance;// 共享池std::vector<std::shared_ptr<NetDevice> > devicePool;
};std::shared_ptr<NetDeviceFactory> NetDeviceFactory::instance = nullptr;#endif

main.cpp

#include <iostream>
#include "FlyweightPattern.h"int main(){std::shared_ptr<NetDeviceFactory> netDeviceFactory = NetDeviceFactory::getFactory();// 客户端1获取一个hubstd::shared_ptr<NetDevice> netDevice1 = netDeviceFactory->getNetDevice('H');netDevice1->print(1);// 客户端2获取一个hubstd::shared_ptr<NetDevice> netDevice2 = netDeviceFactory->getNetDevice('H');netDevice2->print(2);std::cout << "判断两个hub是否是同一个:\n";std::cout << "netDevice1: " << netDevice1 << ", netDevice2: " << netDevice2 << "\n\n\n";// 客户端3获取一个switchstd::shared_ptr<NetDevice> netDevice3 = netDeviceFactory->getNetDevice('S');netDevice3->print(1);// 客户端4获取一个switchstd::shared_ptr<NetDevice> netDevice4 = netDeviceFactory->getNetDevice('S');netDevice4->print(2);std::cout << "判断两个switch是否是同一个:\n";std::cout << "netDevice3: " << netDevice3 << ", netDevice4: " << netDevice4 << "\n";return 0;
}

③ 代理模式

报错如下:

ProxyPattern.h

#ifndef __FLYPATTERN_PATTERN_H__
#define __FLYPATTERN_PATTERN_H__#include <iostream>
#include "time.h"// 抽象主题角色
class Subject{
public:virtual void method() = 0;
};// 真实主题角色
class RealSubject : public Subject{
public:void method(){std::cout << "调用业务方法\n";       }
};// Log类
class Log{
public:void getLog(){std::cout << "调用日志\n";}
};// 代理类
class Proxy : public Subject{
public:void method(){log->getLog();realSubject->method();std::cout << "方法method()调用成功!\n";}private:std::shared_ptr<RealSubject> realSubject;std::shared_ptr<Log> log;
};#endif

main.cpp

#include <iostream>
#include "ProxyPattern.h"int main(){std::shared_ptr<Proxy> p = std::make_shared<Proxy>();p->method();return 0;
}

修改如下:

ProxyPattern.h

#ifndef __FLYPATTERN_PATTERN_H__
#define __FLYPATTERN_PATTERN_H__#include <iostream>
#include "time.h"// 抽象主题角色
class Subject{
public:virtual void method() = 0;
};// 真实主题角色
class RealSubject : public Subject{
public:void method(){std::cout << "调用业务方法\n";       }
};// Log类
class Log{
public:void getLog(){std::cout << "调用日志\n";}
};// 代理类
class Proxy : public Subject{
public:Proxy(){realSubject = std::make_shared<RealSubject>();log = std::make_shared<Log>();}void method(){log->getLog();realSubject->method();std::cout << "方法method()调用成功!\n";}private:std::shared_ptr<RealSubject> realSubject;std::shared_ptr<Log> log;
};#endif

也可修改如下:

ProxyPattern.h

#ifndef __FLYPATTERN_PATTERN_H__
#define __FLYPATTERN_PATTERN_H__#include <iostream>
#include "time.h"// 抽象主题角色
class Subject{
public:virtual void method() = 0;
};// 真实主题角色
class RealSubject : public Subject{
public:void method(){std::cout << "调用业务方法\n";       }
};// Log类
class Log{
public:void getLog(){std::cout << "调用日志\n";}
};// 代理类
class Proxy : public Subject{
public:void method(){realSubject = std::make_shared<RealSubject>();log = std::make_shared<Log>();log->getLog();        realSubject->method();std::cout << "方法method()调用成功!\n";}private:std::shared_ptr<RealSubject> realSubject;std::shared_ptr<Log> log;
};#endif

④  职责链模式

ChainOfResponsibility.h

#ifndef __CHAIN_OF_RESPONSIBILITY_PATTERN_H__
#define __CHAIN_OF_RESPONSIBILITY_PATTERN_H__#include <iostream>// 请求:票据
class Bill{
public:Bill(){}Bill(int iId, std::string iName, double iAccount){id      = iId;name    = iName;account = iAccount;}void print(){std::cout << "id: " << id << "\n";std::cout << "name: " << name << "\n";std::cout << "account: " << account << "\n\n";}double getAccount(){return account;}private:int         id;std::string name;double      account;
};// 抽象处理者
class Approver{
public:// 添加上级void setSuperior(std::shared_ptr<Approver> iApprover){superior = iApprover;}// 处理请求virtual void handleRequest(std::shared_ptr<Bill> bill) = 0;std::shared_ptr<Approver> getSuperior(){return superior;}std::string getName(){return name;}void setName(std::string iName){name = iName;}private:std::shared_ptr<Approver> superior;std::string name;
};// 具体处理者:组长
class GroupLeader : public Approver{
public:GroupLeader(){}GroupLeader(std::string iName){setName(iName);}void handleRequest(std::shared_ptr<Bill> bill){if(bill->getAccount() < 10){std::cout << "组长: " << getName() << "处理了该票据,票据信息:\n";bill->print();}else{std::cout << "组长无权处理,转交上级……\n";getSuperior()->handleRequest(bill);}}
};// 具体处理者:主管
class Head : public Approver{
public:Head(){}Head(std::string iName){setName(iName);}void handleRequest(std::shared_ptr<Bill> bill){if(bill->getAccount() >= 10 && bill->getAccount() < 30){std::cout << "主管: " << getName() << "处理了该票据,票据信息:\n";bill->print();}else{std::cout << "主管无权处理,转交上级……\n";getSuperior()->handleRequest(bill);}}
};// 具体处理者:经理
class Manager : public Approver{
public:Manager(){}Manager(std::string iName){setName(iName);}void handleRequest(std::shared_ptr<Bill> bill){if(bill->getAccount() >= 30 && bill->getAccount() < 60){std::cout << "经理: " << getName() << "处理了该票据,票据信息:\n";bill->print();}else{std::cout << "经理无权处理,转交上级……\n";getSuperior()->handleRequest(bill);}}
};// 具体处理者:老板
class Boss : public Approver{
public:Boss(){}Boss(std::string iName){setName(iName);}void handleRequest(std::shared_ptr<Bill> bill){std::cout << "老板: " << getName() << "处理了该票据,票据信息:\n";bill->print();}
};#endif

main.cpp

#include <iostream>
#include "ChainOfResponsibility.h"int main(){std::shared_ptr<GroupLeader> groupLeader = std::make_shared<GroupLeader>("孙组长");std::shared_ptr<Head> head = std::make_shared<Head>("兵主管");std::shared_ptr<Manager> manager = std::make_shared<Manager>("春经理");std::shared_ptr<Boss> boss = std::make_shared<Boss>("孙老板");// 添加上级groupLeader->setSuperior(head);head->setSuperior(manager);manager->setSuperior(boss);// 创建报销单std::shared_ptr<Bill> bill1 = std::make_shared<Bill>(1, "Jungle", 8);std::shared_ptr<Bill> bill2 = std::make_shared<Bill>(2, "Lucy", 14.4);std::shared_ptr<Bill> bill3 = std::make_shared<Bill>(3, "Jack", 32.9);std::shared_ptr<Bill> bill4 = std::make_shared<Bill>(4, "Tom", 89);// 全部先交给组长审批groupLeader->handleRequest(bill1);groupLeader->handleRequest(bill2);groupLeader->handleRequest(bill3);groupLeader->handleRequest(bill4);return 0;
}

⑤  命令模式

CommandPattern.h

#ifndef __COMMAND_PATTERN_H__
#define __COMMAND_PATTERN_H__#include <iostream>
#include <vector>// 命令队列类
#define COMMAND_QUEUE// 抽象命令类
class Command{
public:virtual void execute() = 0;
};// 接收者:电灯类
class Lamp{
public:void on(){lampState = true;std::cout << "Lamp is on\n";}void off(){lampState = false;std::cout << "Lamp is off\n";}bool getLampState(){return lampState;}private:bool lampState;
};// 接收者:风扇类
class Fan{
public:void on(){fanState = true;std::cout << "Fan is on\n";}void off(){fanState = false;std::cout << "Fan is off\n";}bool getFanState(){return fanState;}private:bool fanState;
};// 具体命令类 LampCommand
class LampCommand : public Command{
public:LampCommand(){lamp = std::make_shared<Lamp>();}void execute(){if(lamp->getLampState()){lamp->off();}else{lamp->on();}}private:std::shared_ptr<Lamp> lamp;
};// 具体命令类 FanCommand
class FanCommand : public Command{
public:FanCommand(){fan = std::make_shared<Fan>();}void execute(){if(fan->getFanState()){fan->off();}else{fan->on();}}private:std::shared_ptr<Fan> fan;
};// 调用者 Button
class Button{
public:    void setCommand(std::shared_ptr<Command> iCom){command = iCom;}void touch(){std::cout << "触摸开关:\n";command->execute();}private:std::shared_ptr<Command> command;
};#ifdef COMMAND_QUEUE// 命令队列类
class CommandQueue{
public:void addCommand(std::shared_ptr<Command> com){commandQueue.push_back(com);}void execute(){for(int i = 0; i < commandQueue.size(); i++){commandQueue[i]->execute();}}private:std::vector<std::shared_ptr<Command> > commandQueue;
};// 调用者 Button2
class Button2{
public:void setCommandQueue(std::shared_ptr<CommandQueue> iCommandQueue){commandQueue = iCommandQueue;}void touch(){std::cout << "触摸开关:\n";commandQueue->execute();}private:std::shared_ptr<CommandQueue> commandQueue;
};#endif#endif

main.cpp

#include <iostream>
#include "CommandPattern.h"int main(){// 按钮std::shared_ptr<Button> bt = std::make_shared<Button>();// 按钮控制电灯std::shared_ptr<LampCommand> lc = std::make_shared<LampCommand>();bt->setCommand(lc);bt->touch();bt->touch();bt->touch();bt->touch();std::cout << "\n\n";std::shared_ptr<FanCommand> fc = std::make_shared<FanCommand>();bt->setCommand(fc);bt->touch();bt->touch();bt->touch();bt->touch();std::cout << "-------------------------\n";#ifdef COMMAND_QUEUEstd::shared_ptr<Button2> bt2 = std::make_shared<Button2>();    std::shared_ptr<CommandQueue> cq = std::make_shared<CommandQueue>();       cq->addCommand(lc);cq->addCommand(fc);bt2->setCommandQueue(cq);bt2->touch();#endifreturn 0;
}

⑥ 备忘录模式

Memento.h

#ifndef __MEMENTO_H__
#define __MEMENTO_H__#include <iostream>class Memento{
public:Memento(){}Memento(int iVersion, std::string iDate, std::string iLabel){version = iVersion;date    = iDate;label   = iLabel;}int getVersion(){return version;}std::string getDate(){return date;}std::string getLabel(){return label;}private:int         version;std::string date;std::string label;
};#endif

Originator.h

#ifndef __CODEVERSION_H__
#define __CODEVERSION_H__#include <iostream>
#include "Memento.h"class CodeVersion{
public:CodeVersion(){version = 0;date    = "1900-01-01";label   = "none";}CodeVersion(int iVersion, std::string iDate, std::string iLabel){version = iVersion;date    = iDate;label   = iLabel;}std::shared_ptr<Memento> save(){return std::make_shared<Memento>(version, date, label);}std::shared_ptr<Memento> commit(){return std::make_shared<Memento>(version, date, label);}void restore(std::shared_ptr<Memento> mem){version = mem->getVersion();date    = mem->getDate();label   = mem->getLabel();}private:int         version;std::string date;std::string label;
};#endif

CodeManager.h

#ifndef __CODEMANAGER_H__
#define __CODEMANAGER_H__#include <iostream>
#include <vector>
#include "Memento.h"class CodeManager{
public:void commit(std::shared_ptr<Memento> mem){std::cout << "version: " << mem->getVersion() << ", date: " << mem->getDate() << ", label: " << mem->getLabel() << "\n";mementoList.push_back(mem);}std::shared_ptr<Memento> switchToPointedVersion(int index){mementoList.erase(mementoList.begin() + mementoList.size() - index, mementoList.end());return mementoList[mementoList.size() - 1];}void codeLog(){for (int i = 0; i < mementoList.size(); i++){std::cout << "version: " << mementoList[i]->getVersion() << ", date: " << mementoList[i]->getDate() << ", label: " << mementoList[i]->getLabel() << "\n";}}private:std::vector<std::shared_ptr<Memento> > mementoList;
};#endif

main.cpp

#include <iostream>
#include "Originator.h"
#include "CodeManager.h"int main(){    std::shared_ptr<CodeManager> Jungle  = std::make_shared<CodeManager>();    std::shared_ptr<CodeVersion> codeVer = std::make_shared<CodeVersion>(1001, "2019-11-03", "Initial version");  std::cout << "提交初始版本:\n";Jungle->commit(codeVer->save());  std::cout << "\n提交一个版本,增加了日志功能:\n";codeVer = std::make_shared<CodeVersion>(1002, "2019-11-04", "Add log funciton"); Jungle->commit(codeVer->save());  std::cout << "\n提交一个版本,增加了Qt图片浏览器:\n";codeVer = std::make_shared<CodeVersion>(1003, "2019-11-05", "Add Qt Image Browser"); Jungle->commit(codeVer->save());  std::cout << "\n查看提交历史\n";Jungle->codeLog();std::cout << "\n回退到上一个版本\n";codeVer->restore(Jungle->switchToPointedVersion(1));std::cout << "\n查看提交历史\n";Jungle->codeLog();return 0;
}

⑦ 策略模式

Strategy.h

#ifndef __STRATEGY_H__
#define __STRATEGY_H__#include <iostream>// 抽象策略类
class Strategy{
public:virtual void sort(int arr[], int N) = 0;
};// 具体策略:冒泡排序
class BubbleSort : public Strategy{
public:BubbleSort(){std::cout << "冒泡排序\n";}void sort(int arr[], int N){for (int i = 0; i<N; i++){for (int j = 0; j<N - i - 1; j++){if (arr[j]>arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}}
};// 具体策略:选择排序
class SelectionSort : public Strategy{
public:SelectionSort(){std::cout << "选择排序\n";}void sort(int arr[], int N){int i, j, k;for (i = 0; i<N; i++){k = i;for (j = i + 1; j<N; j++){if (arr[j] < arr[k]){k = j;}}int temp = arr[i];arr[i] = arr[k];arr[k] = temp;}}
};// 具体策略:插入排序
class InsertSort :public Strategy
{
public:InsertSort(){printf("插入排序\n");}void sort(int arr[], int N){int i, j;for (i = 1; i<N; i++){for (j = i - 1; j >= 0; j--){if (arr[i]>arr[j]){break;}}int temp = arr[i];for (int k = i - 1; k > j; k--){arr[k + 1] = arr[k];}arr[j + 1] = temp;}}
};#endif

Context.h

#include <iostream>
#include "Context.h"int main(){    std::shared_ptr<Context> ctx = std::make_shared<Context>();  int arr[] = { 10, 23, -1, 0, 300, 87, 28, 77, -32, 2 };ctx->setInput(arr, sizeof(arr) / sizeof(int));std::cout << "input: ";ctx->print();std::shared_ptr<BubbleSort> bubbleSort = std::make_shared<BubbleSort>();ctx->setSortStrategy(bubbleSort);ctx->sort();std::shared_ptr<SelectionSort> selectionSort = std::make_shared<SelectionSort>();ctx->setSortStrategy(selectionSort);ctx->sort();std::shared_ptr<InsertSort> insertSort = std::make_shared<InsertSort>();ctx->setSortStrategy(insertSort);ctx->sort();return 0;
}

main.cpp

#include <iostream>
#include "Context.h"int main(){    std::shared_ptr<Context> ctx = std::make_shared<Context>();  int arr[] = { 10, 23, -1, 0, 300, 87, 28, 77, -32, 2 };ctx->setInput(arr, sizeof(arr) / sizeof(int));std::cout << "input: ";ctx->print();std::shared_ptr<BubbleSort> bubbleSort = std::make_shared<BubbleSort>();ctx->setSortStrategy(bubbleSort);ctx->sort();std::shared_ptr<SelectionSort> selectionSort = std::make_shared<SelectionSort>();ctx->setSortStrategy(selectionSort);ctx->sort();std::shared_ptr<InsertSort> insertSort = std::make_shared<InsertSort>();ctx->setSortStrategy(insertSort);ctx->sort();return 0;
}

⑧ 模版方法模式

FingerprintModule.h

#ifndef __FINGERPRINTMODULE_H__
#define __FINGERPRINTMODULE_H__#include <iostream>// 基类
class FingerprintModule{
public:void getImage(){std::cout << "采指纹图像\n";}void output(){std::cout << "指纹图像处理完成!\n";        }virtual bool isSafeMode() = 0;virtual void processImage() = 0;// 加解密virtual void encrypt() = 0;virtual void decrypt() = 0;// 模板方法void algorithm(){// 1.采图getImage();// 2.安全模式下加密和解密if (isSafeMode()){// 2.1. 加密encrypt();// 2.2. 解密decrypt();}// 3.处理ImageprocessImage();// 4.处理结果output();}
};// 派生类A
class FingerprintModuleA : public FingerprintModule{
public:bool isSafeMode(){std::cout << "安全模式\n";     return true;}void processImage(){std::cout << "使用 第一代版本算法 处理指纹图像\n";                                    }void encrypt(){std::cout << "使用RSA密钥加密\n"; }void decrypt(){std::cout << "使用RSA密钥解密\n"; }
};// 派生类B
class FingerprintModuleB : public FingerprintModule{
public:bool isSafeMode(){std::cout << "非安全模式\n";        return false;}void processImage(){std::cout << "使用 第二代版本算法 处理指纹图像\n";                                   }void encrypt(){}void decrypt(){}
};// 派生类C
class FingerprintModuleC : public FingerprintModule{
public:bool isSafeMode(){std::cout << "安全模式\n";     return true;}void processImage(){std::cout << "使用 第一代版本算法 处理指纹图像\n";                                    }void encrypt(){std::cout << "使用DH密钥加密\n";  }void decrypt(){std::cout << "使用DH密钥解密\n";  }
};#endif

main.cpp

#include <iostream>
#include "FingerprintModule.h"int main(){    std::shared_ptr<FingerprintModuleA> fa = std::make_shared<FingerprintModuleA>();fa->algorithm();std::cout << "\n";std::shared_ptr<FingerprintModuleB> fb = std::make_shared<FingerprintModuleB>();fb->algorithm();std::cout << "\n";std::shared_ptr<FingerprintModuleC> fc = std::make_shared<FingerprintModuleC>();fc->algorithm();return 0;
}

2.cmake相关

cmake教程_乒乒乓乓丫的博客-CSDN博客

例1  开源项目GitHub - qicosmos/rest_rpc: modern C++(C++11), simple, easy to use rpc framework

若直接运行server目录下的main.cpp会报错如下:'rest_rpc.hpp' file not found

原因是各头文件的路径是按CMakeLists.txt的include_directories() 来实现的:

所以,通过cmake的方式来组织文件关系, 上面的那个server目录下的main.cpp里的#include <rest_rpc.hpp> 就相当于 #include <../../include/rest_rpc.hpp>。

同理其他的如:

通过cmake执行如下:

3.网络编程

从零开始的C++网络编程 - 知乎

【阅读】《Linux高性能服务器编程》——第五章:Linux网络编程基础API - 知乎

【C++】Web服务器项目所用到的函数详解_c++ iovec_半路杀出来的小黑同学的博客-CSDN博客

TinyWebServer——从0到服务器开发! - 知乎

① 基于socket网络编程的客户端和服务端举例

server.cpp

#include <iostream>
#include <sys/socket.h>   // socket, bind
#include <sys/errno.h>    // errno
#include <netinet/in.h>   // sockaddr_in
#include <cstring>        // bzero
#include <signal.h>       // signal
#include <unistd.h>       // close#define BUFFSIZE     2048
#define DEFAULT_PORT 16555
#define MAXLINK      2048int sockfd, connfd;       // 定义服务端套接字和客户端套接字void stopServerRunning(int){    close(sockfd);    std::cout << "Close Server\n";exit(0);
}int main(){sockaddr_in servaddr; // 结构体char buff[BUFFSIZE];  // 用于收发数据// 在Server端和Client端都有一个socket,通过将socket当作文件,可以写入也可读取sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd == -1){std::cout << "Create socket error: " << errno << ", :" << strerror(errno) << "\n";return -1;}// void bzero(void* s, int n);//s为内存(字符串)指针,n为需要清零的字节数。bzero(&servaddr, sizeof(servaddr));// sin_family指代协议族,在socket编程中只能是AF_INETservaddr.sin_family = AF_INET;      // sin_addr存储IP地址,使用in_addr这个数据结构(in_addr.s_addr就是32位IP地址),按照网络字节顺序存储IP地址// htonl(Host to Network Long)把本机字节顺序转化为网络字节顺序,htons(Host to Network Short)// INADDR_ANY转换过来就是0.0.0.0,泛指本机的意思,也就是表示本机的所有IPservaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(DEFAULT_PORT);      // bind用来给socket绑定地址信息  // 通常服务器在启动时会绑定一个总所周知的地址(ip地址+端口号),客户端不用指定系统自动分配,// 所以通常服务端在listen之前要调用bind(),而客户端不会调用,在connect()时由系统随机生成一个。if(bind(sockfd, (sockaddr*)&servaddr, sizeof(servaddr)) == -1){std::cout << "Bind error: " << errno << ", :" << strerror(errno) << "\n";return -1;}if(listen(sockfd, MAXLINK) == -1){std::cout << "Listen error: " << errno << ", :" << strerror(errno) << "\n";return -1;}std::cout << "Listening...\n";while (true){// 这句用于在输入Ctrl+C的时候关闭服务器signal(SIGINT, stopServerRunning);     connfd = accept(sockfd, nullptr, nullptr);   if(connfd == -1){std::cout << "Accept error: " << errno << ", :" << strerror(errno) << "\n";return -1;}bzero(buff, BUFFSIZE);recv(connfd, buff, BUFFSIZE - 1, 0);std::cout << "Recv: " << buff << "\n";send(connfd, buff, strlen(buff), 0);close(connfd);}    return 0;
}

client.cpp

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>        // sockaddr_in
#include <arpa/inet.h>         // inet_pton
#include <unistd.h>            // close#define BUFFSIZE    2048
#define SERVER_IP   "0.0.0.0"  // 指定服务端的IP,记得修改为你的服务端所在的ip
#define SERVER_PORT 16555      // 指定服务端的portint main(){sockaddr_in servaddr;char buff[BUFFSIZE];int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd == -1){std::cout << "Create socket error: " << errno << ", :" << strerror(errno) << "\n";return -1;}bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;   // IP地址转换函数: 将点分十进制的ip地址转化为用于网络传输的数值格式inet_pton(AF_INET, SERVER_IP, &servaddr.sin_addr);servaddr.sin_port = htons(SERVER_PORT);  if(connect(sockfd, (sockaddr*)&servaddr, sizeof(servaddr))){std::cout << "Connect socket error: " << errno << ", :" << strerror(errno) << "\n";return -1;}std::cout << "Please input: \n";scanf("%s", buff);send(sockfd, buff, strlen(buff), 0);bzero(buff, sizeof(buff));recv(sockfd, buff, BUFFSIZE - 1, 0);std::cout << "Recv: " << buff << "\n";close(sockfd);return 0;
}

其中解决地址已占用问题:

通信效果如下:

② 30天自制C++服务器学习

GitHub - yuesong-feng/30dayMakeCppServer: 30天自制C++服务器,包含教程和源代码

(1)从一个最简单的socket开始

server.cpp

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in serv_addr;bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");serv_addr.sin_port = htons(8888);bind(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr));listen(sockfd, SOMAXCONN);struct sockaddr_in clnt_addr;socklen_t clnt_addr_len = sizeof(clnt_addr);bzero(&clnt_addr, sizeof(clnt_addr));int clnt_sockfd = accept(sockfd, (sockaddr*)&clnt_addr, &clnt_addr_len);printf("new client fd %d! IP: %s Port: %d\n", clnt_sockfd, inet_ntoa(clnt_addr.sin_addr), ntohs(clnt_addr.sin_port));return 0;
}

client.cpp

#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in serv_addr;bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");serv_addr.sin_port = htons(8888);//bind(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)); 客户端不进行bind操作connect(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr));    return 0;
}

至此,day01的教程已经结束了,进入code/day01文件夹,使用make命令编译,将会得到serverclient。输入命令./server开始运行,直到accept函数,程序阻塞、等待客户端连接。然后在一个新终端输入命令./client运行客户端,可以看到服务器接收到了客户端的连接请求,并成功连接。

new client fd 3! IP: 127.0.0.1 Port: 53505

但如果我们先运行客户端、后运行服务器,在客户端一侧无任何区别,却并没有连接服务器成功,因为我们day01的程序没有任何的错误处理。

事实上对于如socket,bind,listen,accept,connect等函数,通过返回值以及errno可以确定程序运行的状态、是否发生错误。在day02的教程中,我们会进一步完善整个服务器,处理所有可能的错误,并实现一个echo服务器(客户端发送给服务器一个字符串,服务器收到后返回相同的内容)。

(2)不要放过任何一个错误

util.h

#ifndef UTIL_H
#define UTIL_Hvoid errif(bool, const char*);#endif

util.cpp

#include "util.h"
#include <stdio.h>
#include <stdlib.h>void errif(bool condition, const char *errmsg){if(condition){perror(errmsg);exit(EXIT_FAILURE);}
}

server.cpp

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include "util.h"int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);errif(sockfd == -1, "socket create error");struct sockaddr_in serv_addr;bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");serv_addr.sin_port = htons(8888);errif(bind(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) == -1, "socket bind error");errif(listen(sockfd, SOMAXCONN) == -1, "socket listen error");struct sockaddr_in clnt_addr;socklen_t clnt_addr_len = sizeof(clnt_addr);bzero(&clnt_addr, sizeof(clnt_addr));int clnt_sockfd = accept(sockfd, (sockaddr*)&clnt_addr, &clnt_addr_len);errif(clnt_sockfd == -1, "socket accept error");printf("new client fd %d! IP: %s Port: %d\n", clnt_sockfd, inet_ntoa(clnt_addr.sin_addr), ntohs(clnt_addr.sin_port));while (true) {char buf[1024];bzero(&buf, sizeof(buf));ssize_t read_bytes = read(clnt_sockfd, buf, sizeof(buf));if(read_bytes > 0){printf("message from client fd %d: %s\n", clnt_sockfd, buf);write(clnt_sockfd, buf, sizeof(buf));} else if(read_bytes == 0){printf("client fd %d disconnected\n", clnt_sockfd);close(clnt_sockfd);break;} else if(read_bytes == -1){close(clnt_sockfd);errif(true, "socket read error");}}close(sockfd);return 0;
}

client.cpp

#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include "util.h"int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);errif(sockfd == -1, "socket create error");struct sockaddr_in serv_addr;bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");serv_addr.sin_port = htons(8888);errif(connect(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) == -1, "socket connect error");while(true){char buf[1024];bzero(&buf, sizeof(buf));scanf("%s", buf);ssize_t write_bytes = write(sockfd, buf, sizeof(buf));if(write_bytes == -1){printf("socket already disconnected, can't write any more!\n");break;}bzero(&buf, sizeof(buf));ssize_t read_bytes = read(sockfd, buf, sizeof(buf));if(read_bytes > 0){printf("message from server: %s\n", buf);}else if(read_bytes == 0){printf("server socket disconnected!\n");break;}else if(read_bytes == -1){close(sockfd);errif(true, "socket read error");}}close(sockfd);return 0;
}

至此,我们已经完整地开发了一个echo服务器,并且有最基本的错误处理!

但现在,我们的服务器只能处理一个客户端,我们可以试试两个客户端同时连接服务器,看程序将会如何运行。在day03的教程里,我们将会讲解Linux系统高并发的基石--epoll,并编程实现一个可以支持无数客户端同时连接的echo服务器!

(3)高并发还得用epoll

server.cpp

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <errno.h>
#include "util.h"#define MAX_EVENTS  1024
#define READ_BUFFER 1024// 设置非阻塞
void setnonblocking(int fd){// 设置文件的flags: fcntl(fd,F_SETFL,flags);     fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
}int main(){int sockfd = socket(AF_INET, SOCK_STREAM, 0);errif(sockfd == -1, "socket create error");struct sockaddr_in serv_addr;bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");serv_addr.sin_port = htons(8888);errif(bind(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) == -1, "socket bind error");errif(listen(sockfd, SOMAXCONN) == -1, "socket listen error");// 创建一个epoll文件描述符并返回,失败则返回-1int epfd = epoll_create1(0);errif(epfd == -1, "epoll create error");struct epoll_event events[MAX_EVENTS], ev;bzero(&events, sizeof(events));bzero(&ev, sizeof(ev));ev.data.fd = sockfd;                                        // 该IO口为服务器socket fd(文件描述符)// EPOLLIN:LT模式,EPOLLET:ET模式ev.events  = EPOLLIN;                                       // 服务端最好不要用ET模式setnonblocking(sockfd);// 将服务器socket fd添加到epollepoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);while (1){// epoll_wait获取有事件发生的fd      // 最大等待时间,设置为-1表示一直等待  int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);    // 有nfds个fd发生事件errif(nfds == -1, "epoll wait error");for(int i = 0; i < nfds; ++i){if(events[i].data.fd == sockfd){                    // 发生事件的fd是服务器socket fd,表示有新客户端连接struct sockaddr_in clnt_addr;bzero(&clnt_addr, sizeof(clnt_addr));socklen_t clnt_addr_len = sizeof(clnt_addr);int clnt_sockfd = accept(sockfd, (sockaddr*)&clnt_addr, &clnt_addr_len);errif(clnt_sockfd == -1, "socket accept error");printf("new client fd %d! IP: %s Port: %d\n", clnt_sockfd, inet_ntoa(clnt_addr.sin_addr), ntohs(clnt_addr.sin_port));bzero(&ev, sizeof(ev));ev.data.fd = clnt_sockfd;ev.events = EPOLLIN | EPOLLET;                   // 对于客户端连接,使用ET模式,可以让epoll更加高效,支持更多并发setnonblocking(clnt_sockfd);                     // ET需要搭配非阻塞式socket使用epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sockfd, &ev);}else if (events[i].events & EPOLLIN){               // 发生事件的是客户端,并且是可读事件(EPOLLIN)char buf[READ_BUFFER];while(true){                                     //由于使用非阻塞IO,读取客户端buffer,一次读取buf大小数据,直到全部读取完毕bzero(&buf, sizeof(buf));ssize_t bytes_read = read(events[i].data.fd, buf, sizeof(buf));if(bytes_read > 0){// 保存读取到的bytes_read大小的数据printf("message from client fd %d: %s\n", events[i].data.fd, buf);write(events[i].data.fd, buf, sizeof(buf));}else if(bytes_read == -1 && errno == EINTR){ // 客户端正常中断、继续读取printf("continue reading");continue;}else if(bytes_read == -1 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))){ // 非阻塞IO,这个条件表示数据全部读取完毕printf("finish reading once, errno: %d\n", errno);break;}else if(bytes_read == 0){                    // EOF,客户端断开连接printf("EOF, client fd %d disconnected\n", events[i].data.fd);close(events[i].data.fd);                 // 关闭socket会自动将文件描述符从epoll树上移除break;}}}else{ //其他事件,之后的版本实现printf("something else happened\n");}            }}close(sockfd);return 0;
}

client.cpp

#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include "util.h"#define BUFFER_SIZE 1024 int main(){int sockfd = socket(AF_INET, SOCK_STREAM, 0);errif(sockfd == -1, "socket create error");struct sockaddr_in serv_addr;bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");serv_addr.sin_port = htons(8888);errif(connect(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) == -1, "socket connect error");while (1){char buf[BUFFER_SIZE];  //在这个版本,buf大小必须大于或等于服务器端buf大小,不然会出错,想想为什么?bzero(&buf, sizeof(buf));scanf("%s", buf);ssize_t write_bytes = write(sockfd, buf, sizeof(buf));if(write_bytes == -1){printf("socket already disconnected, can't write any more!\n");break;}bzero(&buf, sizeof(buf));ssize_t read_bytes = read(sockfd, buf, sizeof(buf));if(read_bytes > 0){printf("message from server: %s\n", buf);}else if(read_bytes == 0){printf("server socket disconnected!\n");break;}else if(read_bytes == -1){close(sockfd);errif(true, "socket read error");}}    close(sockfd);return 0;
}

(4)来看看我们的第一个类

InetAddress.h

#pragma once#include <iostream>
#include <arpa/inet.h>class InetAddress{
public:InetAddress();InetAddress(std::string ip, uint16_t port);struct sockaddr_in getAddr(){return addr;}socklen_t getAddrLen(){return addr_len;}private:struct sockaddr_in addr;socklen_t addr_len;
};

InetAddress.cpp

#include <iostream>
#include<string.h>#include "InetAddress.h"InetAddress::InetAddress() : addr_len(sizeof(addr)){bzero(&addr, sizeof(addr));
}InetAddress::InetAddress(std::string ip, uint16_t port) : addr_len(sizeof(addr)){bzero(&addr, sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr(ip.c_str());addr.sin_port = htons(port);
}

Socket.h

#pragma once#include <iostream>
#include <tr1/memory>#include "InetAddress.h"class Socket{
public:Socket();    Socket(int);  ~Socket();  void bind(InetAddress*);void listen();void setnonblocking();int accept(InetAddress*);int getFd();private:int fd; // 文件描述符
};

Socket.cpp

#include "Socket.h"
#include "InetAddress.h"
#include "util.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <iostream>
#include <tr1/memory>Socket::Socket() : fd(-1){fd = socket(AF_INET, SOCK_STREAM, 0);errif(fd == -1, "socket create error");
}Socket::Socket(int fd_) : fd(fd_){errif(fd == -1, "socket create error");
}Socket::~Socket(){if(fd != -1){close(fd);fd = -1;}
}void Socket::bind(InetAddress *addr){struct sockaddr_in addrTmp = addr->getAddr();socklen_t addrLen = addr->getAddrLen();errif(::bind(fd, (sockaddr*)&addrTmp, addrLen) == -1, "socket bind error");
}void Socket::listen(){errif(::listen(fd, SOMAXCONN) == -1, "socket listen error");
}void Socket::setnonblocking(){fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
}int Socket::accept(InetAddress *addr){struct sockaddr_in addrTmp = addr->getAddr();socklen_t addrLen = addr->getAddrLen();int clnt_sockfd = ::accept(fd, (sockaddr*)&addrTmp, &addrLen);errif(clnt_sockfd == -1, "socket accept error");return clnt_sockfd;
}int Socket::getFd(){return fd;
}

Epoll.h

#pragma once#include <sys/epoll.h>
#include <iostream>
#include <vector>class Epoll{
public:Epoll();~Epoll();void addFd(int fd, uint32_t op);std::vector<epoll_event> poll(int timeout = -1);private:int epfd;epoll_event* events;
};

Epoll.cpp

#include "Epoll.h"
#include "util.h"
#include <unistd.h>
#include <string.h>
#include <iostream>#define MAX_EVENTS 1000Epoll::Epoll() : epfd(-1), events(nullptr){epfd = epoll_create1(0);errif(epfd == -1, "epoll create error");events = new epoll_event[MAX_EVENTS];bzero(events, sizeof(*events) * MAX_EVENTS);
} Epoll::~Epoll(){if(epfd != -1){close(epfd);epfd = -1;}delete[] events; // 防止内存泄漏
}void Epoll::addFd(int fd, uint32_t op){struct epoll_event ev;bzero(&ev, sizeof(ev));ev.data.fd = fd;ev.events = op;errif(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1, "epoll add event error");
}std::vector<epoll_event> Epoll::poll(int timeout){std::vector<epoll_event> activeEvents;int nfds = epoll_wait(epfd, events, MAX_EVENTS, timeout);errif(nfds == -1, "epoll wait error");for(int i = 0; i < nfds; ++i){activeEvents.push_back(events[i]);}return activeEvents;
}

server.cpp

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <vector>
#include "util.h"
#include "Epoll.h"
#include "InetAddress.h"
#include "Socket.h"
#include <iostream>#define MAX_EVENTS 1024
#define READ_BUFFER 1024void setnonblocking(int fd){fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
}void handleReadEvent(int);int main() {Socket *serv_sock = new Socket();InetAddress *serv_addr = new InetAddress("127.0.0.1", 8888);serv_sock->bind(serv_addr);serv_sock->listen();    Epoll *ep = new Epoll();serv_sock->setnonblocking();ep->addFd(serv_sock->getFd(), EPOLLIN | EPOLLET);while(true){std::vector<epoll_event> events = ep->poll();int nfds = events.size();for(int i = 0; i < nfds; ++i){if(events[i].data.fd == serv_sock->getFd()){  // 新客户端连接// new InetAddress() 位置只能在这里!InetAddress *clnt_addr = new InetAddress();      Socket *clnt_sock = new Socket(serv_sock->accept(clnt_addr));       printf("new client fd %d! IP: %s Port: %d\n", clnt_sock->getFd(), inet_ntoa(clnt_addr->getAddr().sin_addr), ntohs(clnt_addr->getAddr().sin_port));clnt_sock->setnonblocking();ep->addFd(clnt_sock->getFd(), EPOLLIN | EPOLLET);} else if(events[i].events & EPOLLIN){       // 可读事件handleReadEvent(events[i].data.fd);} else{  // 其他事件,之后的版本实现printf("something else happened\n");}}}delete serv_sock;delete serv_addr;delete ep;return 0;
}void handleReadEvent(int sockfd){char buf[READ_BUFFER];while (1){                       // 由于使用非阻塞IO,读取客户端buffer,一次读取buf大小数据,直到全部读取完毕bzero(&buf, sizeof(buf));ssize_t bytes_read = read(sockfd, buf, sizeof(buf));if(bytes_read > 0){printf("message from client fd %d: %s\n", sockfd, buf);write(sockfd, buf, sizeof(buf));} else if(bytes_read == -1 && errno == EINTR){  // 客户端正常中断、继续读取printf("continue reading");continue;} else if(bytes_read == -1 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))){ // 非阻塞IO,这个条件表示数据全部读取完毕printf("finish reading once, errno: %d\n", errno);break;} else if(bytes_read == 0){  // EOF,客户端断开连接printf("EOF, client fd %d disconnected\n", sockfd);close(sockfd);           // 关闭socket会自动将文件描述符从epoll树上移除break;}}
}

client.cpp

#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include "util.h"#define BUFFER_SIZE 1024 int main(){int sockfd = socket(AF_INET, SOCK_STREAM, 0);errif(sockfd == -1, "socket create error");struct sockaddr_in serv_addr;bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");serv_addr.sin_port = htons(8888);errif(connect(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) == -1, "socket connect error");while (1){char buf[BUFFER_SIZE];  //在这个版本,buf大小必须大于或等于服务器端buf大小,不然会出错,想想为什么?bzero(&buf, sizeof(buf));scanf("%s", buf);ssize_t write_bytes = write(sockfd, buf, sizeof(buf));if(write_bytes == -1){printf("socket already disconnected, can't write any more!\n");break;}bzero(&buf, sizeof(buf));ssize_t read_bytes = read(sockfd, buf, sizeof(buf));if(read_bytes > 0){printf("message from server: %s\n", buf);}else if(read_bytes == 0){printf("server socket disconnected!\n");break;}else if(read_bytes == -1){close(sockfd);errif(true, "socket read error");}}    close(sockfd);return 0;
}
g++ util.cpp client.cpp -o client && \
g++ util.cpp server.cpp Epoll.cpp InetAddress.cpp Socket.cpp -o server

至此,我们已经完整地开发了一个echo服务器,并且引入面向对象编程的思想,初步封装了SocketInetAddressEpoll,大大精简了主程序,隐藏了底层语言实现细节、增加了可读性。

c++ 常用总结(三)相关推荐

  1. ASP.NET程序中常用的三十三种代码(转载)

    asp.net程序中最常用的三十三种编程代码,为初学者多多积累经验,为高手们归纳总结,看了觉得很有价值~,大家不妨参考下! 1. 打开新的窗口并传送参数: 传送参数: response.write(& ...

  2. Linux Shell常用技巧(三) sed

    Linux Shell常用技巧(三) sed 八.流编辑器sed 8.1 sed简介 sed是stream editor的缩写,一种流编辑器,它一次处理一行内容.处理时,把当前处理的行存储在临时缓冲区 ...

  3. 常用的三种类别的IP地址

    文章目录 常用的三种类别的IP地址 A类地址 B类地址 C类地址 常用的三种类别的IP地址 A类地址 A类地址的网络号字段占1个字节,只有7位可供使用(该字段的第一位已固定为0),但可指派的网络号是1 ...

  4. 统计内表行数常用的三种方法

    以下是统计内表行数常用的三种方法, 如下:   1.LOOP AT it_itab.    g_lines2 = g_lines2 + 1.    ENDLOOP.    该方法是通过循环内部表自己累 ...

  5. java json的使用方法_JAVA编写JSON常用的三种方法

    JAVA编写JSON常用的三种方法 Szx • 2019 年 11 月 15 日 1.jsonlib需要引入的包也多,代码也相对多一些. 2.Gson:谷歌的 3.FastJson:阿里巴巴的,这几个 ...

  6. 10_InfluxDB常用函数(三)变换类函数(DERIVATIVE, DIFFERENCE,ELAPSED,MOVING_AVERAGE,NON_NEGATIVE_DERIVATIVE)等

    10.InfluxDB学习之InfluxDB常用函数(三)变换类函数 转自:https://www.yisu.com/zixun/36847.html 10.1.DERIVATIVE()函数 作用:返 ...

  7. ASP.NET 程序中常用的三十三种代码(1)

    ASP.NET 程序中常用的三十三种代码(1) 1. 打开新的窗口并传送参数: 传送参数: response.write("<script>window.open('*.ASPx ...

  8. 使用php下载网络图片有哪些方法,php下载网络图片常用的三个方法总结

    下面来讲利用PHP下载网络图片常用的三个方法.当然,这三个方法都不是万能的,有时候会因为网络问题,或者网络图片的权限问题(大站一般都有防盗链)导致下载失败. 一起来看看吧,实际工作中经常用到. 方法一 ...

  9. 常用类 (三) ----- BigDecimal和BigInteger大数类

    相关文章: <常用类 (一) ----- Arrays数组工具类> <常用类 (二) ----- Math类> <常用类 (三) ----- BigDecimal和Big ...

  10. 使用php下载网络图片有哪些方法,php下载网络图片常用的三个方法总结_后端开发...

    php文件锁产生的问题和解决方案(一个真实案例)_后端开发 一个真实案例想起自己之前犯过一个相关的错误.当时场景是这样的:有一个微信公众号项目,调用微信公众号的接口都需要access_token,它的 ...

最新文章

  1. 人工智能领域的经典著作!
  2. .NET平台开源项目速览(8)Expression Evaluator表达式计算组件使用
  3. 使用ELK 搭建core文件展示平台
  4. 这样的阅读工具,人手一个不过分吧?
  5. 工程搭建:搭建子工程之分布式id生成器
  6. Log4Net简单使用
  7. 今天英雄联盟服务器维护要到好久,lol今天维护时间是多久 lol维护公告2020最新...
  8. 洗车房,无人车的噩梦
  9. python最新技术开锁工具_Python 自动化库介绍 PySimpleGUI
  10. 文字和图片放一起, 文字下沉的处理方法
  11. 图解DotNet框架之三:System.IO
  12. 【渝粤题库】广东开放大学 质量管理 形成性考核
  13. 电子电路基础 (11)——反馈、偏置与多级放大电路原理分析
  14. 供应链三道防线(读书笔记)4(共4)
  15. 教你如何用通道来快速抠图的方法
  16. enigma机的原理
  17. 走进Cocos Creator游戏开发(第一篇)
  18. import clip时Cannot re-initialize CUDA in forked subprocess
  19. Intriguing properties of neural networks手动翻译
  20. 《中国垒球协会》:新春贺词

热门文章

  1. 用Moment.js 计算两个时间直接的间隔
  2. 成功靠奋斗,奋斗靠坚持
  3. 万维钢:我怎样管理信息
  4. python中rgb颜色_自定义RGB颜色与Python诅咒
  5. npm run dev命令报错解决方式
  6. Docker容器之cgroup搭建
  7. 联想第二季度业绩创纪录 所有业务实现强劲增长
  8. 图片转成pdf格式怎么转?三个好用的方法教给你
  9. 学校校车运营各项安全管理制度_学校校车安全管理的规章制度
  10. 高级驾驶辅助系统各子系统一览