C++类基础(十四)

news2024/11/24 18:44:51

类的继承——虚函数
● 通过虚函数与引用(指针)实现动态绑定
– 使用关键字 virtual 引入
– 非静态、非构造函数可声明为虚函数
– 虚函数会引入vtable结构
在这里插入图片描述

● dynamic_cast

static_cast, reinterpret_cast, const_cast在编译期发生。
dynamic_cast在运行期发生,即判断动态类型满不满足转换条件,如果不满足,不发生转换。运行开销大。

struct Base
{
    virtual void baseMethod() {} //关键字virtual引入了vtable结构,vtable引入了typeinfo
    int baseMember;
};
struct Derive : Base
{
    virtual void deriveMethod() {} //关键字virtual引入了vtable结构,vtable引入了typeinfo
    int deriveMember;
};
struct Derive2 : Derive
{
    virtual void derive2Method() {} //关键字virtual引入了vtable结构,vtable引入了typeinfo
    int derive2Member;
};
int main()
{
    Derive2 d;
    Base& b = d;
    Base* ptr = &d;

    Derive2& d2 = dynamic_cast<Derive2&>(b); //OK,因为typeinfo可以存储动态类型的信息
    Derive2* ptr2 = dynamic_cast<Derive2*>(ptr); //OK,因为typeinfo可以存储动态类型的信息
    return 0;
}
struct Base
{
    //virtual void baseMethod() {}
    int baseMember;
};
struct Derive : Base
{
    //virtual void deriveMethod() {}
    int deriveMember;
};
struct Derive2 : Derive
{
    //virtual void derive2Method() {}
    int derive2Member;
};
int main()
{
    Derive2 d;
    Base& b = d;
    Base* ptr = &d;

    Derive2& d2 = dynamic_cast<Derive2&>(b); //因为没有关键字virtual的非静态、非构造函数,所以不能引入vtable,进而不能引入typeinfo,Error: 'Base' is not polymorphic
    Derive2* ptr2 = dynamic_cast<Derive2*>(ptr); //同上,Error: 'Base' is not polymorphic
    return 0;
}
struct Base
{
    virtual void baseMethod() {}
    int baseMember;
};
struct Derive : Base
{
    //virtual void deriveMethod() {}
    int deriveMember;
};
struct Derive2 : Derive
{
    //virtual void derive2Method() {}
    int derive2Member;
};
int main()
{
    Derive2 d;
    Base& b = d;
    Base* ptr = &d;

    Derive2& d2 = dynamic_cast<Derive2&>(b); //只要基类中有一个virtual函数就可以
    Derive2* ptr2 = dynamic_cast<Derive2*>(ptr); //同上
    return 0;
}
struct Base
{
    virtual void baseMethod() {}
    int baseMember;
};
struct Derive : Base
{
    virtual void deriveMethod() {}
    int deriveMember;
};
struct Derive2 : Derive
{
    virtual void derive2Method() {}
    int derive2Member;
};
int main()
{
    Base b;
    Base& bb = b; //#3: 基类类型的引用bb引用基类对象b
    Base* ptrb = &b; //#1: 基类类型的指针ptrb指向基类对象b
    std::cout << dynamic_cast<Derive2*>(ptrb) << std::endl; //如果#1,将ptrb用dynamic_cast转换为派生类类型的指针,dynamic_cast会返回空指针
    //std::cout << dynamic_cast<Derive2&>(bb) << std::endl; //如果#3,将bb用dynamic_cast转换为派生类类型的引用,dynamic_cast会抛出异常
    Derive2 d2;
    Base& bd = d2; //#4: 基类类型的引用bb引用派生类对象d2
    Base* ptrd = &d2; //#2: 基类类型的指针ptrd指向派生类对象d2
    Derive2* d2ptr = dynamic_cast<Derive2*>(ptrd);
    Derive2& d2ref = dynamic_cast<Derive2&>(bd);
    std::cout << std::is_same_v<decltype(d2ptr), Derive2*> <<std::endl; //如果#2,将ptrd用dynamic_cast转换为派生类类型的指针,dynamic_cast会转换成功返回派生类类型的指针
    std::cout << std::is_same_v<decltype(d2ref), Derive2&> <<std::endl; //如果#4,将bd用dynamic_cast转换为派生类类型的引用,dynamic_cast会转换成功返回派生类类型的引用
    return 0;
}

