面试二十四、继承多态

一、继承的本质和原理

 

 

 

        组合(Composition): 组合是一种"有一个"的关系,表示一个类包含另一个类的对象作为其成员。这意味着一个类的对象包含另一个类的对象作为其一部分。组合关系通常表示强关联,被包含的对象的生命周期通常受到包含它的对象的生命周期的限制。

示例:

class Engine {
    // Engine class definition
};

class Car {
    Engine engine; // Car "has a" Engine
    // Other members and methods
};

继承(Inheritance): 继承是一种"是一个"的关系,表示一个类可以继承另一个类的属性和行为。子类(派生类)可以继承父类(基类)的成员和方法,并且可以添加新的成员和方法。继承关系建立了一个类层次结构,允许代码重用和实现多态。

示例:

class Animal {
    // Animal class definition
};

class Dog : public Animal { // Dog "is a" Animal
    // Dog-specific members and methods
};

在C++中,structclass都是用于定义自定义类型(类)的关键字,它们有一些相同点和区别。

相同点:

  1. 成员变量和成员函数structclass都可以包含成员变量和成员函数。

  2. 访问控制structclass都支持访问控制修饰符publicprotectedprivate,用于控制成员的访问权限。

  3. 封装性structclass都支持封装性,即将数据和操作封装在一个单元中,隐藏内部实现细节,对外提供接口。

  4. 继承structclass都支持继承,可以通过派生类继承基类的成员和方法。

区别:

  1. 默认访问控制:在struct中,默认的成员访问级别是public,而在class中,默认的成员访问级别是private。这意味着,struct中定义的成员默认是公共的,而class中定义的成员默认是私有的。

  2. 继承访问控制:当使用class来定义一个继承关系时,默认的继承访问级别是private,而使用struct时,默认的继承访问级别是public。这意味着,如果你从class继承,派生类的成员默认为private;如果你从struct继承,派生类的成员默认为public

  3. 用法习惯:一般来说,struct更适合用来表示简单的数据结构,成员默认是公共的,不涉及复杂的封装;而class更适合用来表示有复杂行为的对象,需要进行更严格的封装和访问控制。

继承的本质可以总结如下:

  1. 代码重用:子类可以继承父类的成员变量和成员函数,无需重新实现相同的功能,从而实现代码的重用。

  2. 扩展性:子类可以添加新的成员变量和成员函数,从而扩展父类的功能,使其具有更多的行为。

  3. 多态性:继承是实现多态的基础。通过基类的指针或引用指向子类的对象,可以实现运行时多态性,即在运行时根据对象的实际类型调用相应的方法。

  4. 继承链:继承关系可以形成继承链,允许多层次的继承,子类可以继承父类的属性和行为,而子类的子类又可以继承子类的属性和行为,以此类推。

二、 派生类的构造过程

 三、重载、隐藏、覆盖

1. 函数的重载:

        函数重载(Function Overloading)是指在同一个作用域内,可以定义多个函数,它们具有相同的名称但是参数列表不同(参数的类型、个数或顺序不同),编译器根据调用时提供的参数来确定具体调用哪个函数。

        函数重载的主要特点包括:

  1. 相同的函数名:重载的函数具有相同的函数名。
  2. 不同的参数列表:重载的函数具有不同的参数列表,可以是参数的类型、个数或顺序不同。
  3. 在同一个作用域内:重载的函数必须在同一个作用域内定义。

        函数重载使得函数命名更加灵活,可以根据函数的功能和参数的类型选择合适的函数来调用,从而提高了代码的可读性和灵活性。

#include <iostream>

// 函数重载示例
int add(int x, int y) {
    return x + y;
}

double add(double x, double y) {
    return x + y;
}

int add(int x, int y, int z) {
    return x + y + z;
}

