C++ 设计模式

文章目录

  • 类图
    • 泛化
    • 实现
    • 关联
    • 聚合
    • 组合
    • 依赖
    • 总结
  • 类内部的三种权限(公有、保护、私有)
  • 类的三种继承方式
    • 描述与图
    • 总结
  • 面向对象七大原则
    • 单一职责原则(Single Responsibility Principle)
    • 里氏替换原则(Liskov Substitution Principle)
    • 依赖倒置原则(Dependence Inversion Principle)
    • 接口隔离原则(Interface Segregation Principle)
    • 迪米特法则(Law Of Demeter)
    • 开闭原则(Open Close Principle)
    • 组合/聚合复用原则(Composite/Aggregate Reuse Principle CARP)
  • 关于类的静态成员
  • 类的静态成员函数
  • 关于C++构造函数的继承问题
  • 创建型模式
    • Factory模式(工厂模式)
    • AbstractFactory模式(抽象工厂模式)

类图

泛化

  • 【泛化】是一种继承关系,表示一般与特殊的关系,它指定了子类如何继承父类的所有特征和行为。例如:老虎是动物的一种,即有老虎的特性也有动物的共性。
  • 【代码体现】:继承。
  • 特征:空白三角形+实线 指向父类
    动物是老虎的父类

实现

  • 【实现关系】是一种类与接口的关系,表示类是接口所有特征和行为的实现。

  • 特征:空白三角形箭头+虚线 箭头指向接口
    在这里插入图片描述

  • 【代码体现】:纯虚函数

  • 对于C++,其接口类一般具有以下特征:

    • 最好不要有成员变量,但可以有静态常量(static const或enum)
      • 如果成员变量,尤其是可变的成员变量,定义在接口中,等于是把实现细节暴露出来了,不符合接口定义的要求,所以一般不在接口中定义可变的成员变量。
        而常量可以定义在接口中,因为有时接口需要返回状态,而这些状态可以定义成常量放在接口中。
    • 要有纯虚接口方法
      • 由于不能让接口类自身能够实例化,并且需要子类必须实现接口暴露的方法,所以接口方法都要声明成纯虚函数。
        声明成纯虚函数意味着接口类自身不需要提供方法的定义,方法的定义需要由接口类的子类提供,并且接口类自身也因此变成了抽象类而不能被实例化。
    • 要有虚析构函数,并提供默认实现
      • 在使用接口类的指针访问接口类的子类的实例时,当对接口类的指针做delete时,如果接口类的析构函数不是虚析构函数的话,将只会调用接口类(父类)的析构函数,接口类的子类的析构函数将不会被调用,内存泄露将会产生,所以接口类的析构函数必须定义成虚析构函数。
      • 如果接口类的析构函数不提供默认实现,即如果接口类的析构函数是纯虚析构函数的话,接口类的子类将被迫必须提供析构函数的实现,这样对接口类的子类不友好。
    • 不要声明构造函数
      • 不要显式定义任何的构造函数,但也不要在接口中加入如下代码来禁止生成构造函数:
      • Testable() = delete; Testable(const Testable&) = delete;
      • 因为C++的调用机制要求子类的构造函数调用时一定会先调用父类的构造函数,如果禁止生成父类构造函数,代码编译时会报错。如果程序员不显式的提供构造函数,编译器也会隐式的加上构造函数的,虽然这些构造函数对于接口类来说实际没有什么意义。
    • 例子:
class Testable
{
public:
    static const int START = 1;  // #1
    static const int STOP = 2;
 
    virtual void test() = 0;  // #2: 接口方法
 
    virtual ~Testable() {};   // #3: 从C++11开始可以: virtual ~Testable() = default;
}

关联

  • 【关联关系】是一种拥有的关系,它使一个类知道另一个类的属性和方法;如:老师与学生,丈夫与妻子关联可以是双向的,也可以是单向的。双向的关联可以有两个箭头或者没有箭头,单向的关联有一个箭头。
  • 【代码体现】:成员变量
  • 特征:实线 n n实线 1 n 箭头指向被拥有者
    在这里插入图片描述
  • 上图中,老师与学生是双向关联,老师有多名学生,学生也可能有多名老师。但学生与某课程间的关系为单向关联,一名学生可能要上多门课程,课程是个抽象的东西他不拥有学生。