在这里插入图片描述

● 虚函数在基类中的定义
– 引入缺省逻辑
– 可以通过 = 0 声明纯虚函数,相应地构造抽象基类
● 虚函数在派生类中的重写( override )

struct Base
{
    void fun() //无virtual关键字
    {
        std::cout << "void Base::fun()\n";
    }
};
struct Derive : Base
{
    void fun()
    {
        std::cout << "void Derive::fun()\n";
    }
};
int main()
{
    Derive d;
    d.fun(); //调用Derive::fun()

    Base& b = d;
    b.fun(); //调用Base::fun()
    return 0;
}

在这里插入图片描述

struct Base
{
    virtual void fun() //有virtual关键字
    {
        std::cout << "virtual void Base::fun()\n";
    }
};
struct Derive : Base
{
    void fun()
    {
        std::cout << "void Derive::fun()\n";
    }
};
int main()
{
    Derive d;
    d.fun(); //调用Derive::fun()

    Base& b = d;
    b.fun(); //调用Derive::fun()
    return 0;
}

在这里插入图片描述

struct Base
{
    virtual void fun() //有virtual关键字
    {
        std::cout << "virtual void Base::fun()\n";
    }
};
struct Derive : Base
{
    void fun() //函数签名与基类中的虚函数void fun()相同,是后者在派生类中的重写(override)
    {
        std::cout << "void Derive::fun()\n";
    }
};
void proc(Base& b) //传入的参数类型不一样
{
    b.fun(); //内部逻辑会表现出不同的行为,称为动态多态(也叫运行期多态(即通过动态类型实现的运行期的多态)),其基础就是virtual
}
int main()
{
    Base b; //b的静态类型和动态类型都是Base
    proc(b); //调用Base::fun()

    Derive d; //动态类型是Derive
    proc(d); //调用Derive::fun()
    return 0;
}

在这里插入图片描述

– 函数签名保持不变(返回类型可以是原始返回指针 / 引用类型的派生指针 / 引用类型)

struct Base2 {};
struct Derive2 : Base2 {};
struct Base
{
    virtual Base2* fun()
    {
        std::cout << "virtual Base2* Base::fun()\n";
        static Base2 b2;
        return &b2;
    }
};
struct Derive : Base
{
    Derive2* fun() //即使返回类型不同也构成重写
    //Derive2& fun() //Error: Virtual function 'fun' has a different return type ('Derive2 &') than the function it overrides (which has return type 'Base2 *')
    {
        std::cout << "Derive2* Derive::fun()\n";
        static Derive2 d2;
        return &d2;
    }
};
void proc(Base& b)
{
    b.fun();
}
int main()
{
    Base b;
    proc(b); //OK

    Derive d;
    proc(d); //OK
    return 0;
}

在这里插入图片描述

struct Base2 {};
struct Derive2 : Base2 {};
struct Base
{
    virtual void fun()
    {
        std::cout << "virtual void Base::fun()\n";
    }
};
struct Derive : Base
{
    void fun(int) //参数列表不同,与继承自Base中的virtual void fun()构成重载
    //int fun() //Error: Virtual function 'fun' has a different return type ('int') than the function it overrides (which has return type 'void')
    {
        std::cout << "void Derive::fun(int)\n";
        //return 3;
    }
};
void proc(Base& b)
{
    b.fun();
}
int main()
{
    Base b;
    proc(b); //OK

    Derive d;
    proc(d); //OK
    return 0;
}

在这里插入图片描述

struct Base
{
    virtual void fun() = 0; //纯虚函数,不需要给出一个实现,在vtable指向的地方开了一个槽但是并不指向任何的东西
    //例如:triangle继承自shape,right triangle继承自triangle,triangle和right triangle都可以画出形状,但不能画出shape的形状
};
struct Derive : Base
{
    void fun()
    {
        std::cout << "void Derive::fun()\n";
    }
};
void proc(Base& b)
{
    b.fun();
}
int main()
{
    Derive d;
    Base* ptr = &d;
    Base& ref = d;
    proc(*ptr); //OK
    proc(ref); //OK
    return 0;
}

