【C++进阶学习】第四弹——多态——迈向C++更深处的关键一步

news2024/11/24 22:52:43

前言:


在前面我们已经学习了C++中继承的相关知识,已经体会到C++在与C语言的对比中的便捷性,但是有一些问题并没有被解决,比如继承中如何使不同的派生类公用基类的一个函数,这就需要多态的知识,而且,有一个很重要的点要知道,多态是以后找工作的时候经常经常被问到的一个知识

目录

一、多态的概念

二、多态的实现

2.1 多态的构成条件

2.2 override 和 final 关键字(C++11)

2.3 重载、覆盖(重写)、隐藏(重定义)的对比

三、抽象类

四、总结


一、多态的概念

C++中多态的概念通俗来讲就是多种形态,同样的东西,在不同场景下发挥着不同的作用;体现在代码上其实就是同一个虚函数,在不同的派生类中可能发挥着不同的作用,就比如一把雨伞既可以用来挡雨,也可以用来遮阳

二、多态的实现

2.1 多态的构成条件

在C++中,要实现多态,需要满足以下几个条件:

1、基类中必须包含至少一个虚函数: 虚函数是在基类中声明的,并在派生类中重写的函数。通过在函数声明前加上virtual关键字来声明虚函数。虚函数是实现动态多态的关键,因为它允许在运行时根据对象的实际类型来调用相应的函数。

2、通过基类指针或引用调用虚函数: 多态通常通过基类的指针或引用来实现。当使用基类指针或引用指向派生类对象时,调用虚函数将根据对象的实际类型(而不是指针或引用的类型)来决定调用哪个函数。

3、派生类必须重写(override)基类的虚函数: 派生类需要重写基类中的虚函数,以提供特定于派生类的实现。重写时,函数签名(包括返回类型、函数名和参数列表)必须与基类中的虚函数完全匹配。在C++11及以后的版本中,可以使用override关键字显式声明派生类中的函数是重写基类的虚函数,这有助于编译器检查是否正确重写了虚函数。

4、使用虚析构函数: 如果基类中使用了虚函数,通常建议也将析构函数声明为虚函数。这是因为当通过基类指针删除派生类对象时,如果析构函数不是虚函数,将只会调用基类的析构函数,而不会调用派生类的析构函数,这可能导致资源泄漏。

下面是一个简单的示例,展示了多态的构成条件:

#include <iostream>

class Base {
public:
    virtual void show() {  // 虚函数
        std::cout << "Base show()" << std::endl;
    }
    virtual ~Base() {}  // 虚析构函数
};

class Derived : public Base {
public:
    void show() override {  // 重写基类的虚函数
        std::cout << "Derived show()" << std::endl;
    }
};

int main() {
    Base* ptr = new Derived();  // 基类指针指向派生类对象
    ptr->show();  // 调用派生类的show(),显示 "Derived show()"
    delete ptr;  // 正确调用析构函数
    return 0;
}

在这个例子中,Base类有一个虚函数show()和一个虚析构函数。Derived类重写了show()函数。在main()函数中,通过基类指针ptr调用show()函数,实际执行的是Derived类的show()函数,展示了动态多态的效果。同时,删除ptr时,会正确调用DerivedBase的析构函数。

2.2 override final 关键字(C++11)

通过上面的限制条件,我们已经可以看出要想构成多态还是不容易的,在我们平时写多态的时候,经常可能因为某些小差错而导致构建失败,而这种编译错误是不会指出来的,所以在C++11中提供了这两个关键字来帮助我们更容易的实现多态

override 关键字

override 关键字用于在派生类中明确地指示一个成员函数是重写了基类中的虚函数。这样做可以增加代码的可读性,并且能够帮助编译器检测错误,比如当试图重写一个基类中并不存在的虚函数时。

用法示例:

class Base {
public:
    virtual void display() const {
        // 基类实现
    }
};

class Derived : public Base {
public:
    void display() const override { // 明确这是一个重写的函数
        // 派生类实现
    }
};

如果不使用 override,编译器仍然可以正确地识别出重写的函数,但使用 override 可以让意图更加明确,并且能够检测出一些错误。

final 关键字
final 关键字用于阻止类被进一步继承,或者阻止虚函数被进一步重写。

final 用于类时,表示该类不能被继承。
final 用于虚函数时,表示该函数不能被派生类重写。

用法示例:

class Base {
public:
    virtual void display() const final {
        // 基类实现
    }
};

class Derived : public Base {
    // 下面的重写会失败,因为基类的 display 函数被标记为 final
    // void display() const override {
    //     // 派生类实现
    // }
};

class FinalClass final { // 这个类不能被继承
    // ...
};

// 下面的继承会失败,因为 FinalClass 被标记为 final
// class DerivedFromFinal : public FinalClass {
//     // ...
// }

使用 final 可以确保类的接口不会被意外地改变,这对于维护代码的稳定性和可预测性非常有帮助。

