C++ · 代码笔记5 · 探索多态与虚函数

目录

  • 前言
  • 011虚函数_使用基类指针实现覆盖特性
  • 012虚函数_使用引用实现覆盖特性
  • 013使用多态场景小例程
  • 020构成多态的条件
  • 030虚析构函数
  • 040纯虚函数
  • 051typeinfo运算符_获取类型信息
  • 052typeinfo_根据不同类型进行不同操作

前言

  本笔记所涉及到的编程环境与 《C++ · 代码笔记1 · 从C到C++》 中的编程环境一致,具体可参考此笔记。

011虚函数_使用基类指针实现覆盖特性

  相关代码:

#include <iostream>
using namespace std;

class Base
{
public:
    virtual void show()
    {
        cout << "Base show" << endl;
    }
};

class Derived : public Base
{
public:
    // 使用 override 关键字明确表示覆盖
    void show() override
    {
        cout << "Derived show" << endl;
    }
};

int main()
{
    // 基类指针调用基类函数
    Base *bPtr = new Base;
    bPtr->show();

    // 派生类指针调用派生类函数
    Derived *dPtr = new Derived();
    dPtr->show();

    // 基类指针指向派生类对象,基类show函数已经被声明为了虚函数,此时会表现出多态性。
    // 当基类相关函数声明为虚函数时,派生类相关函数将会覆盖基类相关函数,
    // 此时会根据基类指针指向的对象类型调用相关函数
    bPtr = dPtr;
    bPtr->show();

    return 0;
}

  运行结果:

在这里插入图片描述

012虚函数_使用引用实现覆盖特性

  相关代码:

#include <iostream>
using namespace std;

class Base
{
public:
    virtual void show()
    {
        cout << "Base show" << endl;
    }
};

class Derived : public Base
{
public:
    // 使用 override 关键字明确表示覆盖
    void show() override
    {
        cout << "Derived show" << endl;
    }
};

int main(int argc, char const *argv[])
{
    Base b_obj;
    Base &br = b_obj;
    br.show();

    Derived d_obj;
    Derived &dr = d_obj;
    dr.show();

    // 基类引用派生类对象,此时由于基类的函数被声明为了虚函数,表现出了多态性,
    // 所以此时调用的是派生类的函数
    Base &brd = d_obj;
    brd.show();

    return 0;
}

  运行结果:

在这里插入图片描述

013使用多态场景小例程

  相关代码:

#include <iostream>
using namespace std;

/**
 * 这个例子中就体现出了多态的优点,如果使用非多态的方式实现这个程序,
 * 那么将会需要定义很多指针,但使用了多态仅使用一个指针变量即可。
 */

// 动物类
class Animal
{
public:
    virtual void makeSound() const
    {
        cout << "动物发出声音!" << endl;
    }
    virtual ~Animal() {}
};

// 狗类
class Dog : public Animal
{
public:
    void makeSound() const override
    {
        cout << "--狗狗:汪汪!" << endl;
    }
};

// 猫类
class Cat : public Animal
{
public:
    void makeSound() const override
    {
        cout << "--猫咪:喵喵!" << endl;
    }
};

// 鸟类
class Bird : public Animal
{
public:
    void makeSound() const override
    {
        cout << "--小鸟:啾啾!" << endl;
    }
};

// 大象类
class Elephant : public Animal
{
public:
    void makeSound() const override
    {
        cout << "--大象:哞哞!" << endl;
    }
};

int main()
{
    Animal *p = new Animal;
    p->makeSound();

    // 创建不同类型的动物
    p = new Dog;
    p->makeSound();
    p = new Cat;
    p->makeSound();
    p = new Bird;
    p->makeSound();
    p = new Elephant;
    p->makeSound();

    // 释放内存
    delete p;

    return 0;
}

  运行结果:

在这里插入图片描述

020构成多态的条件

  相关代码:

#include <iostream>
using namespace std;

/**
 * 构成多态条件:
 *  1、必须存在继承关系;
 *  2、继承关系中必须有同名的虚函数,并且它们是覆盖关系(函数原型相同)。
 *  3、存在基类的指针,通过该指针调用虚函数。
 */