在这里插入图片描述

struct Base
{
    virtual void fun() = 0;
};
struct Derive : Base
{
    void fun()
    {
        std::cout << "void Derive::fun()\n";
    }
};
struct Derive2 : Base
{
    //Derive2中并没有重写基类中的virtual void fun() = 0,因此Derive2也是一个抽象基类
};

void proc(Base& b)
{
    b.fun();
}
int main()
{
    Derive2 d; //Error: Variable type 'Derive2' is an abstract class
    return 0;
}
struct Base
{
    virtual void fun() = 0;
};
struct Derive : Base
{
    void fun() //重写
    {
        std::cout << "void Derive::fun()\n";
    }
};
struct Derive2 : Derive
{
    //Derive2的公有成员中有继承自Derive的void fun(),不是抽象基类
};

void ptrProc(Base* b)
{
    b->fun();
}
void refProc(Base& b)
{
    b.fun();
}
int main()
{
    Derive2 d;
    ptrProc(&d); //OK
    refProc(d); //OK
    return 0;
}

在这里插入图片描述

struct Base
{
    virtual void fun() = 0
    {
            
    } //原因尚待求索,Semantic Issue: Initializer on function does not look like a pure-specifier
};
struct Base
{
    virtual void fun() = 0;
};
void Base::fun() //应用场景非常少,但技术上是可以的,但并不改变Base是一个抽象基类,所以不能定义Base类对象
{
    std::cout << "Defination of virtual void Base::fun() = 0 out of class OK\n";
}
struct Derive : Base
{
    void fun()
    {
        std::cout << "void Derive::fun()\n";
    }
    //相应地,如果派生类中没有定义重写逻辑,那派生类仍是一个抽象基类
};
void ptrProc(Base* b)
{
    b->fun();
}
void refProc(Base& b)
{
    b.fun();
}
int main()
{
    Derive d;
    ptrProc(&d); //OK
    refProc(d); //OK
    return 0;
}

在这里插入图片描述

struct Base
{
    virtual void fun() = 0;
};
void Base::fun() //纯虚函数的一些辅助逻辑
{
    std::cout << "Defination of virtual void Base::fun() = 0 out of class OK\n";
}
struct Derive : Base
{
    void fun()
    {
        Base::fun(); //显式调用
        std::cout << "void Derive::fun()\n";
    }
};
void ptrProc(Base* b)
{
    b->fun();
}
void refProc(Base& b)
{
    b.fun();
}
int main()
{
    Derive d;
    ptrProc(&d); //OK
    refProc(d); //OK
    return 0;
}

在这里插入图片描述

– 虚函数特性保持不变

struct Base
{
    virtual void fun()
    {
        std::cout << "virtual void fun()\n";
    }
};
struct Derive : Base
{
    void fun() //无virtual关键字
    {
        std::cout << "void Derive::fun()\n";
    }
};
struct Derive2 : Derive
{
    void fun() //无virtual关键字
    {
        std::cout << "void Derive2::fun()\n";
    }
};
void ptrProc(Base* b)
{
    b->fun();
}
void refProc(Base& b)
{
    b.fun();
}
int main()
{
    Base b;
    ptrProc(&b);
    refProc(b);
    std::cout << '\n';

    Derive d;
    ptrProc(&d); //调用void Derive::fun()
    refProc(d); //同上
    std::cout << '\n';

    Derive2 d2;
    ptrProc(&d2); //调用void Derive::fun()
    refProc(d2); //同上
    std::cout << "\nTHIS IS DYNAMIC POLYMORPHISM (unable to std::cout Chinese in QT)\n";
    return 0;
}

在这里插入图片描述

– override 关键字

struct Base
{
    virtual void fun()
    {
        std::cout << "virtual void fun()\n";
    }
};
struct Derive : Base
{
    void fun(int) override //编译器会报错,方便代码调试 since C++11,Error: Non-virtual member function marked 'override' hides virtual member function
    {
        std::cout << "void Derive::fun()\n";
    }
};