总结
override final C++11中用于控制虚函数行为的两个关键字。override 用于指示派生类中的成员函数是重写了基类的虚函数,而 final 用于阻止类被继承或虚函数被重写。这两个关键字都有助于提高代码的清晰性和安全性。

2.3 重载、覆盖(重写)、隐藏(重定义)的对比

三、抽象类

在 C++ 中,抽象类是一个不能直接实例化的类,它主要用于定义一组接口,要求其子类必须实现这些接口。

抽象类通常包含以下特点:

1、纯虚函数:抽象类中至少有一个或多个函数声明为 virtual 并且没有实现(即函数体为空,没有 = 0 后面的函数体),这些函数被称为纯虚函数(Pure Virtual Function)。例如:

class MyAbstractClass {
public:
    virtual void abstractMethod() = 0; // 纯虚函数
};

2、继承:子类可以继承抽象类,但是不能直接实例化抽象类。子类必须实现抽象类中所有纯虚函数,否则子类也将成为抽象类。例如:

class DerivedClass : public MyAbstractClass {
public:
    void abstractMethod() override; // 必须实现抽象方法
};

3、设计目的:抽象类通常用于为一组相关的类提供一个共同的行为框架,或者作为接口的定义,避免代码重复。

4、使用场景:抽象类常用于模式设计,如工厂模式、策略模式等,以及多态和模板编程中。

当你试图创建一个抽象类的对象时,编译器会报错,因为不能创建抽象类的实例。抽象类只有在将其中的纯虚函数重写之后才能实例化对象。抽象类主要用于定义接口,实际的业务逻辑通常由继承它的具体子类来实现。

四、总结

以上就是C++中多态的基本知识,总的来说并不是很难,但我们还没有将多态底层的原理——虚函数表的问题,这个与前面继承那个类似,都是很重要的知识点,这些我们放在后面统一讲解。

感谢各位大佬观看,创作不易,还请各位大佬点赞支持!!!

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

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

相关文章

数据可视化期末考试(编程)

1.KNN 1.新增数据的分类 import pandas as pd # 您的原始数据字典 data { 电影名称: [电影1, 电影2, 电影3, 电影4, 电影5], 打斗镜头: [10, 5, 108, 115, 20], 接吻镜头: [110, 89, 5, 8, 200], 电影类型: [爱情片, 爱情片, 动作片, 动作片, 爱情片] } …

240621_Git初始配置及常用命令

Git初始配置及常用命令 初始配置 在安装Git后&#xff0c;我们应该首先设置修改、查看用户名及邮箱 运行Git Bash&#xff0c;使用以下命令设置本地Git工具的用户名及邮箱&#xff08;比如你的用户名是zhangsan&#xff0c;邮箱是zhangsan1123163.com&#xff09;&#xff1…

Tomcat 下载部署到 idea

一、下载Tomcat Tomcat 是Apache 软件基金会&#xff08;Apache Software Foundation&#xff09;下的一个核心项目&#xff0c;免费开源、并支持Servlet 和JSP 规范。属于轻量级应用服务器&#xff0c;在中小型系统和并发访问用户不是很多的场合下被普遍使用&#xff0c;是开发…

昇思25天学习打卡营第2天 | 张量Tensor

张量Tensor 张量&#xff08;Tensor&#xff09;基础 张量是MindSpore中的基本数据结构的一种&#xff0c;类似于NumPy中数组和矩阵非常相似。它具有以下重要属性&#xff1a; 形状&#xff08;shape&#xff09;和数据类型&#xff08;dtype&#xff09;&#xff1a;每个张量…

AI大模型的核心

前言 没错&#xff0c;AI大模型的核心成功因素通常可以归结为三大要素&#xff1a;大数据、大算力和强算法。这三个因素相辅相成&#xff0c;共同推动了现代人工智能技术的发展。 1. 大数据 • 定义&#xff1a;指的是涵盖广泛领域的海量数据&#xff0c;包括文本、图像、音…

tensorflow学习:错误 InternalError: Dst tensor is not initialized

tensorflow学习&#xff1a;错误 InternalError: Dst tensor is not initialized_dst tensor is not initialized.-CSDN博客https://blog.csdn.net/wanglitao588/article/details/77033659

我是如何从功能测试转成自动化测试的!

大家好&#xff0c;我是测试君。&#xff08;六哥也行&#xff09; 时间过得好快&#xff0c;不知不觉已经在这个行业摸爬滚打10年多了&#xff0c;也算是测试圈的一枚老兵了。 回想自己在自动化求知路上的种种&#xff0c;依然历历在目&#xff0c;看着自己一步步的走来&#…

Git 使用指南(附详细解释)