int main() {
    std::cout << "Sum of 3 and 5 is: " << add(3, 5) << std::endl; // 调用第一个 add 函数
    std::cout << "Sum of 3.5 and 2.5 is: " << add(3.5, 2.5) << std::endl; // 调用第二个 add 函数
    std::cout << "Sum of 2, 4, and 6 is: " << add(2, 4, 6) << std::endl; // 调用第三个 add 函数
    return 0;
}
2 基类和派生类 

        基类和派生类的show()不能说是重载,因为作用域不同。其是隐藏关系(隐藏的是作用域)。   如果派生类中没有show()函数,则可以调用基类的show()方法,但是如果派生类中存在show()同名的,不管参数列表,会将基类的show隐藏,包括基类中show函数的全部重载。

 3.继承的相互转换

指针的类型限制了指针的解引用能力 

 四、虚函数、静态绑定和动态绑定

1.typeid

        在C++中,typeid 是一个操作符,用于获取一个表达式的类型信息。它返回一个std::type_info 对象,该对象包含有关表达式类型的信息,如类名、基类等。std::type_info 是一个标准库类型,定义在 <typeinfo> 头文件中。通常,typeid 主要用于运行时类型识别(RTTI)和多态代码中。

        例如,你可以使用 typeid 来比较两个对象的类型是否相同,或者在运行时查找对象的实际类型以执行相应的操作。以下是一个示例:

#include <iostream>
#include <typeinfo>

class Base {
    virtual void foo() {}
};

class Derived : public Base {};

int main() {
    Base* basePtr = new Derived();
    
    if (typeid(*basePtr) == typeid(Derived)) {
        std::cout << "basePtr 指向的对象是 Derived 类型" << std::endl;
    } else {
        std::cout << "basePtr 指向的对象不是 Derived 类型" << std::endl;
    }
    
    delete basePtr;
    
    return 0;
}
2. 静态绑定,没有虚函数,编译阶段就确定调用

 3.虚函数和虚函数表

  这里的show()加不加virtual,都是虚函数

        虚函数表(vtable)和虚函数表指针(vptr)是用于实现C++中虚函数机制的重要组成部分。它们的创建时机如下:

  1. 虚函数表(vtable)的创建时机:

    • 虚函数表是在编译阶段由编译器生成的。当一个类中包含至少一个虚函数时,编译器会在编译时生成该类的虚函数表。虚函数表是一个存储了该类中所有虚函数地址的数组,每个虚函数在表中占据一个位置,编译器会根据虚函数声明的顺序依次将其地址存入虚函数表中。
  2. 虚函数表指针(vptr)的创建时机:

    • 虚函数表指针是在运行时由编译器插入到对象的内存布局中的。当一个类包含虚函数时,每个对象都会包含一个虚函数表指针,用来指向该类的虚函数表。这个虚函数表指针(vptr)是在对象创建时初始化的,通常是在构造函数中完成初始化。当对象被销毁时,虚函数表指针也会被销毁。

 

4. 动态绑定

首先看指针的类型,然后看调用函数在父类中是正常的函数还是虚函数,如果是正常函数在编译的时候就知道了,如果是虚函数,先要查找子类的虚函数表(因为虚函数会被重写或者覆盖),然后调用函数,

 

【C++】RTTI有什么用?怎么用? - 知乎 (zhihu.com)

五、虚析构函数(new出来的派生类对象)

        在C++中,构造函数不能声明为虚函数。这是因为在构造对象时,需要确定构造函数的调用路径。如果构造函数是虚的,那么在构造对象时,需要在虚函数表中查找适当的构造函数。但是,在对象构造过程中,虚函数表尚未被设置,因此无法进行动态绑定。

        先构造函数,才有对象。

        静态成员方法不依赖于对象,就不会去对象中查询虚函数指针,不会去查虚函数表

         基类的析构函数会在派生类的析构函数执行完毕后被隐式调用。这是因为在派生类的析构函数中,会自动调用其直接基类的析构函数,然后依次向上调用每个基类的析构函数,直到调用完毕为止。

 