参考
深蓝学院: C++基础与深度解析
Virtual Function and Multilevel Inheritance

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

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

相关文章

Linux常用命令--目录文件管理

Linux常用命令一、目录与文件管理场景一&#xff1a;我是谁场景二&#xff1a;我在哪&#xff1f;跳转目录场景三&#xff1a;查看目录内容场景四&#xff1a;创建目录场景五&#xff1a;如何在目录中创建一个文件场景六&#xff1a;如何编辑一个简单文本文件&#xff1f;场景七…

用Python实现自动发送周报给老板,强制周报不用愁

前言 老板每周要求写周报上交&#xff1f; 像我这种记性不好的&#xff0c;一个月四周忘记三次 只能用点小技术&#xff0c;用Python写个小工具&#xff0c;让它每周帮我给老板发周报~ Github: Weekday 小工具 目标细化 SMTP发送邮件, 用smtplib 读取配置文件 发件人 收件…

uml图六种箭头的含义

在看uml图忘记箭头含义记录一下 泛化 概念&#xff1a;泛化是一种一般与特殊、一般与具体之间关系的描述&#xff0c;具体描述建立在一般描述的基础之上&#xff0c;并对其进行了扩展。在java中用来表示继承的关系。 表示方法&#xff1a;用实线空心三角箭头表示。 实现 概念…

Postman中cookie的操作

在接口测试中&#xff0c;某些接口的调用&#xff0c;需要带入已有Cookie&#xff0c;比如有些接口需要登陆后才能访问。 Postman接口请求使用Cookie有如下两种方式&#xff1a; 1、直接在头域中添加Cookie头域&#xff0c;适用于已经知道请求所用Cookie数据的情况。 2、使用…

Vue3.0学习笔记

文章目录一. Vue开始1.1 渐进式框架1.2 单文件组件1.3 API 风格1.3.1 选项式 API1.3.2 组合式 API二. Vue基础2.1 创建一个 Vue 应用2.1.1 应用实例2.1.2 根组件2.1.3 挂载应用2.1.4 DOM 中的根组件模板2.1.5 应用配置2.1.6 多个应用实例2.2 模板语法2.2.1 文本插值2.2.2 原始 …

4K、高清、无水印视频素材库

推荐5个可以免费下载视频素材的网站&#xff0c;建议收起来&#xff01; 1、菜鸟图库 视频素材下载_mp4视频大全 - 菜鸟图库 菜鸟图库主要提供设计素材为主&#xff0c;自媒体等相关素材也很多&#xff0c;像商用图片、背景图、视频素材、音频素材都很齐全。视频素材全部都是…

不同接口的LCD硬件操作原理

不同接口的LCD硬件操作原理 文章目录不同接口的LCD硬件操作原理参考资料&#xff1a;一、 应用工程师眼里看到的LCD1.1 像素的颜色怎么表示二、 驱动工程师眼里看到的LCD2.1 统一的LCD硬件模型2.2 MCU常用的8080接口LCD模组2.3 MPU常用的TFT RGB接口2.4 有一个MIPI标准参考资料…

大专生现在转行IT可行吗?

抛开其他具体情况不说&#xff0c;大专是行业基本要求&#xff0c;大专学习转IT完全没有问题&#xff01; 关于IT行业的学历要求 大专学历是满足基本入行要求的&#xff0c;并且大专可以选择的方向也很多&#xff0c;比如开发、云计算、大数据、数据分析等方向&#xff0c;这…

云计算ACP云服务器ECS实例题库(二)

&#x1f618;作者简介&#xff1a;一名99年软件运维应届毕业生&#xff0c;正在自学云计算课程。&#x1f44a;宣言&#xff1a;人生就是B&#xff08;birth&#xff09;和D&#xff08;death&#xff09;之间的C&#xff08;choise&#xff09;&#xff0c;做好每一个选择。&…

java面试题-容器

目录 1、Java 容器都有哪些&#xff1f; 2.、Collection 和 Collections 有什么区别&#xff1f; 3、 List、Set、Map 之间的区别是什么&#xff1f; 4、HashMap 和 Hashtable 有什么区别&#xff1f; 5、 如何决定使用 HashMap 还是 TreeMap&#xff1f; 6、 说一下 Has…