聚合

  • 【聚合关系】:是整体与部分的关系,且部分可以离开整体而单独存在。如车和轮胎是整体和部分的关系,轮胎离开车仍然可以存在。
  • 【聚合关系】是关联关系的一种,是强的关联关系;关联和聚合在语法上无法区分,必须考察具体的逻辑关系。
  • 特征:菱形+实线+箭头在这里插入图片描述

组合

  • 【组合关系】:是整体与部分的关系,但部分不能离开整体而单独存在。如公司和部门是整体和部分的关系,没有公司就不存在部门

  • 组合关系是关联关系的一种,是比聚合关系还要强的关系,它要求普通的聚合关系中代表整体的对象负责代表部分的对象的生命周期。
    在这里插入图片描述

依赖

  • 【依赖关系】:是一种使用的关系,即一个类的实现需要另一个类的协助,所以要尽量不使用双向的互相依赖.
  • 【代码表现】:局部变量、方法的参数、对静态方法的调用、函数返回值
  • 【箭头及指向】:带箭头的虚线,指向被使用者。
    在这里插入图片描述

总结

各种关系的强弱顺序:
泛化 = 实现 > 组合 > 聚合 > 关联 > 依赖
下面这张UML图,比较形象地展示了各种类图关系:
在这里插入图片描述

类内部的三种权限(公有、保护、私有)

在这里插入图片描述
权限按照以下两点递减:

  • 是否能被外部访问
  • 是否能被继承的子类访问

类的三种继承方式

描述与图

  • public继承方式
    基类中所有 public 成员在派生类中为 public 属性;
    基类中所有 protected 成员在派生类中为 protected 属性;
    基类中所有 private 成员在派生类中不能使用。

  • protected继承方式
    基类中的所有 public 成员在派生类中为 protected 属性;
    基类中的所有 protected 成员在派生类中为 protected 属性;
    基类中的所有 private 成员在派生类中不能使用。

  • private继承方式
    基类中的所有 public 成员在派生类中均为 private 属性
    基类中的所有 protected 成员在派生类中均为 private 属性
    基类中的所有 private 成员在派生类中不能使用。

继承方式/基类成员public成员protected成员private成员
public继承publicprotected不可见
protected继承protectedprotected不可见
private继承privateprivate不可见

总结

  • 父类的private成员在子类不能使用、不可见
  • 父类的成员在子类中的权限,最高为其子类的继承方式。

面向对象七大原则

单一职责原则(Single Responsibility Principle)

  • 每一个类应该专注于做一件事情
  • 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;提高类的可读性,提高系统的可维护性;变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。

里氏替换原则(Liskov Substitution Principle)

  • 子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏
  • 使用里氏替换原则时需要注意,子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法,运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能,同时无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。

依赖倒置原则(Dependence Inversion Principle)

  • 程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
  • 面向抽象编程,也就是面向抽象类或接口编程。

接口隔离原则(Interface Segregation Principle)

应当为客户端提供尽可能小的单独的接口,而不是提供大的总的接口。

迪米特法则(Law Of Demeter)

又叫最少知识原则,一个软件实体应当尽可能少的与其他实体发生相互作用。

开闭原则(Open Close Principle)

面向扩展开放,面向修改关闭。

组合/聚合复用原则(Composite/Aggregate Reuse Principle CARP)

尽量使用合成/聚合达到复用,尽量少用继承。原则: 一个类中有另一个类的对象。

关于类的静态成员

  • 类的静态成员与类本身直接相关而不是与类的各个对象保持关联

  • 我们不能在类的内部初始化静态数据成员必须在类的外部初始化
    在这里插入图片描述
    在这里插入图片描述

  • 字面值常量类型constexpr除外
    在这里插入图片描述

类的静态成员函数

在这里插入图片描述

/*类静态成员、静态成员函数例子如下*/
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

class Guest{
private:
	static int m_num;
	static int m_count;
	static double m_totalFee;
	string name;
	int num;
	double fee;
public:
	Guest(string name, double fee):name(name),fee(fee)
	{
		m_num++;
		num = m_num;
		m_count++;
		m_totalFee+=fee;
	}
	~Guest()
	{
		m_count--;
	}
	void show() const{
		printf("num:%d,name:%s,fee:%.2lf\n",num,name.c_str(),fee);
	}
	static int GetCount()
	{
		return m_count;
	}
	static double GetTotalIncome(){
		return m_totalFee;
	}
};