六、多态 

 

七、抽象类 

 八、类型转换

 

 九、多继承

1 虚继承和虚继承

 

 

1  是因为基类没有虚函数,才建的自己的虚函数指针和虚函数表

2. 是继承A的虚函数表和虚函数指针

2. 菱形继承  

 虚基类的数据搬到派生类中最后面,在原来的地方补vbptr

 

 

 

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

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

相关文章

【Week-Y7】使用自己的数据集训练YOLO-v8

文章目录 一、官方环境配置与测试1. 配置环境2. 用官方图片测试&#xff08;图片下载失败&#xff09;3. 用本地图片测试&#xff0c;检查配置的环境是否可用 二、使用自己的数据集进行训练测试1. 执行split_train_val.py文件2. 执行python .\voc_label.py文件3. 创建fruit.yam…

[Python基础知识]05函数和模块

一、函数的定义 格式&#xff1a;def 函数名&#xff08;参数列表&#xff09;: 注&#xff1a; 函数代码块以 def 关键词开头&#xff0c;后接函数标识符名称和圆括号()。即使该函数不需要接收任何参数&#xff0c;也必须保留一对空的圆括号 函数形参不需要声明其类型&#x…

layui中禁用div标签等操作

为了实现点击表格行后触发事件 然后去触发后进行操作 页面流程操作设置规定 不可编辑直接添加属性 class"layui-disabled"如果在最大的 div 设置不可编辑 但是内部有些还是可以触发使用的 所以就重写一下 取到当前 div 下的 所有的子元素 然后在给所有的子元素…

闲话 ASP.NET Core 数据校验(二):FluentValidation 基本用法

前言 除了使用 ASP.NET Core 内置框架来校验数据&#xff0c;事实上&#xff0c;通过很多第三方框架校验数据&#xff0c;更具优势。 比如 FluentValidation&#xff0c;FluentValidation 是第三方的数据校验框架&#xff0c;具有许多优势&#xff0c;是开发人员首选的数据校验…

抢先体验:MacOS成功安装PHP8.4教程

根据官方消息&#xff0c;PHP 8.4将于2024年11月21日发布。它将通过三个 alpha 版本、三个 beta 版本和六个候选版本进行测试。 这次的重大更新将为PHP带来许多优化和强大的功能。我们很高兴能够引导您完成最有趣的更新升级&#xff0c;这些更改将使我们能够编写更好的代码并构…

解决React报错Encountered two children with the same key

当我们从map()方法返回的两个或两个以上的元素具有相同的key属性时&#xff0c;会产生"Encountered two children with the same key"错误。为了解决该错误&#xff0c;为每个元素的key属性提供独一无二的值&#xff0c;或者使用索引参数。 这里有个例子来展示错误是…

YOLOv8主要命令讲解

YOLOv8主要有三个常用命令&#xff0c;分别是&#xff1a;train&#xff08;训练&#xff09;、predict&#xff08;预测&#xff09;、export&#xff08;转化模型格式&#xff09;&#xff0c;下面我将展开讲讲三个常用命令的常用参数与具体使用方法。 一、训练 通过自己标…

STM32单片机通过串口控制DDSM210 直驱伺服电机

1 电机介绍 官方资料&#xff1a;https://www.waveshare.net/wiki/DDSM210 DDSM210 直驱伺服电机是基于一体化开发理念&#xff0c;集外转子无刷电机、编码器、伺服驱动于一体的高可靠性永磁同步电动机&#xff0c;其结构紧凑&#xff0c;安装方便&#xff0c;运行稳定&#x…

react核心知识

1. 对 React 的理解、特性 React 是靠数据驱动视图改变的一种框架&#xff0c;它的核心驱动方法就是用其提供的 setState 方法设置 state 中的数据从而驱动存放在内存中的虚拟 DOM 树的更新 更新方法就是通过 React 的 Diff 算法比较旧虚拟 DOM 树和新虚拟 DOM 树之间的 Chan…