class Base
{
public:
    virtual void func();
    virtual void func(int);
    void func(double);
};
void Base::func()
{
    cout << "void Base::func()" << endl;
}
void Base::func(int n)
{
    cout << "void Base::func(int)" << endl;
}
void Base::func(double n)
{
    cout << "void Base::func(double)" << endl;
}

class Derived : public Base
{
public:
    void func();
    void func(char *);
    void func(double);
};
void Derived::func()
{
    cout << "void Derived::func()" << endl;
}
void Derived::func(char *str)
{
    cout << "void Derived::func(char *)" << endl;
}
void Derived::func(double n)
{
    cout << "void Derived::func(double)" << endl;
}

/** 本例是一个简单的多态例程。以本例来说,当一个基类指针指向派生类对象时,
 * 会调用的函数有以下三种情况:
 *   1、派生类【重写】了基类的虚函数,在调用处传递完全符合【重写】函数的形参列表参数时,
 *       调用派生类重写的函数,如下面的 a 注释处的代码
 *   2、派生类【未重写】基类的虚函数,在调用处传递完全符合【未重写】函数的形参列表参数时,
 *       调用基类的虚函数,如下面的b注释处的代码
 *   3、仅派生类有该函数,基类没有相关的虚函数,则不能调用,否则会编译报错,
 *       如下面c注释处的代码
 *   4、补充内容:当基类成员函数未声明为虚函数,即使是派生类对其进行了隐藏,
 *       但调用的仍会是基类的函数,如注释d处代码所示。
 **/
int main(int argc, char const *argv[])
{
    Base *p = new Derived();
    // a、调用的是派生类的虚函数,构成了多态。
    p->func();
    // b、调用的是基类的虚函数,因为派生类中没有函数覆盖它。
    p->func(10);
    // c、编译报错,因为通过基类的指针只能访问从基类继承过去的成员,不能访问派生类新增的成员。
    // p->func("test");
    // p指针是基类指针且基类函数未被声明为virtual,这将会调用基类成员函数
    p->func(1.0);

    return 0;
}

  运行结果:
在这里插入图片描述

030虚析构函数

  相关代码:

#include <iostream>

/**
 *  基类的析构函数如果不声明为虚析构函数,那么当基类指针指向派生类对象的情况下
 * 销毁对象仅调用基类的析构函数,不会调用派生类的析构函数,
 * 这会导致派生类中的资源无法得到释放,产生内存泄漏的危险
 **/

class Base
{
public:
    Base()
    {
        std::cout << "基类构造函数被调用" << std::endl;
    }

    virtual ~Base()
    {
        std::cout << "基类析构函数被调用" << std::endl;
    }
};

class Derived : public Base
{
public:
    Derived()
    {
        std::cout << "派生类构造函数被调用" << std::endl;
    }

    ~Derived() override
    {
        std::cout << "派生类析构函数被调用" << std::endl;
    }
};

int main()
{
    Base *obj = new Derived();

    delete obj;

    return 0;
}

  运行结果:

在这里插入图片描述

040纯虚函数

  相关代码:

#include <iostream>

/**
 * 1、仅能在类中定义纯虚函数
 * 2、纯虚函数必须要在派生类中定义,且全部完成实现
 */

class A
{
public:
    // 定义纯虚函数vFunc1,没有函数体,需要在派生类中实现
    virtual void vFunc1(void) = 0;
    // 定义纯虚函数vFunc2,没有函数体,需要在派生类中实现
    virtual void vFunc2(void) = 0;
};

class B : public A
{
public:
    // 声明B类对A类的vFunc1函数进行覆盖实现
    void vFunc1(void) override;
};
// B类对A类的vFunc1函数的具体实现
void B::vFunc1(void)
{
    std::cout << "派生类 B类 实现了 抽象基类的vFunc1函数" << std::endl;
}