int Guest::m_num = 0;
int Guest::m_count = 0;
double Guest::m_totalFee = 0;

int main(int argc, char *argv[])
{
	Guest g1("lihua",11.99), g2("zhangsan",12.99), g3("xiaomei",13.99);
	g1.show();
	g2.show();
	g3.show();
	printf("count:%d,totalFee:%.2f\n",Guest::GetCount(),Guest::GetTotalIncome());
	
	return 0;
}

关于C++构造函数的继承问题

  • 构造方法用来初始化类的对象,与父类的其它成员不同,它不能被子类继承子类可以继承父类所有的成员变量和成员方法,但不继承父类的构造方法)。因此,在创建子类对象时,为了初始化从父类继承来的数据成员,子类需要调用其父类的构造方法
  • 如果没有显式的构造函数,编译器会给一个默认的构造函数,并且该默认的构造函数仅仅在没有显式地声明构造函数情况下创建
  • 如果子类调用父类带参数的构造方法,需要用初始化父类成员对象的方式
#include <iostream.h>  
 class animal  
 {  
 public:  
   animal(int height, int weight)   //有且仅有 有参参数,必须显性调用
   {  
     cout<<"animal construct"<<endl;  
   }  
   …  
 };  
 class fish:public animal  
 {  
 public:  
   fish():animal(400,300)  
   {  
     cout<<"fish construct"<<endl;  
   }  
   …  
 };  
 void main()  
 {  
   fish fh;  
 }  
  • 在fish类的构造函数后,加一个冒号(:),然后加上父类的带参数的构造函数。这样,在子类的构造函数被调用时,子类就会去调用父类的带参数的构造函数去构造对象

创建型模式

Factory模式(工厂模式)

在这里插入图片描述

//Product.h
#ifndef _PRODUCT_H_
#define _PRODUCT_H_
class Product
{
public:
    virtual ~Product() = 0; //多态时,调用子类的析构函数
protected:
    Product();     //不可被外部调用,抽象基类不可以被实例化
private:
};

class ConcreteProduct:public Product
{
public:
    ~ConcreteProduct();
    ConcreteProduct();
protected:
private:
};
#endif
//Product.cpp
#include "Product.h"
#include <iostream>
Product::Product()
{
    std::cout<<"Product()...."<<std::endl; 
}

Product::~Product()
{
    std::cout<<"~Product()...."<<std::endl; 
}

ConcreteProduct::ConcreteProduct()
{
    std::cout<<"ConcreteProduct()...."<<std::endl; 
}

ConcreteProduct::~ConcreteProduct()
{
    std::cout<<"~ConcreteProduct()...."<<std::endl; 
}
//Factory.h
#ifndef _FACTORY_H_
#define _FACTORY_H_
class Product;
//factory抽象基类
class Factory   
{
public:
    virtual ~Factory() = 0;         //析构函數
    virtual Product* CreateProduct() = 0;   
protected:
    Factory();      //构造函数,外部不可以调用,子类可以调用
private:
};

//factory实例
class ConcreteFactory:public Factory
{
public:
    ~ConcreteFactory();         
    ConcreteFactory();
    Product* CreateProduct();
protected:
private:
};
#endif
//Factory.cpp
#include "Product.h"
#include "Factory.h"
#include <iostream>

Factory::Factory()
{
    std::cout<<"Factory()...."<<std::endl; 
}

Factory::~Factory()
{
    std::cout<<"~Factory()...."<<std::endl; 
}

ConcreteFactory::ConcreteFactory()
{
    std::cout<<"ConcreteFactory()......"<<std::endl;
}

ConcreteFactory::~ConcreteFactory()
{
    std::cout<<"~ConcreteFactory()......"<<std::endl;
}

Product* ConcreteFactory::CreateProduct()
{
    return new ConcreteProduct();
}
//main.cpp
#include "Factory.h"
#include "Product.h"
#include <iostream>
#include <cstdio>
int main(int argc, char* argv[])
{
    Factory *fac = new ConcreteFactory();
    Product* p = fac->CreateProduct();
    delete p;
    delete fac;
    return 0;
}
#makefile
CC = g++ -g
BaseIncludePath = ../include/
SRCS = main.cpp Product.cpp Factory.cpp
OBJS = $(addsuffix .o,$(basename ${SRCS}))

