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] …...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