class C : public B
{
public:
    // 声明C类对A类的vFunc2函数进行覆盖实现
    void vFunc2(void) override;
};
// C类对A类的vFunc2函数的具体实现
void C::vFunc2(void)
{
    std::cout << "派生类 C类 实现了 抽象基类的vFunc2函数" << std::endl;
}

int main(int argc, char const *argv[])
{
    C c_obj;

    A *pa = &c_obj;

    // 通过基类指针调用派生类实现的vFunc1函数
    pa->vFunc1();
    // 通过基类指针调用派生类实现的vFunc2函数
    pa->vFunc2();

    return 0;
}

  运行结果:

在这里插入图片描述

051typeinfo运算符_获取类型信息

  相关代码:

#include <iostream>
#include <typeinfo>

// 定义一个简单的类
class MyClass
{
public:
    MyClass() {}
};

// 定义一个简单的结构体
struct MyStruct
{
    int member;
};

int main()
{
    int i = 10;
    double d = 10.5;
    char c = 'a';
    std::string s = "Hello";
    MyClass obj;
    struct MyStruct struct_obj;

    // 获取和打印不同类型的类型信息
    std::cout << "typeid(1).name(): " << typeid(1).name() << std::endl;
    std::cout << "typeid(1.0).name(): " << typeid(1.0).name() << std::endl;
    std::cout << "typeid(int).name(): " << typeid(i).name() << std::endl;
    std::cout << "typeid(double).name(): " << typeid(d).name() << std::endl;
    std::cout << "typeid(char).name(): " << typeid(c).name() << std::endl;
    std::cout << "typeid(std::string).name(): " << typeid(s).name() << std::endl;
    std::cout << "typeid(MyClass).name(): " << typeid(obj).name() << std::endl;
    std::cout << "typeid(MyStruct).name(): " << typeid(struct_obj).name() << std::endl;


    // 比较两个类型是否相同
    if (typeid(obj) == typeid(MyClass))
    {
        std::cout << "obj is of type MyClass" << std::endl;
    }

    if (typeid(struct_obj) == typeid(MyStruct))
    {
        std::cout << "struct_obj is of type MyStruct" << std::endl;
    }

    return 0;
}

  运行结果:

在这里插入图片描述

052typeinfo_根据不同类型进行不同操作

  相关代码:

#include <iostream>
#include <typeinfo>
using namespace std;

// 基类
class Animal
{
public:
    virtual void speak() const
    {
        cout << "我是一只动物。" << endl;
    }
    virtual ~Animal(){}; // 将析构函数声明为虚的
};

// 派生类
class Dog : public Animal
{
public:
    void speak() const override
    {
        cout << "我是一只狗。汪汪!" << endl;
    }
    ~Dog() {}
};

// 另一个派生类
class Cat : public Animal
{
public:
    void speak() const override
    {
        cout << "我是一只猫。喵喵!" << endl;
    }
    ~Cat() {}
};

int main(int argc, char const *argv[])
{
    Animal *animal;
    int choice;

    cout << "输入一个数字(1 表示狗,2 表示猫): ";
    cin >> choice;

    if (choice == 1)
    {
        animal = new Dog();
    }
    else if (choice == 2)
    {
        animal = new Cat();
    }
    else
    {
        animal = new Animal();
    }

    // 使用 typeid 进行类型检查
    if (typeid(*animal) == typeid(Dog))
    {
        cout << "这只动物是一只狗。" << endl;
    }
    else if (typeid(*animal) == typeid(Cat))
    {
        cout << "这只动物是一只猫。" << endl;
    }
    else
    {
        cout << "这只动物是一只普通的动物。" << endl;
    }

    // 清理动态分配的内存
    delete animal;

    return 0;
}

  运行结果:
在这里插入图片描述

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

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

相关文章

7款前端实战型项目特效分享(附在线预览)

分享7款实用性的前端动画特效 其中有canvas特效、css动画、svg动画等等 下方效果图可能不是特别的生动 那么你可以点击在线预览进行查看相应的动画特效 同时也是可以下载该资源的 CSS春节灯笼特效 基于CSS实现的灯笼特效 灯笼会朝左右两个方向来回的摆动着 以下效果图只能体现…