main.exe: $(OBJS)
	$(CC) $(OBJS) -o main.exe 

%.o: %.cpp
	$(CC) -c $< -o $@ -I$(BaseIncludePath)

clean:
	del $(OBJS) main.exe

AbstractFactory模式(抽象工厂模式)

在这里插入图片描述

  • 抽象工厂模式和工厂方法模式一样,都符合开闭原则。但是不同的是,在抽象工厂模式中,增加一个产品族很容易,而增加一个产品等级结构却很难,工厂模式则反之。

  • 也就是说,在抽象工厂模式中,增加一个具体的工厂很容易,但是你想在工厂中多生产一种产品,就需要修改很多个类,会违背开闭原则,这种情况下应该使用工厂模式。

  • 简单来说:工厂模式新增产品类型容易,抽象工厂模式新增工厂类型容易

表头表头
单元格单元格
单元格单元格

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C3_W2_Collaborative_RecSys_Assignment_吴恩达_中英_Pytorch

Practice lab: Collaborative Filtering Recommender Systems(实践实验室:协同过滤推荐系统) In this exercise, you will implement collaborative filtering to build a recommender system for movies. 在本次实验中&#xff0c;你将实现协同过滤来构建一个电影推荐系统。 …

【Memory协议栈】Memory Abstraction Interface模块介绍

目录 前言 正文 1.功能简介 2.关键概念 3.关键类型定义 3.1 MemIf_StatusType 3.2 MemIf_JobResultType 3.3 MemIf_ModeType 4.关键API定义 4.1 MemIf_SetMode 4.2 MemIf_Read 4.3 MemIf_Write 4.4 MemIf_Cancel 4.5 MemIf_GetStatus 4.6 MemIf_GetJobResult 4…

「滚雪球学Java」:集合(章节汇总)

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

Ocr之PaddleOcr模型训练

目录 一、系统环境 1 镜像拉取ppocr 进行部署 2 安装paddlepaddle 二、训练前的准备 1 下载源码 2 预模型下载 3 修改模型训练文件yml 4 编排训练集 5 执行脚本进行训练 6 需要修改文件夹名称 三、开始训练 1 执行训练命令 2 对第一次评估进行解释 3 引言 五、总…

【解决方案】ArcGIS Engine二次开发时,运行后出现“正尝试在 OS 加载程序锁内执行托管代码。不要尝试在 DllMain...”

我们在做ArcGIS Engine二次开发时&#xff0c;特别是新手&#xff0c;安装好了开发环境&#xff0c;满怀信心的准备将按照教程搭建好的框架在Visual Studio中进行运行。点击运行后&#xff0c;却出现了“正尝试在 OS 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化…

2023年09月CCF-GESP编程能力等级认证Scratch图形化编程四级真题解析

一、单选题(共15题,共30分) 第1题 人们所使用的手机上安装的 App 通常指的是( )。 A:一款操作系统 B:一款应用软件 C:一种通话设备 D:以上都不对 答案:B 第2题 下列流程图的输出结果是?( ) A:9 B:7 C:5 D:11 答案:A 第3题 默认小猫角色,执行下列程序…

【Linux】软件管理yum | 编辑器vim | vim插件安装

目录 1. Linux软件管理yum 1.1 什么是软件包 1.2 查看软件包 1.3 如何安装软件 1.4 如何卸载软件 2. Linux编辑器vim 2.1 vim的基本概念 2.2 vim的基本操作 2.3 vim正常模式命令集 2.4 vim末行模式命令集 2.5 简单vim配置 2.6 插件安装 1. Vim-Plug 3. coc.nvim …

力扣hot100题解(python版44-47题)

44、二叉搜索树中第K小的元素 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 个最小元素&#xff08;从 1 开始计数&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,1,4,null,2], k 1 输出&#xff1a;…

C++的内联函数

目录 前言 内联函数 为什么声明和定义分离 为什么声明和定义分离后不出错 为什么内联函数不支持声明和定义分离 为什么内联函数支持声明和定义不分离 坚持声明和定义不分离的解决方法 static修饰函数 inline修饰函数 结论 声明和定义不分离的应用场景 前言 在C语言…

Compiling from source on UNIX(cmake doxygen ant maven ccache)

