(Boolan) C++面向对象高级编程(三)

2024-04-18 08:08

本文主要是介绍(Boolan) C++面向对象高级编程(三),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

今天来看一看关于面向对象的编程的一些内容,也就是关于类和类之间的关系。类和类之间的关系其实一种抽象的描述现实世界的方法。

现实生活中的事物不都和之前提到的String或者Complex一样与其他事务没有太多的联系,其实从不同角度看到事物往往会得出不同的结论。比如,生物界的划分经常是以界门纲目科属种这样的思路来划分的,找出其共性,然后再向上抽象 得出一个概述性的大类。比如,生物学上,人被分类为真核总界 动物界 后生动物亚界 后口动物总门 脊索动物门 脊椎动物亚门 羊膜总纲 哺乳纲 真兽亚纲 灵长目 类人猿亚目 狭鼻猴次目 类人猿超科 人科 人亚科 人族 人属 智人种;猫被分类为真核总界 动物界 后生动物亚界 后口动物总门 脊索动物门 脊椎动物亚门 羊膜总纲 哺乳纲 真兽亚纲 食肉目 猫亚科 猫属 猫种(引自百度百科)。对比猫和人不难发现,在真兽亚纲之前和人类没有区别,但是从目开始,人属于灵长目、而猫咪属于食肉目,从才分道扬镳。那这些分类对于解释这个世界到底有什么帮助吗?这个帮助是很自然的,比如每往下细分一个层次,就意味着区分的细节越细,而越往上则越来越抽象。又比如,真核生物中分四个界:原生生物界、真菌界、植物界和动物界。就拿最熟悉的植物界和动物界来看,他们的区别是显著的,但却别的点往往是粗糙的。

毕竟我对生物学没有太深的了解,但是这样一种分类方法在认识和解释世界的过程中却是通用的。我们希望通过计算机来人和世界结合,那么还是需要对这样的认识方法有一些了解。

C++相较于C的发展,主要是出现了对象的概念,而对象是由各式各样的class构造而成,那么class和class之间的关系就变得特别重要。因为人和人之间就像是对象和对象之间的关系,他们都是同一种class所构造而成的,但是属性却不相同(性别、名 字、名族、肤色、身高、体重、性格,生命周期都不一定相同),虽然存在着这些差异,但每个人都还是属于人类的 (class 人)。但是人和猫的却别不仅仅是对象之间的差别了,更是物种之间的差别了。但是这两个class之间也并不是完全没有相同点,比如这两个物种小的时候都得喝奶等等的相同点,把这喝奶的的这个特点向上抽取,那么就得到了一个新的class(哺乳纲)[ 可以是 抽象类 ],人和猫这两个class和新产生的class之间发生关系就行了,因为这足够表示出这俩的关系了。最后,我们会得到一棵像树一样庞大的一个体系,通过这个体系,我们就能很方便的解释节点(class之间的关系)。

类的基本思想是 数据抽象(data abstraction) 封装(encapsulation)。数据抽象就是一种依赖于 接口(Interface) 实现(implementation)。类的接口包括用户所能执行的操作;类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。

通过这样的抽象,不能方便描述class之间的关系,还更好的将其中的数据进行封装。再拿人和猫来举例,猫身上通常都一身漂亮的皮毛,但人身上基本没有(如果说人身上的汗毛其实不一定比猫少,那么这就是种诡辩了)。这样会有一个巨大的好处,让猫身上的毛很直接的就属于猫咪,而不会出现在人类的class中。这就实现了非常好的 封装 的效果。

这样的操作,对于数据的封装有着巨大的好处,但是,这之间也又带有了一个巨大的方便,比如,猫咪身上的毛,是猫咪自己的私有属性,这时候,同样猫也可以具有一个函数,这个函数专门用于操作他的私有成员属性(猫身上的毛),而人则不需要这样的函数(方法),原因是因为没有人类这样的私有成员属性。

那么为了描述这样的关系,我们主要讨论class之间的3中关系。

