c++ 设计模式 的课本范例(中)

(10)单例模式 singleton 。整个应用程序执行时,只有一个单例模式的对象。

class GameConfig    // 懒汉式,啥时候用单例对象,啥时候创建。
{
private:static GameConfig* ptrGameConfig;  // 这些函数都作为私有函数,不让外界调用GameConfig() {}~GameConfig() {}GameConfig(const GameConfig&) {}GameConfig& operator= (const GameConfig&) {}
public:static GameConfig* getGameConfig() {if (ptrGameConfig)  // 本代码非线程安全,需要在  main 主线程中,在创建子线程之前,创建单例模式的对象ptrGameConfig = new GameConfig();return ptrGameConfig;}
};
GameConfig* GameConfig::ptrGameConfig = nullptr;int main()
{auto ptr = GameConfig::getGameConfig();return 0;
}

下面介绍饿汉式的单例对象生成:

class GameConfig   // 饿汉式,在程序启动时候,在 main 函数执行前,就完成对 单例对象的初始化与创建
{
private:static GameConfig* ptrGameConfig;  // 这些函数都作为私有函数,不让外界调用GameConfig() {}~GameConfig() {}GameConfig(const GameConfig&) {}GameConfig& operator= (const GameConfig&) {}
public:static GameConfig* getGameConfig() { return ptrGameConfig;	}
};
GameConfig* GameConfig::ptrGameConfig = new GameConfig();  // 此时可以调用私有的单例模式的构造函数int main()
{auto ptr = GameConfig::getGameConfig();return 0;
}

若有其它静态全局变量的初始化引用了单例对象的数据,可能会导致错误。因为各个 cpp 文件的全局静态变量的初始化的顺序是不太确定的。

以上的单例模式返回的都是单例对象的指针。可以改为返回单例对象的引用,用引用比指针更符合 c++ 的风格,且可以少起变量名:

class GameConfig  
{
private:GameConfig() {}    // 这些函数都作为私有函数,不让外界调用~GameConfig() { }GameConfig(const GameConfig&) {}GameConfig& operator= (const GameConfig&) {}
public:static GameConfig& getGameConfig() { static GameConfig gameConfig;return gameConfig;}
};int main()
{auto& gameConfig = GameConfig::getGameConfig();   // 直接使用 auto 是值复制, auto & 才是创建引用。return 0;
}

用 MFC 框架测试不显式析构单例模式的对象,是否会造成内存泄露。发现不会。因为 这些 static 静态对象是在 main 函数之前就构建出来的,静态对象的析构与回收由 main 函数结束后由系统回收。也就不存在内存泄露。 MFC 框架测试的内存泄露应该指的是在 main 函数结束前,申请的内存没有显式释放,才叫内存泄露,贴图如下,无论使用的是静态单例模式对象的指针还是引用,都没提示内存泄露:

在这里插入图片描述

(11)外观模式 fasade 。用于隔离类与类,代码与代码之间的强耦合。比如设置游戏环境:图形、声音、特效。设置会很繁琐,细致。这就是代码里的强耦合。可以在游戏界面里提供两个按钮:高配模式与低配模式。由这两个模式自行进行所有的游戏环境设置。这高配低配按钮的实现,就是外观模式。词典翻译是 fasade 正面;(尤指虚假的)外表,表面,外观。个人感觉表面模式更合适。表面,区别于内核,由表面联接与内核打交道。

class Graphic
{
private:Graphic() {}~Graphic() {}Graphic(const Graphic&) {}Graphic& operator=(const Graphic&) {}
public:static Graphic&  getGraphic(){static Graphic gra;return gra;}void effects_mode(bool b)  // 是否开启特效{if (b) cout << "开启高特效\n\n";else   cout << "不开启高特效\n\n";}void resolution(int i , int j)	{	cout << "分辨率 :" << i << " * " << j << "\n\n";	}
};class Sound
{
private:Sound() {}~Sound() {}Sound(const Sound&) {}Sound& operator=(const Sound&) {}
public:static Sound&  getSound(){static Sound sound;return sound;}void bgSound(bool b)     // 是否开启背景音{if (b) cout << "开启背景音\n\n";else   cout << "不开启背景音\n\n";}void envirSound(bool b)  // 是否开启环境音效{if (b) cout << "开启环境音效\n\n";else   cout << "不开启环境音效\n\n";}
};class Facade // 外观类,用于隔离类之间的强耦合
{
private:Facade() {}~Facade() {}Facade(const Facade&) {}Facade& operator=(const Facade&) {}
public:static Facade& getFacade(){static Facade facade;return facade;}void config(bool high){auto& graphic = Graphic::getGraphic();auto& sound = Sound::getSound();if (high){graphic.effects_mode(true);graphic.resolution(1920 , 1080);sound.bgSound(true);sound.envirSound(true);}else{graphic.effects_mode(false);graphic.resolution(1366, 800);sound.bgSound(false);sound.envirSound(false);}}};int main()
{auto& fasade = Facade::getFacade();fasade.config(true);cout << "----------------------\n\n";fasade.config(false);return 0;
}

测试结果如下:

在这里插入图片描述

(12)命令模式 Command 。例如 wps 软件的菜单,由一个个命令组成。把这些执行了的命令,集合到容器里,如 list 双向链表。就可以编写日志,或者支持了撤销操作, undo 。

class Cook
{
public:void fish() { cout << "厨师做了鱼\n\n"; }void beef() { cout << "厨师做了牛肉\n\n"; }
};class Command    // 封装命令
{
protected: Cook* pCook;
public:Command( Cook* p) :pCook(p) {}virtual ~Command() {}virtual void excute() = 0;
};class Command_Fish : public Command  // 做鱼命令
{
public:Command_Fish( Cook* p) : Command(p) {}void excute() override { this->pCook->fish(); }
};class Command_Beef : public Command   // 做牛肉命令
{
public:Command_Beef(Cook* p) : Command(p) {}void excute() override { this->pCook->beef(); }
};class Waiter              // 用服务员角色一次可以执行很多命令
{
private: list<Command*> listCommand;
public:void addCommand(Command* p)    { listCommand.push_back(p); }void deleteCommand(Command* p) { listCommand.remove(p); }void excute() {for (auto iter = listCommand.begin(); iter != listCommand.end(); iter++)(*iter)->excute();}
};int main()
{Cook cook;Command_Fish cmdFish(&cook);Command_Beef cmdBeef(&cook);Waiter waiter;waiter.addCommand(&cmdFish);waiter.addCommand(&cmdBeef);waiter.excute();return 0;
}

测试结果如下:

在这里插入图片描述

(13)迭代器模式 itertor 。主要适用于对容器的操作,定义了迭代器模式,以对容器进行增删改查的操作,同时要良好的组织这些数据结构,以实现高效。目前迭代器模式用的不多了,因为 STL 标准库定义了各种容器及其迭代器。库大师们的顶尖的容器设计,我们直接用就可以了。

for ( auto iter = vector . begin() ; iter != vector . end() ; iter++ )auto temp = * iter ;

以上代码显示了定义一个迭代器时应具有的最小功能: begin() 函数返回容器的头部, end() 函数返回容器的尾部 , 迭代器要支持 自加
的运算, * 运算符返回迭代器指向的容器中的元素。从而由这些函数配合完成对容器的遍历操作。即使以后要咱们自己写迭代器,也应该模仿库大师们的代码比如用模板方式定义容器及其迭代器。

(14) 组合模式 compasite 。 对于文件系统,一个目录可以包含文件与目录,子目录里又可以包含新的文件与目录。为了描述和组织这种树型的数据结构,编码的方式叫做组合模式,其实更确切的叫法应该叫树型模式。

先给出第一版代码与测试结果:

class File
{
private:string fileName;
public:File(const string& s) : fileName(s) {}void showName(const string& indentStr) { cout << indentStr << "-" << fileName << '\n'; }  // indent : 缩进
};class Directory
{
private:string dirName;list<File*> listFile;list<Directory*> listDirectory;
public:Directory(const string& s) : dirName(s) {}void addFile(File* f) { listFile.push_back(f); }void addDir(Directory* d) { listDirectory.push_back(d); }void showName(const string& indentStr){cout << indentStr << '+' << dirName << '\n';string newIndentStr = indentStr + "    ";for (auto iter = listFile.begin(); iter != listFile.end(); iter++)(*iter)->showName(newIndentStr);for (auto iter = listDirectory.begin(); iter != listDirectory.end(); iter++)(*iter)->showName(newIndentStr);}
};int main()
{File h("h"), i("i") , e("e") , f("f") , b("b") , c("c");Directory g("G") , d("D") , a("A");g.addFile(&h);g.addFile(&i);d.addFile(&e);d.addFile(&f);a.addFile(&b);a.addFile(&c);a.addDir(&d);a.addDir(&g);a.showName("");return 0;
}

测试结果如下,即为图中的目录进行了编码:

在这里插入图片描述

以上版本把 文件和目录作为了两种类型。其实可以把其视为一种类型,用类的继承与多态来实现,由此得到组合模式的第二版(例子同上):

class FileSystem // 把文件类型与目录类型视为同一种类型
{
public:virtual ~FileSystem() {}virtual void showName(int indentNum) = 0;virtual int addFile(FileSystem* f) = 0;  // 返回值 0 表示正确给目录增删了文件, 返回 -1 表不应当对文件类型进行目录的增删操作virtual int addDir(FileSystem* d) = 0;
};class File : public FileSystem
{
private:string fileName;
public:File(const string& s) : fileName(s) {}virtual int addFile(FileSystem* f) { return -1; }virtual int addDir(FileSystem* d) { return -1; }void showName(int indentNum)   // indent : 缩进{for (int i = 0; i < indentNum; i++)cout << "    ";cout << "-" << fileName << '\n';}
};class Directory : public FileSystem
{
private:string dirName;list<FileSystem*> listFS;
public:Directory(const string& s) : dirName(s) {}int addFile(FileSystem* f) { listFS.push_back(f); return 0; }int addDir(FileSystem* d) { listFS.push_back(d); return 0; }void showName(int indentNum) override{for (int i = 0; i < indentNum; i++)cout << "    ";cout << "+" << dirName << '\n';indentNum++;for (auto iter = listFS.begin(); iter != listFS.end(); iter++)(*iter)->showName(indentNum);}
};int main()
{File h("h"), i("i") , e("e") , f("f") , b("b") , c("c");Directory g("G") , d("D") , a("A");g.addFile(&h);g.addFile(&i);d.addFile(&e);d.addFile(&f);a.addFile(&b);a.addFile(&c);a.addDir(&d);a.addDir(&g);a.showName(0);return 0;
}

(15) 状态模式 state。用以解决以下场景:在编译原理中,根据一门语言的语法,编写出有限状态机。对代码的编译过程,实际始终是在该有限状态机的几个状态上跳转。这几个状态,足以满足用该门语法编写的所有代码情形。或者游戏里,打怪物。怪物受伤后的状态,始终就那几种,有章可循。给出课本代码,写在一个页面了,为了简洁,突出整体。没有拆分成头文件与 cpp 文件。

class Monster;class Status   // 状态的基类
{
public:virtual ~Status() {}virtual void attacked(int power, Monster* ptrMonstor) = 0;
};class Status_Violent : public Status  // 出于编译原理的从上往下编译,只能把包含大量符号的函数体下移。否则总提示符号找不到
{
public:virtual void attacked(int power, Monster* ptrMonstor);static Status* getStatusObj();
};class Monster
{
private:int life;Status* pStatus;  // 这里原先使用了左值引用,但不好使,总因为 是否具有 const 属性报错,改成课本上的指针了。
public:Monster() : life(500), pStatus(Status_Violent::getStatusObj()) {}  // 新生怪物处于  violent 状态,满血int getLife() { return life; }void setLife(int t) { life = t; }Status* getStatus() { return pStatus; }void setStatus(Status* s) { pStatus = s; }void attacked(int power) { pStatus->attacked(power, this); }
};class Status_Dead : public Status
{
public:virtual void attacked(int power, Monster* ptrMonstor) {	}static Status* getStatusObj(){static Status_Dead objDead;return &objDead;}
};class Status_Fear : public Status
{
public:virtual void attacked(int power, Monster* ptrMonstor){cout << " 怪物处于 Fear 状态 , " << " 但受到了 " << power << "  点攻击  , ";int newLife = ptrMonstor->getLife() - power;if (newLife >= 1)cout << "  仍处于 fear 状态\n\n";else{cout << "  进入了 Dead 状态\n\n";ptrMonstor->setStatus(Status_Dead::getStatusObj());}ptrMonstor->setLife(newLife);}static Status* getStatusObj(){static Status_Fear objFear;return &objFear;}
};void Status_Violent::attacked(int power, Monster* ptrMonstor)  // 这个函数包含的符号最多,只能放在最后,要不总提示符号找不到
{cout << " 怪物处于 violent 状态 , " << " 但受到了 " << power << "  点攻击  , ";int newLife = ptrMonstor->getLife() - power;if (newLife >= 300)cout << "  仍处于 violent 状态\n\n";else if (newLife >= 1){cout << "  进入了 fear 状态\n\n";ptrMonstor->setStatus(Status_Fear::getStatusObj());}else{cout << "  进入了 Dead 状态\n\n";ptrMonstor->setStatus(Status_Dead::getStatusObj());}ptrMonstor->setLife(newLife);
}Status*  Status_Violent:: getStatusObj()
{static Status_Violent objViolent;return &objViolent;
}int main()
{Monster m;m.attacked(100);m.attacked(150);m.attacked(50);m.attacked(400);return 0;
}

测试结果如下:

在这里插入图片描述

(16)享元模式 Flyweight 。也叫蝇量模式。比如围棋游戏,要绘制棋子。只要知道棋子的颜色和坐标信息,就可以绘制出该棋子。但也会造成创建大量的重复的小对象–棋子。由此提出享元模式。让程序中代码共享共用一些对象,达到减少内存使用提高效率的效果。比如可以只在围棋环境中创建黑子白子两个对象。依据位置,重复在不同坐标处绘制。以下给出范例代码:

enum Color{ black , white };struct Position
{int x;int y;
};class Piece
{
public:virtual ~Piece() {}virtual void draw( const Position& p) = 0;
};class Piece_White : public Piece
{
public:void draw(const Position& p) override{cout << "  在棋盘 ( " << p.x << " , " << p.y << " ) 处,画了一个白棋子\n\n";}
};class Piece_Black : public Piece
{
public:void draw(const Position& p) override{cout << "  在棋盘 ( " << p.x << " , " << p.y << " ) 处,画了一个黑棋子\n\n";}
};class Factory
{
private:map< Color, Piece* > mapPiece;
public:~Factory(){for (auto iter = mapPiece.begin(); iter != mapPiece.end(); iter++)//delete iter->second;  这两种写法是等价的delete (*iter).second;}Piece* getPiece(Color color){auto iter = mapPiece.find(color);if (iter != mapPiece.end())return iter->second;else{Piece* ptr = nullptr; if (color == Color::black)ptr = new Piece_Black;elseptr = new Piece_White;mapPiece.insert(make_pair(color , ptr));return ptr;}}
};int main()
{Factory fact;fact.getPiece(black)->draw(Position(3, 4));fact.getPiece(white)->draw(Position(6, 9));return 0;
}

测试结果如下:

在这里插入图片描述

(17)

谢谢

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

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

相关文章

【wsl2】WIN11借助wsl2挂载ext4磁盘

我有一块ext4文件系统的硬盘&#xff0c;想要在win11上访问&#xff0c;我们可以通过wsl2进行挂载 wsl2的安装就跳过了&#xff0c;可以自行搜索安装。 安装完成后 >>> GET-CimInstance -query "SELECT * from Win32_DiskDrive"通过这个命令&#xff0c;可…

汽车电子工程师入门系列——AUTOSAR通信服务框架(上)

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

Spring Boot项目如何配置跨域

1、通过SpringSecurity进行配置 2、前端跨域配置&#xff1a;proxy配置项用于设置代理规则&#xff0c;用于前端开发中与后端API交互时使用。

ROS2自定义接口Python实现机器人移动

1.创建机器人节点接口 cd chapt3_ws/ ros2 pkg create example_interfaces_rclpy --build-type ament_python --dependencies rclpy example_ros2_interfaces --destination-directory src --node-name example_interfaces_robot_02 --maintainer-name "Joe Chen" …

Java 基本数据类型【基础篇】

目录 Java 数据类型基本数据类型整数类型【byte、short、int、long】浮点类型【float、double】布尔类型【boolean】字符类型【char】 引用数据类型 Java 数据类型 Java 语言支持的数据类型分为两种&#xff1a;基本数据类型 和 引用数据类型。其数据类型结构如下图所示&#x…

24年hvv前夕,微步也要收费了,情报共享会在今年结束么?

一个人走的很快&#xff0c;但一群人才能走的更远。吉祥同学学安全https://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247483727&idx1&sndb05d8c1115a4539716eddd9fde4e5c9&scene21#wechat_redirect这个星球&#x1f517;里面已经沉淀了&#xff1a; 《Ja…

30 哈希的应用

位图 概念 题目 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何判断一个数是否在这40亿个整数中 1.遍历&#xff0c;时间复杂度O(N) 2.二分查找&#xff0c;需要先排序&#xff0c;排序(N*logN)&#xff0c;二分查找&#xff0c;logN。…

2024年【焊工(初级)】考试及焊工(初级)报名考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 焊工&#xff08;初级&#xff09;考试是安全生产模拟考试一点通生成的&#xff0c;焊工&#xff08;初级&#xff09;证模拟考试题库是根据焊工&#xff08;初级&#xff09;最新版教材汇编出焊工&#xff08;初级&a…

C++ 史上首次超越 C,仅次于Python!【送源码】

TIOBE 公布了 2024 年 6 月的编程语言排行榜——C 史上首次超越 C&#xff0c;跃至榜二&#xff0c;仅次于 Python。 C 是一种广泛应用于嵌入式系统、游戏开发和金融交易软件等领域的语言&#xff0c;在本月成功超越了 C&#xff0c; 成为了 TIOBE 指数中新的第二名。 这是 C …

四川省高等职业学校大数据技术专业建设暨专业质量监测研讨活动顺利开展

6月21日&#xff0c;省教育评估院在四川邮电职业技术学院组织开展全省高等职业学校大数据技术专业建设暨专业质量监测研讨活动。省教育评估院副院长赖长春&#xff0c;四川邮电职业技术学院党委副书记、校长冯远洪&#xff0c;四川邮电职业技术学院党委委员、副校长程德杰等出席…

鸿蒙开发设备管理:【@ohos.multimodalInput.inputConsumer (组合按键)】

组合按键 InputConsumer模块提供对按键事件的监听。 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。本模块接口均为系统接口&#xff0c;三方应用不支持调用。 导入模块 import inputConsumer …

36.基于多目标螳螂优化算法的微电网优化matlab

微♥关注“电击小子程高兴的MATLAB小屋”获取资源 基于螳螂优化算法的多目标优化算法 求解微电网多目标优化调度 比较不同目标函数寻优对调度结果的影响 第1种.将两个目标函数值归一化相加&#xff0c;取相加后最小的目标值的粒子&#xff0c;即寻找折衷解并画图 第2种寻找…

AI助力校园安全:EasyCVR视频智能技术在校园欺凌中的应用

一、背景分析 近年来&#xff0c;各地深入开展中小学生欺凌行为治理工作&#xff0c;但有的地方学生欺凌事件仍时有发生&#xff0c;严重损害学生身心健康&#xff0c;引发社会广泛关注。为此&#xff0c;教育部制定了《防范中小学生欺凌专项治理行动工作方案》进一步防范和遏…

json文件 增删查改

默认收藏夹 qt操作json格式文件... 这个人的 写的很好 我的demo全是抄他的 抄了就能用 —————————— 下次有空把我的demo 传上来 在E盘的demo文件夹 json什么名字

Leetcode - 133双周赛

目录 一&#xff0c;3190. 使所有元素都可以被 3 整除的最少操作数 二&#xff0c;3191. 使二进制数组全部等于 1 的最少操作次数 I 三&#xff0c;3192. 使二进制数组全部等于 1 的最少操作次数 II 四&#xff0c;3193. 统计逆序对的数目 一&#xff0c;3190. 使所有元素都…

PyQt问题汇总(持续更新)

目录 1.抛出异常后QAppliaction自动闪退 2.Unbuntu共享文件夹自动挂载 1.抛出异常后QAppliaction自动闪退 开发阶段&#xff0c;PyQt5 QAppliaction会在遇到未捕获的异常时立即退出&#xff0c;它能够快速发现并报告错误&#xff0c;我在调用一些密码算法库的时候&#xff0…

FreeBSD虚拟化解决之道:高效、安全、灵活的虚拟解决方案全览

FreeBSD下的虚拟化技术 虚拟化软件可让一台计算机同时运行多个操作系统。这种用于个人电脑的系统软件通常涉及一个运行虚拟化软件的宿主机&#xff08;host&#xff09;操作系统&#xff0c;并支持任何数量的客户机&#xff08;guest&#xff09;操作系统。 FreeBSD下的虚拟解…

基于Java的地方废物回收机构管理系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;Java技术&#xff0c;MIS的总体思想&#xff0c;MySQL数据库 工具&#xff1a;Eclipse&#xff0c;…

【数据结构】详解二叉树之堆

失败只是暂时停止成功&#xff0c;假如我不能&#xff0c;我就一定要&#xff1b;假如我要&#xff0c;我就一定能&#xff01;&#x1f493;&#x1f493;&#x1f493; 目录 ✨说在前面 &#x1f34b;知识点一&#xff1a;树的概念和结构 • &#x1f330;1.什么是树&#x…

【漏洞复现】学分制系统GetTimeTableData SQL注入

0x01 产品简介 学分制系统由上海鹏达计算机系统开发有限公司研发&#xff0c;是基于对职业教育特点和需求的深入理解&#xff0c;结合教育部相关文件精神&#xff0c;并广泛吸纳专家、学者意见而开发的一款综合性管理系统。系统采用模块化的设计方法&#xff0c;方便学校根据自…