knife4j生产环境禁止打开页面

Knife4j是一个集Swagger2 和 OpenAPI3为一体的增强解决方案&#xff0c;官网地址&#xff1a;Knife4j 集Swagger2及OpenAPI3为一体的增强解决方案. | Knife4j 考虑到安全性问题&#xff0c;在实际服务部署到生产环境后就需要禁用到swagger页面的展示&#xff0c;这个时候只需…

Jmeter查看结果树之查看响应的13种详解方法

Jmeter查看结果树查看响应有哪几种方法&#xff0c;可通过左侧面板底部的下拉框选择: 01 Text 查看结果树中请求的默认格式为Text&#xff0c;显示取样器结果、请求、响应数据3个部分内容。 取样器结果&#xff1a; 默认Raw展示&#xff0c;可以切换为Parsed视图&#xff0c…

Selenium常见元素操作,学完就能上手

web端自动化测试在回归测试、兼容测试扮演着举足轻重的角色&#xff0c;作为web自动化测试工程师&#xff0c;日常工作主要的部分就是编写自动化测试用例代码&#xff0c;借助的开源框架来说&#xff0c;目前市场占有率较高的仍然是selenium。 如何使用selenium完成web页面元素…

算法---双指针练习-5(有效三角形的个数)

有效三角形的个数 1. 题目解析2. 讲解算法原理3. 编写代码 1. 题目解析 题目地址&#xff1a;有效三角形的个数 2. 讲解算法原理 算法的基本思想是首先对数组进行排序&#xff0c;然后使用三个指针left、right和cur&#xff0c;分别表示三个元素的索引。其中&#xff0c;left指…

PCIE UVM SVT

PCIE中存在UI概念,unit interval 定义为每个bit 传输需要的时间。 Introduction VC UVM VIP for PCIE 是一个兼容UVM验证方法学的高级验证组合以及数据类型的套件。 VC UVM VIP是基于uvm_agen 的架构图如下所示: svt_pcie_device_agent:它包含Driver,Target,Requeseter,I…

CDR(CorelDRAW)2024最新汉化注册补丁包下载

CorelDRAW 2024是一款功能强大的平面设计软件&#xff0c;广泛应用于图形设计、编辑照片以及创建网站等领域。凭借对高级操作系统的支持、多监视器查看和4K显示屏的兼容性&#xff0c;它让初始用户、图形专家、小型企业主和设计爱好者都能自信快速地交付专业级结果。 CorelDRA…

解决方案|珈和科技推出农业特色产业数字化服务平台

今年中央一号文件提出&#xff0c;鼓励各地因地制宜大力发展特色产业&#xff0c;支持打造乡土特色品牌。 然而&#xff0c;农业特色产业的生产、加工和销售仍然面临诸多挑战。产品优质不能优价&#xff0c;优质不能优用的现象屡见不鲜&#xff0c;产业化程度低、生产附加值不…

套接字编程 --- 一

目录 1. 预备知识 1.1. 端口号 1.2. 认识TCP协议 1.3. 认识UDP协议 1.4. 网络字节序 2. socket 2.1. socket 常见系统调用 2.1.1. socket 系统调用 2.1.2. bind 系统调用 2.1.3. recvfrom 系统调用 2.1.4. sendto系统调用 2.3. 其他相关接口 2.3.1. bzero 2.3.2…

智慧公厕的三大特点:信息化、数字化、智慧化

智慧公厕是以物联网、互联网、大数据、云计算等先进技术为支撑&#xff0c;对公共厕所的使用、运营、管理、养护进行全方位高效应用的创新型公厕。它具有三大显著特点&#xff1a;&#xff08;ZonTree中期&#xff09;信息化、数字化和智慧化。本文以智慧公厕源头实力厂家广州中…

【鸿蒙 HarmonyOS 4.0】常用组件:List/Grid/Tabs