融资、量产和一栈式布局,这家Tier 1如此备战高阶智驾决赛圈

作者 | Bruce 编辑 | 于婷 从早期的ADAS&#xff0c;到高速/城市NOA&#xff0c;智能驾驶的竞争正逐渐升级&#xff0c;这对于车企和供应商的核心技术和产品布局都是一个重要的考验。 部分智驾供应商已经在囤积粮草&#xff0c;响应变化。 2023刚一开年&#xff0c;智能驾驶…

C语言消消乐游戏代码

C和C游戏趣味编程》一书各个章节的案例代码&#xff0c;每章案例逐步利用学到的语法知识。 本章我们将编写十字消除游戏&#xff0c;用户点击空白方块&#xff0c;沿其上下左右方向寻找第一个彩色方块&#xff0c;如果有两个或两个以上颜色一致&#xff0c;就将其消除。在进度…

Spark 3.1.1 shuffle fetch 导致shuffle错位的问题

背景 最近从数据仓库小组那边反馈了一个问题,一个SQL任务出来的结果不正确&#xff0c;重新运行一次之后就没问题了&#xff0c;具体的SQL如下&#xff1a; select col1,count(1) as cnt from table1 where dt 20230202 group by col1 having count(1) > 1这个问题是偶发…

SpringBoot 整合JWT实现基于自定义注解的-登录请求验证拦截(保姆级教学,附:源码)

学习目标&#xff1a; Spring Boot 整合JWT实现基于自定义注解的 登录请求接口拦截 例&#xff1a; 一篇掌握 JWT 入门知识1.1 在学习SpringBoot 整合JWT之前&#xff0c;我们先来说说JWT进行用户身份验证的流程 1:客户端使用用户名和密码请求登录 2:服务端收到请求&#xf…

spring中@Autowire和@Resource的区别在哪里?

介绍今天使用Idea写代码的时候&#xff0c;看到之前的项目中显示有warning的提示&#xff0c;去看了下&#xff0c;是如下代码?Autowire private JdbcTemplate jdbcTemplate;提示的警告信息Field injection is not recommended Inspection info: Spring Team recommends: &quo…

AntDB-M设计之内存结构

亚信科技专注通信行业多年&#xff0c;AntDB数据库从诞生开始&#xff0c;就面对通信级的大数据量应用场景挑战&#xff0c;在性能、稳定性、规模化等方面获得了超过10年的通信核心业务系统验证&#xff0c;性能峰值达到每秒百万的通信核心交易量。AntDB-M&#xff08;AntDB内存…

CMMI有哪几个级别,每个级别有哪些其特征

CMMI 一共分五个级别&#xff0c;一级低&#xff0c;五级高&#xff0c;一般初次认证CMMI从三级或者二级开始。 1、CMMI一级&#xff0c;完成级。 在完成级水平上&#xff0c;企业对项目的目标与要做的努力很清晰。项目的目标得以实现。一般来说&#xff0c;公司的初始阶段就是…

工作记录------@Accessors(chain = true)引起的BUG,Excel导入时获取不到值

工作记录------Accessors(chain true)引起的BUG&#xff0c;Excel导入时获取不到值 如题所示 背景&#xff1a;在进行文件excel文件导入时&#xff0c;发现实体类获取到的属性值都为null。 框架&#xff1a;com.alibaba.excel 2.2.0的版本。 结论&#xff1a;首先说下结论 如…

2021年职业院校技能大赛“网络安全”项目江西省A模块

目录 一、竞赛时间 三、竞赛任务书内容 &#xff08;一&#xff09;拓扑图 &#xff08;二&#xff09;A模块基础设施设置/安全加固&#xff08;200分&#xff09; A-1&#xff1a;登录安全加固 1.密码策略&#xff08;Web&#xff09; a.最小密码长度不少于8个字符&…

C++入门基础

本章内容&#xff1a; 一、C前言 1. 什么是C C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对于复杂的问题&#xff0c;规模较大的程序&#xff0c;需要高度的抽象和建模时&#xff0c;C语言则不合适。为了解决软件危机&#xff0c; 20世纪80年代&#x…