Git 是一个强大的版本控制系统&#xff0c;广泛用于软件开发中&#xff0c;用于跟踪文件的更改、协作工作等。无论你是新手还是有经验的开发者&#xff0c;掌握 Git 都是非常有益的。这篇博客将带你了解 Git 的基本使用&#xff0c;希望能帮助你快速入门并有效使用 Git。 1. 创…

【大数据】大数据的核心特征与挑战:Volume、Velocity、Variety、Veracity

目录 Volume&#xff1a;海量数据的挑战与机遇 挑战 技术挑战 机遇 Velocity&#xff1a;数据处理的速度与实时性 挑战 技术挑战 机遇 Variety&#xff1a;数据类型的多样性与复杂性 挑战 技术挑战 机遇 Veracity&#xff1a;数据的真实性与质量控制 挑战 技术挑…

《数字图像处理》实验报告四

一、实验任务与要求 对 Fig0403.tif 进行傅里叶变换并显示其频谱图像&#xff1b;fft2(x) 对 Fig0405.tif 图像进行填充和非填充的高斯滤波&#xff0c;并观察其不同&#xff1b;paddedsize&#xff0c;fft2&#xff08;x,m,n&#xff09; 由 sobel 空间滤波算子生成相应的频率…

A股3000点下方继续跳水,股民都跌懵了。

今天的A股跌懵了&#xff0c;让人几乎无法呼吸&#xff0c;盘面上出现2个重要信号&#xff0c;不废话&#xff0c;直接说重点&#xff1a; 1、今天两市又跳水了&#xff0c;但绝大多数的个股已经拒绝下跌&#xff0c;市场已然处于一个阶段底部&#xff0c;短线反弹随时可能出现…

找不到d3dcompiler_47.dll如何修复,这几种修复方法可搞定

最近&#xff0c;我在尝试运行一款游戏时遇到了一个问题&#xff0c;系统提示我丢失了d3dcompiler_47.dll文件。这让我感到非常困扰&#xff0c;因为这个问题导致我无法正常运行游戏。经过一番搜索和尝试&#xff0c;我找到了几种修复这个问题的方法&#xff0c;并成功解决了这…

k8s使用Endpoint将信息存储到集群外部数据库

https://mp.csdn.net/mp_blog/creation/editor/139864305 上一篇文章

01数字电子技术基础

第一节课&#xff1a;introduction 导论 决定了这门课的学习方法、学习内容、一个大概的把握、虽不是具体的技术&#xff0c;不是细节&#xff0c;但是这是一节思想 每门课都重要&#xff0c;但侧重点不同。 学习前人的思想和营养&#xff0c;为自己所用。 1.课程性质&#x…

贪心算法精品课 2024.6.21-24.6.25

0、贪心算法介绍 例三中的最优解为装两个2号物品&#xff0c;总价值为14。贪心算法鼠目寸光 解释&#xff1a;若某个位置的最优解为20元,那么10元的就一定不超过一张&#xff0c;否则两张10元就可以被一张20元代替&#xff0c;同理5元的最大张数为1&#xff0c;1元的最大张数为…

MYSQL十、MYSQL的存储过程和触发器的基本认识

存储过程 存储过程&#xff1a;存储过程是事先经过编译并存储在数据库中的一段SQL语句的集合。调用存储过程可以简化应用开发人员的很多工作&#xff0c;减少数据在数据库和应用服务器之间的传输&#xff0c;对于提高数据处理的效率是有好处的。 存储过程思想上很简单&#xff…

springboot网上商城系统的设计与实现-计算机毕业设计源码08789

摘 要 随着互联网趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己推广出去&#xff0c;最好方式就是建立自己的互联网系统&#xff0c;并对其进行维护和管理。在现实运用中&#xff0c;应用软件的工作规则和开发步骤&#xff0c;采用Java技术建设网上商城系统。 本设…

Selenium IED-控制已打开的Chrome浏览器

本文已收录于专栏 《自动化测试》 目录 背景介绍优势特点操作步骤总结提升 背景介绍 在我们进行自动化测试的过程中有时候会遇见一个很棘手的问题那就是登录的过程中需要图片验证码&#xff0c;图片验证码设计的初衷其实就是为了防自动化&#xff0c;防止一些人利用自动工具恶意…

蒙特卡洛算法简介及应用

文章目录 基础简介蒙特卡洛方法示例其他应用 基础简介 蒙特卡洛算法&#xff08;Monte Carlo Algorithm&#xff09;是一种基于随机抽样或统计试验的数值计算方法&#xff0c;用于求解各种数学问题&#xff0c;尤其是那些解析解难以获得或者是高维度的积分问题。这种方法的核心…

浏览器断点调试(用图说话)

浏览器断点调试&#xff08;用图说话&#xff09; 1、开发者工具2、添加断点3、查看变量值 浏览器断点调试 有时候我们需要在浏览器中查看 html页面的js中的变量值。1、开发者工具 打开浏览器的开发者工具 按F12 &#xff0c;没反应的话按FnF12 2、添加断点 3、查看变量值