一、背景 列表页面&#xff1a;List组件和Grid组件&#xff1b; 页签切换&#xff1a;Tabs组件&#xff1b; 二、列表页面 在我们常用的手机应用中&#xff0c;经常会见到一些数据列表&#xff0c;如设置页面、通讯录、商品列表等。下图中两个页面都包含列表&#xff0c;“…

【PCIe 链路训练】之均衡(equalization)

1、概述 这篇文章简单介绍一下PCIE phy的均衡原理和过程,USB phy,ethernet phy这些高速的串行serdes也有相同或者相似的结构。可以不用太关注其中的细节,等到debug的时候可以查询协议,但是需要了解这个故事讲的大概内容。整个equalization过程是controller和phy一起配合完成…

什么是智慧公厕?智慧公厕设备有哪些

在现代社会&#xff0c;公共厕所作为城市基础设施的重要一环&#xff0c;承载着城市卫生、居民生活品质的重要责任。然而&#xff0c;传统公厕存在的问题仍然不可忽视&#xff1a;脏乱差、资源浪费、安全隐患等等。 为了解决这些问题&#xff0c;针对公共厕所日常使用、运营管…

go语言-k8s宿主信息采集运维脚本

背景 由于工作需要&#xff0c;需要采集k8s集群中的宿主相关信息&#xff0c;包括cpu,memory,lvm,标签等信息。通常作为SRE会主选shell或python脚本来实现。但最近我们团队主流开发语言已经切换到golang.所以本次尝试用go语言来写写运维脚本。 实现流程图 代码实现 package m…

[笔记]Crash Course Computer Science

二进制 byte 在电脑中的单位换算&#xff1a; kilobyte 千字节 megabyte 兆字节 gigabyte 千兆字节 1kb210bit 1024byte 1000b 1Mb 220bit 1024kb 1Gb 230bit 1TB1000GB 1GB十亿字节1000MB10^6KB Gb 和 GB 一般而言GB用于文件&#xff0c;Gb用于通信。B代表Byte&…

类与对象(三)--static成员、友元

文章目录 1.static成员1.1概念&#x1f3a7;面试题✒️1.2static的特性&#x1f3a7;1.3思考&#x1f3a7; 2.友元2.1什么是友元&#xff1f;&#x1f3a7;2.2两种友元关系&#xff1a;&#x1f3a7; 1.static成员 1.1概念&#x1f3a7; &#x1f50e; static关键字用于声明类…

SpringCloudAlibaba微服务之Nacos架构及基础概念梳理

SpringCloudAlibaba微服务之Nacos架构及基础概念梳理 文章目录 SpringCloudAlibaba微服务之Nacos架构及基础概念梳理1. 官网介绍1. 简介2. Naocs是什么3. Nacos 地图4. Nacos 生态图 2. Nacos 架构1. 基本架构及概念1. 服务 (Service)2. 服务注册中心 (Service Registry)3. 服务…

第四届信息通信与软件工程国际会议(ICICSE 2024)即将召开!

2024年第四届信息通信与软件工程国际会议&#xff08;ICICSE 2024&#xff09;将于2024年5月10-12日在中国北京举办。本次会议由北京工业大学、IEEE以及Comsoc 联合主办。随着当今社会信息化的高速发展&#xff0c;电子信息技术的应用更是随处可见。其中&#xff0c;信息通信与…

K倍区间(蓝桥杯)

文章目录 K倍区间题目描述前缀和数学优化代码部分解释 K倍区间 题目描述 给定一个长度为 N的数列&#xff0c;A1,A2,…AN&#xff0c;如果其中一段连续的子序列 Ai,Ai1,…Aj 之和是 K的倍数&#xff0c;我们就称这个区间 [i,j]是 K倍区间。 你能求出数列中总共有多少个 K倍区…

Zabbix(四)

Zabbix Proxy zabbix作为一个分布式监控系统(分布式监控解决方案)&#xff0c;支持通过代理(proxy)收集zabbix agent的监控数据&#xff0c;然后由zabbix proxy再把数据发送给zabbix server&#xff0c;也就是zabbix proxy 可以代替zabbix server收集监控数据&#xff0c;然后…