前言 源码链接 cmake-3.18.0 https://cmake.org/files/v3.18/cmake-3.18.0.tar.gzdoxygen-1.10.0 https://www.doxygen.nl/files/doxygen-1.10.0.src.tar.gzapache-ant-1.10.8-bin https://archive.apache.org/dist/ant/binaries/apache-ant-1.10.8-bin.tar.gzapache-maven-3…

tomcat部署和优化(二)----- 轻松搭建博客、状态页优化、虚拟主机配置

一、tomcat 1、自建博客 [rootzzzcentos1 ~]#systemctl stop firewalld [rootzzzcentos1 ~]#setenforce 0 [rootzzzcentos1 ~]#cd /data/ [rootzzzcentos1 data]#rz -E rz waiting to receive. [rootzzzcentos1 data]#ls apache-tomcat-9.0.16 apache-tomcat-9.0…

数据挖掘:航空公司的客户价值分析

需求分析 理解并掌握聚类分析方法&#xff0c;掌握数据的标准化&#xff0c;掌握寻找最佳聚类数&#xff0c;掌握聚类的绘图&#xff0c;掌握聚类分析的应用场景。 系统实现 实验流程分析 借助航空公司数据&#xff0c;对客户进行分类对不同类别的客户进行特征分析&#xf…

对称加密与非对称加密

1、对称加密 对称加密&#xff0c;即采用对称的密码编码技术&#xff0c;他的特点是&#xff0c;加密和解密使用相同的秘钥。 常见的对称加密算法有DES、3DES、Blowfish、IDEA、RC4、RC5、RC6和AES。 优点&#xff1a;对称加密算法使用起来简单快捷&#xff0c;密钥较短&…

【Python】进阶学习:pandas--如何根据指定条件筛选数据

【Python】进阶学习&#xff1a;pandas–如何根据指定条件筛选数据 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望…

亿道信息轻工业三防EM-T195,零售、制造、仓储一网打尽

厚度仅10.5mm&#xff0c;重量仅0.65千克的EM-T195&#xff0c;其紧凑而纤薄的设计为以往加固型平板带来了全新的轻薄概念。尽管设计时尚、轻薄&#xff0c;但经过军用认证的强固性仍然能够承受所有具有挑战性的环境条件。随身携带无负担的轻便性加上抗震功能使其成为餐厅、酒店…

数据结构——Top-k问题

Top-k问题 方法一&#xff1a;堆排序&#xff08;升序&#xff09;&#xff08;时间复杂度O(N*logN)&#xff09;向上调整建堆&#xff08;时间复杂度&#xff1a;O(N * logN) &#xff09;向下调整建堆&#xff08;时间复杂度&#xff1a;O(N) &#xff09;堆排序代码 方法二&…

Linux信号【systemV】

目录 前言 正文&#xff1a; 1消息队列 1.1什么是消息队列&#xff1f; 1.2消息队列的数据结构 1.3消息队列的相关接口 1.3.1创建 1.3.2释放 1.3.3发送 1.3.4接收 1.4消息队列补充 2.信号量 2.1什么是信号量 2.2互斥相关概念 2.3信号量的数据结构 2.4…

【JSON2WEB】07 Amis可视化设计器CRUD增删改查

总算到重点中的核心内容&#xff0c;CRUD也就是增删改查&#xff0c;一个设计科学合理的管理信息系统&#xff0c;95%的就是CRUD&#xff0c;达不到这个比例要重新考虑一下你的数据库设计了。 1 新增页面 Step 1 启动amis-editor Setp 2 新增页面 名称和路径随便命名&#xf…

【谈一谈】我们所用的三种工厂模式优缺点

【谈一谈】我们所用的三种工厂模式优缺点 Hello!!大家好啊,好久也没有进行文章的更新了,原因嘛,最近的工作任务量有点大,导致摸鱼充电的时间大量减少,哈哈哈(你别说,这是借口嘛!) 不过,今天是星期六,难的能够在这里分享下最近在工作中,我用到的三种工厂模式(简工抽),有啥区别呢…

在线开源免费问卷调查系统

在线开源免费问卷调查系统 平台简介 本项目旨在提供一个简单易用的问卷调查平台&#xff0c;帮助用户创建、分享问卷&#xff0c;并收集、分析调查数据。我们希望能够为各行各业的调查需求提供一种高效、便捷的解决方案。 项目特点 用户友好&#xff1a;清晰直观的用户界面…