【PCL】教程 supervoxel_clustering执行超体聚类并可视化点云数据及其聚类结果

[done, 417.125 ms : 307200 points] Available dimensions: x y z rgba 源点云milk_cartoon_all_small_clorox.pcd > Loading point cloud... > Extracting supervoxels! Found 423 supervoxels > Getting supervoxel adjacency 这段代码主要是使用PCL&#xff08;Po…

Linux进程——进程的创建(fork的原理)

前言&#xff1a;在上一篇文章中&#xff0c;我们已经会使用getpid/getppid函数来查看pid和ppid,本篇文章会介绍第二种查看进程的方法&#xff0c;以及如何创建子进程&#xff01; 本篇主要内容&#xff1a; 查看进程的第二种方法创建子进程系统调用函数fork 在开始前&#xff…

【华为】路由综合实验(基础)

【华为】路由综合实验 实验需求拓扑配置AR1AR2AR3AR4AR5PC1PC2 查看通信OSPF邻居OSPF路由表 BGPBGP邻居BGP 路由表 配置文档 实验需求 ① 自行规划IP地址 ② 在区域1里面 启用OSPF ③ 在区域1和区域2 启用BGP&#xff0c;使AR4和AR3成为eBGP&#xff0c;AR4和AR5成为iBGP对等体…

buuctf-misc-22.神秘龙卷风1

22.神秘龙卷风1 题目&#xff1a;暴力破解-翻译Brainfuck计算机语言 根据提示是4位密码&#xff0c;直接破解密码即可 解压后发现是这样一个文档 我们尝试使用网站翻译这个 内容由“”、“.”、“>”三种符号组成&#xff0c;我刚开始认为这是一种密文&#xff0c;经过搜索…

thinkpad电脑文件隐藏了怎么恢复?教你几招

在使用ThinkPad电脑时&#xff0c;有时我们可能会发现一些文件或文件夹突然“消失”了&#xff0c;这通常是因为它们被隐藏了。本文将为您介绍几招恢复ThinkPad电脑上隐藏文件的方法&#xff0c;帮助您轻松找回丢失的文件。 图片来源于网络&#xff0c;如有侵权请告知 一、了解…

【实时数仓架构】方法论

笔者不是专业的实时数仓架构&#xff0c;这是笔者从其他人经验和网上资料整理而来&#xff0c;仅供参考。写此文章意义&#xff0c;加深对实时数仓理解。 一、实时数仓架构技术演进 1.1 四种架构演进 1&#xff09;离线大数据架构 一种批处理离线数据分析架构&#xff0c;…

when to create a ViewRootImpl

when to create a ViewRootImpl when method setView is called: when method dispatchDetachedFromWindow is called:

预训练模型介绍

一、什么是GPT GPT 是由人工智能研究实验室 OpenAI 在2022年11月30日发布的全新聊天机器人模型, 一款人工智能技术驱动的自然语言处理工具 它能够通过学习和理解人类的语言来进行对话, 还能根据聊天的上下文进行互动,能完成撰写邮件、视频脚本、文案、翻译、代码等任务 二、 为…

TypeScript学习笔记:迈向更安全的JavaScript编程

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Linux专栏05:Linux基本指令之目录处理指令

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Linux专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Linux基本指令之目录处理指令 编号&#xff1a;05 文章目录 Linux基…

STM32之HAL开发——电容按键

电容按键原理 电容器 (简称为电容) 就是可以容纳电荷的器件&#xff0c;两个金属块中间隔一层绝缘体就可以构成一个最简单的电容。如图 32_1 (俯视图)&#xff0c;有两个金属片&#xff0c;之间有一个绝缘介质&#xff0c;这样就构成了一个电容。这样一个电容在电路板上非常容…
最新文章