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";
// 指定具体建造者B
std::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 6
void* 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_t
pthread_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_create
tids[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_t
pthread_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品牌的手机,想玩游戏A
std::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想在这个手机上玩游戏B
std::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获取一个hub
std::shared_ptr<NetDevice> netDevice1 = netDeviceFactory->getNetDevice('H');
netDevice1->print(1);
// 客户端2获取一个hub
std::shared_ptr<NetDevice> netDevice2 = netDeviceFactory->getNetDevice('H');
netDevice2->print(2);
std::cout << "判断两个hub是否是同一个:\n";
std::cout << "netDevice1: " << netDevice1 << ", netDevice2: " << netDevice2 << "\n\n\n";
// 客户端3获取一个switch
std::shared_ptr<NetDevice> netDevice3 = netDeviceFactory->getNetDevice('S');
netDevice3->print(1);
// 客户端4获取一个switch
std::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_QUEUE
std::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();
#endif
return 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.处理Image
processImage();
// 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 2048
int 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_INET
servaddr.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,泛指本机的意思,也就是表示本机的所有IP
servaddr.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 // 指定服务端的port
int 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命令编译,将会得到server
和client
。输入命令./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_H
void 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文件描述符并返回,失败则返回-1
int 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添加到epoll
epoll_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 1000
Epoll::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 1024
void 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服务器,并且引入面向对象编程的思想,初步封装了Socket
、InetAddress
和Epoll
,大大精简了主程序,隐藏了底层语言实现细节、增加了可读性。