C++多态的理解
C++多态的理解
- 1 C++多态的概念
- 2 C++多态的类型
- 3 虚函数实现多态案例分析
- 3.1 非虚函数成员函数调用的案例
- 3.1.1 测试代码
- 3.1.2 测试运行结果
- 3.2 虚函数成员函数调用案例
- 3.2.1 虚函数实现测试代码
- 3.2.2 测试运行结果
- 3.3 基类析构函数的实现
- 3.3.1 析构函数定义和实现案例
- 3.3.2 基类析构函数采用虚函数实现的原因
- 3.4 纯虚函数
1 C++多态的概念
C++多态是面向对象编程中的一个重要概念,它允许同一个接口或父类引用可以指向多种实际类型,并且可以根据实际类型来调用相应的方法。多态的存在可以使程序具有更好的灵活性和可维护性,同时减少代码的重复性。
多态的实现主要基于继承、重写和对象指针或引用来实现。继承允许一个类继承另一个类的属性和方法,而重写则允许子类重新定义从父类中继承的方法,以满足自身的需求。通过使用父类引用来指向子类对象,我们可以在运行时确定实际调用的方法,从而实现多态。
在C++中,多态主要通过虚函数来实现。虚函数是在基类中使用关键字virtual声明的成员函数,它可以在派生类中被重写。通过在派生类中重写虚函数,可以改变基类中的方法实现逻辑,使其适应派生类的需求。然后,通过使用基类指针或引用指向派生类对象,可以调用虚函数,并根据实际类型来调用相应的方法。这个过程称为动态绑定或延迟绑定。
除了虚函数,构造函数重载和类型多态性也可以实现多态。构造函数多态性允许我们在创建派生类对象时调用基类构造函数,并在其中添加派生类的初始化代码。类型多态性则允许我们使用基类指针或引用调用派生类的方法,这些方法可以在运行时动态地绑定到正确的对象上。
需要注意的是,多态的实现需要适当的条件。首先,父类中必须声明虚函数,以便在派生类中进行重写。其次,父类指针或引用必须指向派生类对象,以便在运行时确定实际调用的方法。此外,多态的实现还需要注意访问修饰符、纯虚函数和抽象基类等概念的使用。
C++多态是一种重要的面向对象编程技术,它可以提高程序的灵活性和可维护性,同时减少代码的重复性。通过继承、重写和虚函数等机制,我们可以实现多态,从而让程序更加灵活、可扩展和易于维护。
2 C++多态的类型
C++中的多态性有多种不同的类型,主要包括以下几种:
编译时多态性:这种多态性是通过函数重载和运算符重载来实现的。它允许我们使用相同的函数名或操作符来定义多个版本,这些版本在编译时会根据其参数类型和个数进行选择。
运行时多态性:这种多态性是通过虚函数和基类指针或引用来实现的。它允许我们使用基类指针或引用指向子类对象,并调用子类重写后的虚函数。这种多态性也称为动态绑定或延迟绑定。
构造函数多态性:这种多态性是通过在派生类中重载基类构造函数来实现的。它允许我们在创建派生类对象时,可以调用基类的构造函数,并在其中添加派生类的初始化代码。
类型多态性:这种多态性是通过在基类中使用指针或引用调用派生类对象来实现的。它允许我们使用基类指针或引用调用派生类的方法,这些方法可以在运行时动态地绑定到正确的对象上。
泛型编程多态性:这种多态性是通过模板和泛型编程来实现的。它允许我们编写可适用于不同数据类型的代码,这些代码在编译时会根据实际数据类型进行选择。
这些多态性类型提供了不同的编程方式和工具,使我们能够更灵活地设计和实现程序。
3 虚函数实现多态案例分析
该案例的代码是基于C++多态上提供的案例去做的分析
3.1 非虚函数成员函数调用的案例
3.1.1 测试代码
需要注意的地方是基类Shape的area成员函数是未用virtual关键字去修饰成员函数的: int area()
#include <iostream>using namespace std;class Shape {protected:int width, height;public:Shape( int a=0, int b=0){width = a;height = b;cout << "base constructor class" <<endl;}int area() ------>基类成员函数实现未用virtual关键字修饰{cout << "Parent class area :" <<endl;return 0;}~Shape(){cout << "Base DeConstructor Shape" <<endl;}
};
class Rectangle: public Shape{public:Rectangle( int a=0, int b=0):Shape(a, b) {cout << "Rectangle constructor class" <<endl;}int area (){cout << "Rectangle class area :" <<endl;return (width * height);}~Rectangle(){cout << " Rectangle class deconstructor "<<endl;}
};
class Triangle: public Shape{public:Triangle( int a=0, int b=0):Shape(a, b) {cout << "Triangle constructor class" <<endl;}int area (){cout << "Triangle class area :" <<endl;return (width * height / 2);}~Triangle(){cout << " Triangle class deconstructor "<<endl;}
};
// 程序的主函数
int main( )
{Shape *shape;cout <<" ============== 1 =============="<<endl;Rectangle rec(10,7);cout <<" ============== 2 =============="<<endl;Triangle tri(10,5);cout <<" ============== 3 =============="<<endl;// 存储矩形的地址shape = &rec;cout <<" ============== 4 =============="<<endl;// 调用矩形的求面积函数 areashape->area();cout <<" ============== 5 =============="<<endl;// 存储三角形的地址shape = &tri;cout <<" ============== 6 =============="<<endl;// 调用三角形的求面积函数 areashape->area();cout <<" ============== 7 =============="<<endl;return 0;
}
3.1.2 测试运行结果
重点关注下面的log:
============== 4 ==============
Parent class area :============== 5 ============================ 6 ==============
Parent class area :============== 7 ==============
测试运行结果:
$ g++ base_derive_test.cpp -o demo
$ ./demo ============== 1 ==============
base constructor class
Rectangle constructor class============== 2 ==============
base constructor class
Triangle constructor class============== 3 ============================ 4 ==============
Parent class area :============== 5 ============================ 6 ==============
Parent class area :============== 7 ==============Triangle class deconstructor
Base DeConstructor ShapeRectangle class deconstructor
Base DeConstructor Shape
$
3.2 虚函数成员函数调用案例
3.2.1 虚函数实现测试代码
基类Shape的area成员函数是未用virtual关键字去修饰成员函数的: virtual int area()
#include <iostream>using namespace std;class Shape {protected:int width, height;public:Shape( int a=0, int b=0){width = a;height = b;cout << "base constructor class" <<endl;}virtual int area(){cout << "Parent class area :" <<endl;return 0;}~Shape(){cout << "Base DeConstructor Shape" <<endl;}
};
class Rectangle: public Shape{public:Rectangle( int a=0, int b=0):Shape(a, b) {cout << "Rectangle constructor class" <<endl;}int area (){cout << "Rectangle class area :" <<endl;return (width * height);}~Rectangle(){cout << " Rectangle class deconstructor "<<endl;}
};
class Triangle: public Shape{public:Triangle( int a=0, int b=0):Shape(a, b) {cout << "Triangle constructor class" <<endl;}int area (){cout << "Triangle class area :" <<endl;return (width * height / 2);}~Triangle(){cout << " Triangle class deconstructor "<<endl;}
};
// 程序的主函数
int main( )
{Shape *shape;cout <<" ============== 1 =============="<<endl;Rectangle rec(10,7);cout <<" ============== 2 =============="<<endl;Triangle tri(10,5);cout <<" ============== 3 =============="<<endl;// 存储矩形的地址shape = &rec;cout <<" ============== 4 =============="<<endl;// 调用矩形的求面积函数 areashape->area();cout <<" ============== 5 =============="<<endl;// 存储三角形的地址shape = &tri;cout <<" ============== 6 =============="<<endl;// 调用三角形的求面积函数 areashape->area();cout <<" ============== 7 =============="<<endl;return 0;
}
3.2.2 测试运行结果
调用成员函数erea时的结果如下所示:
============== 4 ==============
Rectangle class area :============== 5 ============================ 6 ==============
Triangle class area :============== 7 ==============
整个测试程序的运行结果:
$ g++ base_derive_test.cpp -o demo
$ ./demo ============== 1 ==============
base constructor class
Rectangle constructor class============== 2 ==============
base constructor class
Triangle constructor class============== 3 ============================ 4 ==============
Rectangle class area :============== 5 ============================ 6 ==============
Triangle class area :============== 7 ==============Triangle class deconstructor
Base DeConstructor ShapeRectangle class deconstructor
Base DeConstructor Shape
$
3.3 基类析构函数的实现
基类的析构函数在实现定义的时候建议采用虚函数定义的方式去声明和实现:
3.3.1 析构函数定义和实现案例
class Shape {protected:int width, height;public:Shape( int a=0, int b=0){width = a;height = b;cout << "base constructor class" <<endl;}virtual int area(){cout << "Parent class area :" <<endl;return 0;}virtual ~Shape(){cout << "Base DeConstructor Shape" <<endl;}
};
3.3.2 基类析构函数采用虚函数实现的原因
基类析构函数采用虚函数实现主要是为了确保正确的资源清理和避免潜在的内存泄漏问题。
当使用基类指针或引用指向派生类对象时,如果析构函数不是虚函数,那么只有基类的析构函数会被调用,而派生类的析构函数不会被调用。这样会导致派生类对象中的资源无法被正确地清理,从而产生内存泄漏或其他资源泄漏问题。
如果将基类的析构函数声明为虚函数,那么在通过基类指针或引用删除一个派生类对象时,派生类的析构函数也会被正确调用。这样可以确保派生类对象中的资源被正确清理,避免内存泄漏和其他资源泄漏问题。
另外,析构函数采用虚函数实现还可以确保在派生类析构函数执行之前,基类的析构函数已经执行完毕。这样可以保证正确的析构顺序,避免因析构顺序不当而导致的问题。
因此,为了避免潜在的资源清理问题和保证正确的析构顺序,通常将基类的析构函数声明为虚函数。
3.4 纯虚函数
在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。
纯虚函数的定义格式为:virtual int area() = 0;
= 0 告诉编译器,函数没有主体,上面的虚函数是纯虚函数。
基类中定义了纯虚函数,则该基类被成为虚基类,对应的纯虚函数必须要在派生类中去实现。
相关文章:
C++多态的理解
C多态的理解 1 C多态的概念2 C多态的类型3 虚函数实现多态案例分析3.1 非虚函数成员函数调用的案例3.1.1 测试代码3.1.2 测试运行结果 3.2 虚函数成员函数调用案例3.2.1 虚函数实现测试代码3.2.2 测试运行结果 3.3 基类析构函数的实现3.3.1 析构函数定义和实现案例3.3.2 基类析…...
关于深拷贝和浅拷贝你需要了解的内容
深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是在复制对象或数据结构时使用的两种不同的策略,它们的主要区别在于复制后新旧对象之间的关系以及对嵌套对象的处理方式。 浅拷贝: 浅拷贝创建一个新对象ÿ…...
Visual Studio自定义模板参数、备注
模板路径: VS2022 x64:C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\ItemTemplatesVS2022 x86:C:\Program Files (x86)\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\ItemTemplates 一、声明和启用模板…...
机器学习-数值特征
离散值处理 import pandas as pd import numpy as npvg_df pd.read_csv(datasets/vgsales.csv, encoding "ISO-8859-1") vg_df[[Name, Platform, Year, Genre, Publisher]].iloc[1:7]NamePlatformYearGenrePublisher1Super Mario Bros.NES1985.0PlatformNintendo2…...
Rocky(centos)安装nginx并设置开机自启
一、安装nginx 1、安装依赖 yum install -y gcc-c pcre pcre-devel zlib zlib-devel openssl openssl-devel 2、去官网下载最新的稳定版nginx nginx: downloadhttp://nginx.org/en/download.html 3、将下载后的nginx上传至/usr/local下 或者执行 #2023-10-8更新 cd /usr/…...
Android约束布局ConstraintLayout的Guideline,CardView
Android约束布局ConstraintLayout的Guideline,CardView <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:a…...
LVGL8.3.6 Flex(弹性布局)
使用lv_obj_set_flex_flow(obj, flex_flow)函数 横向拖动 LV_FLEX_FLOW_ROW 将子元素排成一排而不包裹 LV_FLEX_FLOW_ROW_WRAP 将孩子排成一排并包裹起来 LV_FLEX_FLOW_ROW_REVERSE 将子元素排成一行而不换行,但顺序相反 LV_FLEX_FLOW_ROW_WRAP_REVERSE 将子元素…...
计算机算法分析与设计(8)---图像压缩动态规划算法(含C++)代码
文章目录 一、知识概述1.1 问题描述1.2 算法思想1.3 算法设计1.4 例题分析 二、代码 一、知识概述 1.1 问题描述 1. 一幅图像的由很多个像素点构成,像素点越多分辨率越高,像素的灰度值范围为0~255,也就是需要8bit来存储一个像素的灰度值信息…...
React 状态管理 - Mobx 入门(上)
Mobx是另一款优秀的状态管理方案 【让我们未来多一种状态管理选型】 响应式状态管理工具 扩展学习资料 名称 链接 备注 mobx 文档 1. MobX 介绍 MobX 中文文档 mobx https://medium.com/Zwenza/how-to-persist-your-mobx-state-4b48b3834a41 英文 Mobx核心概念 M…...
OLED透明屏技术在智能手机、汽车和广告领域的市场前景
OLED透明屏技术作为一种新型的显示技术,具有高透明度、触摸和手势交互、高画质和图像显示效果等优势,引起了广泛的关注。 随着智能手机、汽车和广告等行业的快速发展,OLED透明屏技术也在这些领域得到了广泛的应用。 本文将介绍OLED透明屏技…...
考研是为了逃避找工作的压力吗?
如果逃避眼前的现实, 越是逃就越是会陷入痛苦的境地,要有面对问题的勇气,渡过这个困境的话,应该就能一点点地解决问题。 众所周知,考研初试在大四上学期的十二月份,通常最晚的开始准备时间是大三暑假&…...
广州华锐互动:VR动物解剖实验室带来哪些便利?
随着科技的不断发展,我们的教育方式也在逐步变化和进步。其中,虚拟现实(VR)技术的应用为我们提供了一种全新的学习方式。尤其是在动物解剖实验中,VR技术不仅能够增强学习的趣味性,还能够提高学习效率和准确性。 由广州华锐互动开发…...
Uniapp 婚庆服务全套模板前端
包含 首页、社区、关于、我的、预约、订购、选购、话题、主题、收货地址、购物车、系统通知、会员卡、优惠券、积分、储值金、订单信息、积分、充值、礼品、首饰等 请观看 图片参观 开源,下载即可 链接:婚庆服务全套模板前端 - DCloud 插件市场 问题反…...
RabbitMQ-网页使用消息队列
1.使用消息队列 几种模式 从最简单的开始 添加完新的虚拟机可以看到,当前admin用户的主机访问权限中新增的刚添加的环境 1.1查看交换机 交换机列表中自动新增了刚创建好的虚拟主机相关的预设交换机。一共7个。前面两个 direct类型的交换机,一个是…...
弹性资源组件elastic-resource设计(四)-任务管理器和资源消费者规范
简介 弹性资源组件提供动态资源能力,是分布式系统关键基础设施,分布式datax,分布式索引,事件引擎都需要集群和资源的弹性资源能力,提高伸缩性和作业处理能力。 本文介绍弹性资源组件的设计,包括架构设计和详…...
【Java】微服务——RabbitMQ消息队列(SpringAMQP实现五种消息模型)
目录 1.初识MQ1.1.同步和异步通讯1.1.1.同步通讯1.1.2.异步通讯 1.2.技术对比: 2.快速入门2.1.RabbitMQ消息模型2.4.1.publisher实现2.4.2.consumer实现 2.5.总结 3.SpringAMQP3.1.Basic Queue 简单队列模型3.1.1.消息发送3.1.2.消息接收3.1.3.测试 3.2.WorkQueue3.…...
react高阶成分(HOC)实践例子
以下是一个使用React函数式组件的高阶组件示例,它用于添加身份验证功能: import React, { useState, useEffect } from react;// 定义一个高阶组件,它接受一个组件作为输入,并返回一个新的包装组件 const withAuthentication (W…...
20231005使用ffmpeg旋转MP4视频
20231005使用ffmpeg旋转MP4视频 2023/10/5 12:21 百度搜搜:ffmpeg 旋转90度 https://zhuanlan.zhihu.com/p/637790915 【FFmpeg实战】FFMPEG常用命令行 https://blog.csdn.net/weixin_37515325/article/details/127817057 FFMPEG常用命令行 5.视频旋转 顺时针旋转…...
MySQL-锁
MySQL的锁机制 1.共享锁(Shared Lock)和排他锁(Exclusive Lock) 事务不能同时具有行共享锁和排他锁,如果事务想要获取排他锁,前提是行没有共享锁和排他锁。而共享锁,只要行没有排他锁都能获取到。 手动开启共享锁/排他锁: -- 对…...
ES6中变量解构赋值
数组的解构赋值 ES6规定以一定模式从数组、对象中提取值,然后给变量赋值叫做解构。 本质上就是一种匹配模式,等号两边模式相同,左边的变量就能对应的值。 假如解构不成功会赋值为undefined。 不需要匹配的位置可以置空 let [ a, b, c] …...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
rknn toolkit2搭建和推理
安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 ,不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源(最常用) conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...
leetcode_69.x的平方根
题目如下 : 看到题 ,我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历,我们是整数的平方根,所以我们分两…...
python读取SQLite表个并生成pdf文件
代码用于创建含50列的SQLite数据库并插入500行随机浮点数据,随后读取数据,通过ReportLab生成横向PDF表格,包含格式化(两位小数)及表头、网格线等美观样式。 # 导入所需库 import sqlite3 # 用于操作…...
Vue.js教学第二十一章:vue实战项目二,个人博客搭建
基于 Vue 的个人博客网站搭建 摘要: 随着前端技术的不断发展,Vue 作为一种轻量级、高效的前端框架,为个人博客网站的搭建提供了极大的便利。本文详细介绍了基于 Vue 搭建个人博客网站的全过程,包括项目背景、技术选型、项目架构设计、功能模块实现、性能优化与测试等方面。…...