三种关系

  • Inheritance(继承)

    • 表示is-a的关系

      struct _List_node_hase
      {_List_node_base * _M_next;_List_node_base * _M_prev;
      };
      template <typename _Tp>
      struct _List_node: public _List_node_base
      {_Tp _M_data;
      };
    • UML图


    • 父类的数据在public的继承关系下,能够被完全继承

    • 父类函数的调用权也可以被子类进程
    • Inheritance关系下的构造和析构


      • 构造:由内而外
        • Derived的构造函数首先调用Base的default构造函数,然后才执行自己
      • 析构:由外而内
        • Derived的析构函数首先执行自己,然后才调用Base的析构函数
    • 虚函数

      • non-virtual函数:不希望Derived class重新定义(override)
      • virtual函数:希望Derived class 重新定义,且已有默认的定义
      • pure virtual函数:希望Derived class一定要重新定义(override),并且没有默认定义
  • Composition(复合)

    • has-a的关系
    • 我把他定义为人和长在身上的器官的关系

        template<class T, class Sequence = deque<T> >class queue {....protected :Sequence c; //底层容器public://以下完全利用c的操作函数完成bool empty() const { return c.empty(); }  //其中的操作均为调用 Sequence c;中所实现的功能size_type size() const { return c.size(); }....}
      • 在queue中,持有一个Sequence c 的对象,queue和Sequence之间的关系就是Composition
      • 其中UML图为

        queue 和 sequence 为Composition的关系
        • 其中黑色菱形一段表示持有另外一个class的对象
      • Adapter设计模式主要使用了Composition来实现。Adapter可以很好的将部分功能封装和开放
    • Composition所占用内存的大小等于他持有的所有的成员所占内存的总和

    • Composition关系下的构造和析构
      • 构造:由内而外(类似于打包裹)
        • Container的构造函数首先调用Component的default构造函数,然后才执行自己的构造函数
          Container:: Container(...) : Component()//Component()由编译器完成添加
          • 如果不满意调用默认构造函数那么需要自己来写需要的构造函数在初始化列表
      • 析构:由外而内(相当于拆包裹)
        • Container的析构函数首先执行,然后才会调用Component的析构函数
          Container:: ~Container(...){ ... ~Component(); } //~Component()有编译器添加
      • 只需要将自己的构造和析构完成即可,顺序会有编译器自动来完成
      • 从生命周期的角度来看,这两个对象之间的关系为“同生共死”
      • 我理解Composition类似于人身上的器官,这些器官由他自己的功能,比如我们需要吸收能力来维持生命,那么我们只需要调用我们自己的消化系统的消化功能(消化系统.消化(食物);),类似于这样的过程,我们就完成了吸收营养的过程,但实际这个过程不是人类这个class完成的。并且一旦人死去,这些器官也会死去(这里可不讨论捐献移植啊!!!),并且也是人先死,之后的器官一个一个死去。
  • Delegation(委托)

    • Composition by reference(不持有需要的对象,只持有目标对象的指针)
    • 我把这个关系定义为我们和东西关系

      class StringRep;
      class String{.....private:StringRep* rep;//持有目标对象的指针
      }
    • UML图示为


    • 用指针相连,所以两个对象的生命周期不同
  • 对于这个关系,我把他理解为我们和东西关系,比如我们和手机,我们需要和远处的朋友聊天,我们可以用手机来完成(只需要调用手机的打电话函数手机->打电话(电话号码);),就相当于我们用我们持有的手机的指来打了一个电话。那为什么是指针呢,因为我们并不是真正拥有一部手机,我们拥有的只是他的使用权和处置权(说白了就是他们都是我们的身外之物,生不带来死不带去,在过去,大家都共用一部电话,那时候只有使用权,也就反应了指针所指对象的特点,可以多个指针变量同时指向一个内存中的对象)。我们可以换手机(也就是让我们持有的指针指向另外一个手机),我们如果没有手机的时候,那么这个指针指向的就是一个NULL;我们想要拥有一个手机的前提是我们必须买一个(也就是为了持有该对象的前提是必须要手动创建)。同样,我们和这个对象的生命周期并没有绑定的关系,我们死了,手机还可以用,手机坏了,我们健康的活着。所以Delegation的关系就是一种生不带来死不带去的关系,不过和我们与东西之间的关系的区别在于,这个类里面有这个指针才有资格拥有这个东西的使用权,如果没有啊,那也是求不来的。这里面有个很大的好处就是,我们可以不断的换手机,而对于你的朋友来说他们知不知道你还没换没手机其实不重要,但对你来说,你可以换一台更好的手机,更漂亮的手机。搜易通过Delegation可以作为一个接口,背后通过他持有的指针进行实现所需要的功能,也就是相当于,朋友只要通过号码就能联系你,实现了联系的功能,但这个功能与你用哪部手机没多大关系,也就实现了不同功能的切换,这也就相当于(Handle/Body)的设计模式的一种现实生活的表现,非常方便的帮你完成了实现类的升级

  • 浅谈几种设计模式
    这些类与类之间的关系,构成了大量的设计模式,由于之后专门有课程讨论设计模式的问题,在这里简单使用图例来表示几种设计模式,来说明三种关系的使用场景。

    • Template Method(模版方法)

模版方法的UML

  • Observer模式

UML

  • Composite(组合模式)

Composite设计模式的UML
  • Prototype(原型模式)

Prototype的UML

这篇关于(Boolan) C++面向对象高级编程(三)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/914221

相关文章

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