浅谈C++|多态篇


1.多态的基本概念

多态是C++面向对象三大特性之一多态分为两类
1. 静态多态:函数重载和运算符重载属于静态多态,复用函数名·

2.动态多态:派生类和虚函数实现运行时多态
静态多态和动态多态区别:
·静态多态的函数地址早绑定–编译阶段确定函数地址

·动态多态的函数地址晚绑定–运行阶段确定函数地址下面通过案例进行讲解多态

动态多态满足条件

1、有继承关系
2、子类重写父类的虚函数

动态多态使用
父类的指针或者引用执行子类对象

重写:函数返回值类型函数名参数列表完全—致称为重写

 静态多态代码:

#include <iostream>
using namespace std;
class dongwu {
public:void speak() {cout << "动物叫" << endl;}
};class cat :public dongwu {void speak() {cout << "猫叫" << endl;}
};
//早绑定,编译阶段就确定函数的地址void speak(dongwu& p) {p.speak();
}
void fun() {cat p;speak(p);
}
int main() {fun();cat p;//发生隐式转换,只能把儿子转为父亲,退化dongwu m = p;m.speak();return 0;
}

注意转化,以及早绑定的特性是执行当前的类型的函数,不执行儿子的函数

动态动态代码: 

#include <iostream>
using namespace std;
class dongwu {
public://virtual虚函数关键字virtual void speak() {cout << "动物叫" << endl;}
};class cat :public dongwu {//子类的virtual可加可不加virtual void speak() {cout << "猫叫" << endl;}
};//晚绑定,执行子类的函数
void speak(dongwu& p) {p.speak();
}
void fun() {cat p;speak(p);
}
int main() {fun();return 0;
}

二.多态的底层原理

我们先看以下代码:

#include <iostream>
using namespace std;
class father1 {
public:void speak() {cout << "动物叫" << endl;}
};
class father2 {
public:virtual void speak() {cout << "动物叫" << endl;}
};
int main() {cout << sizeof(father1) << endl;cout << sizeof(father2) << endl;return 0;
}

 当类的成员函数加入虚函数关键字后,会发现类的大小发生了改变。此时类的内部结构。此时类的内部会多一个指针虚函数(表)指针,虚函数指针指向虚函数表,虚函数表中存储虚函数的入口地址

那么当派生类继承基类后,如果成员函数没有重名,那么会完全继承父类的结构。 

 

 但是当派生类,重写基函数的虚函数时,派生类中的虚函数表会发生改变,此时虚函数表指向派生类的虚函数,基类的虚函数被覆盖。

 

 此时,我们有派生类隐式转换为基类时,虚函数表中的内容并不改变,此时调用虚函数,执行的是派生类的虚函数。

三.多态的优点 

1、组织结构清晰

2、可读性强

3、对于前期和后期扩展以及维护性高

 普通计算机类:

#include <iostream>
using namespace std;
class jisuanqi {
public:int a, b;int jisuan(string fu) {if (fu == "+") {return a + b;}else if (fu == "-") {return a - b;}else if (fu == "*") {return a * b;}}
};
void fun() {jisuanqi q;q.a = 200;q.b = 100;cout << q.a << " - " << q.b << " = " << q.jisuan("-") << endl;cout << q.a << " + " << q.b << " = " << q.jisuan("+") << endl;cout << q.a << " * " << q.b << " = " << q.jisuan("*") << endl;}
int main() {fun();return 0;
}

 多态计算机类:

#include <iostream>
using namespace std;
class jisuanqi {
public:int a;int b;virtual int jisuan() {return 0;}
};class add :public jisuanqi {virtual int jisuan() {return a+b;}
};class jian :public jisuanqi {virtual int jisuan() {return a - b;}
};class cheng:public jisuanqi {virtual int jisuan() {return a * b;}
};void fun() {jisuanqi* p = new add;p->a = 200;p->b = 100;cout << p->a << " + " << p->b << " = " << p->jisuan()<<endl;delete p;p = new jian;p->a = 200;p->b = 100;cout << p->a << " - " << p->b << " = " << p->jisuan()<<endl;delete p;p = new cheng;p->a = 200;p->b = 100;cout << p->a << " * " << p->b << " = " << p->jisuan()<<endl;delete p;
}
int main() {fun();return 0;
}

四.纯虚函数和抽象类

在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容因此可以将虚函数改为纯虚函数。当类中有了纯虚函数,这个类也称为抽象类

纯虚函数语法: virtual    返回值类型   函数名︰(参数列表)= 0 ;

抽象类特点:
    ·无法实例化对象
    ·子类必须重写抽象类中的纯虚函数,否则也属于抽象类
 

 代码:

#include <iostream>
using namespace std;class father {
public://纯虚函数virtual void fun() = 0;
};class son :public father{
public:void fun() {cout << "我是sond" << endl;}
};void fun() {//多态f必须是指针或者引用//father f; 报错不可实例化father* f = new son;f->fun();
}
int main() {fun();return 0;
}

案例制作饮品:

#include <iostream>
using namespace std;
class father {
public:virtual void zhushui() = 0;virtual void chongpao() = 0;virtual void daoru() = 0;virtual void jialiao() = 0;void fun() {zhushui();chongpao();daoru();jialiao();}
};class tea :public father{void zhushui() {cout << "煮山泉水" << endl;};void chongpao() {cout << "冲茶" << endl;};void daoru() {cout << "倒入茶杯中" << endl;};void jialiao() {cout << "加入枸杞" << endl;};
};class kafei : public father{void zhushui() {cout << "煮水" << endl;};void chongpao() {cout << "冲咖啡" << endl;};void daoru() {cout << "倒入咖啡杯中" << endl;};void jialiao() {cout << "加入奶和糖" << endl;};
};
//函数接口
void fun(father* p) {p->fun();delete p;
}
int main() {fun(new tea);cout << "----------" << endl;fun(new kafei);return 0;
}

五. 虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:

将父类中的析构函数改为虚析构或者纯虚析构


虚析构和纯虚析构共性:
·可以解决父类指针释放子类对象·都需要有具体的函数实现


虚析构和纯虚析构区别:
·如果是纯虚析构,该类属于抽象类,无法实例化对象

代码: 

#include <iostream>
using namespace std;
class father {
public://纯虚函数virtual void fun() =0;father() {cout << "father构造函数" << endl;}~father() {cout << "father析构函数" << endl;}
};class son :public father {
public://堆区开辟数据son(int age) {cout << "son构造函数" << endl;this->age = new int(age);}~son() {cout << "son析构函数" << endl;if (this->age != NULL) {delete age;age = NULL;}}void fun() {cout << *age<< "son的fun函数调用" << endl;}int* age;
};
void fun() {father* p = new son(21);delete p;
}
int main() {fun();return 0;
}

如图,当发生多态时,基类并不会调用子类的析构函数,当子类中含有堆区开辟的空间时。会造成内存泄漏。此时需要虚析构或纯虚析构来解决。

虚析构代码: 

#include <iostream>
using namespace std;
class father {
public://纯虚函数virtual void fun() =0;father() {cout << "father构造函数" << endl;}virtual ~father() {cout << "father析构函数" << endl;}
};class son :public father {
public://堆区开辟数据son(int age) {cout << "son构造函数" << endl;this->age = new int(age);}~son() {cout << "son析构函数" << endl;if (this->age != NULL) {delete age;age = NULL;}}void fun() {cout << *age<< "son的fun函数调用" << endl;}int* age;
};
void fun() {father* p = new son(21);delete p;
}
int main() {fun();return 0;
}

纯虚析构

#include <iostream>
using namespace std;
class father {
public://纯虚函数virtual void fun() =0;father() {cout << "father构造函数" << endl;}virtual ~father() = 0;
};
//纯虚函数必须
father::~father()
{cout << "father析构函数" << endl;
}class son :public father {
public://堆区开辟数据son(int age) {cout << "son构造函数" << endl;this->age = new int(age);}~son() {cout << "son析构函数" << endl;if (this->age != NULL) {delete age;age = NULL;}}void fun() {cout << *age<< "son的fun函数调用" << endl;}int* age;
};
void fun() {father* p = new son(21);delete p;
}
int main() {fun();return 0;
}

案例计算机

#include <iostream>
using namespace std;class CPU {
public://纯虚函数virtual void func() = 0;
};class  Memory_Module {
public://纯虚函数virtual void func() = 0;
};class  Graphics_card {
public://纯虚函数virtual void func() = 0;
};class CPU_intel : public CPU {
public:void func() {cout << "intel的CPU工作" << endl;}
};class Graphics_card_intel : public Graphics_card {
public:void func() {cout << "intel的显卡工作" << endl;}
};class Memory_Module_intel : public Memory_Module {
public:void func() {cout << "intel的内存条工作" << endl;}
};class CPU_lenovo: public CPU {
public:void func() {cout << "联想的CPU工作" << endl;}
};class Graphics_card_lenovo : public Graphics_card {
public:void func() {cout << "联想的显卡工作" << endl;}
};
class Memory_Module_lenovo : public Memory_Module {
public:void func() {cout << "联想的内存条工作" << endl;}
};
class computer {
public://当传入的是子类时发生多态computer() {};computer(CPU* CPU , Memory_Module* m, Graphics_card* g) {this->cpu = CPU;this->m = m;this->g = g;}void work() {cpu->func();m->func();g->func();}
private:CPU* cpu;Memory_Module* m;Graphics_card* g;
};void fun() {CPU_lenovo* c1 = new CPU_lenovo;CPU_intel* c2 = new CPU_intel;Graphics_card_intel* g1 = new Graphics_card_intel;Graphics_card_lenovo* g2 = new Graphics_card_lenovo;Memory_Module_intel* m1 = new Memory_Module_intel;Memory_Module_lenovo* m2 = new Memory_Module_lenovo;cout << "第一台电脑" << endl;computer* com = new computer(c1,m1,g1);com->work();cout << "********************************" << endl;cout << "第二台电脑" << endl;computer* com1 = new computer(c2, m2, g2);com1->work();
}
int main() {fun();return 0;
}

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

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

相关文章

Linux学习之平均负载的概念和查看方法

先理解一下平均负载的含义&#xff1a; 平均负载是指单位时间内&#xff0c;系统处于可运行状态和不可中断状态的进程数&#xff0c;也可以看成平均活跃进程数。 可运行状态的进程&#xff1a; 正在使用CPU或者正在等待CPU处理的进程&#xff0c;ps 命令看到的&#xff0c;处于…

黑马JVM总结(十)

&#xff08;1&#xff09;直接内存_基本使用 下面我们看一下使用了ByteBuffer直接内存&#xff0c;大文件的读写效率是非常的高 Java本身并不具备磁盘读写的能力&#xff0c;它需要调用操作系统的函数&#xff0c;需要从java的方法内部调用本地方法操作系统的方法&#xff0c…

bboss 流批一体化框架 与 数据采集 ETL

数据采集 ETL 与 流批一体化框架 特性&#xff1a; 高效、稳定、快速、安全 bboss 是一个基于开源协议 Apache License 发布的开源项目&#xff0c;主要由以下三部分构成&#xff1a; Elasticsearch Highlevel Java Restclient &#xff0c; 一个高性能高兼容性的Elasticsea…

【C刷题】day2

一、选择题 1、以下程序段的输出结果是&#xff08; &#xff09; #include<stdio.h> int main() { char s[] "\\123456\123456\t"; printf("%d\n", strlen(s)); return 0; } A: 12 B: 13 C: 16 D: 以上都不对【答案】&#xff1a; A 【解析】…

Python Opencv实践 - 视频文件写入(格式和分辨率修改)

参考资料&#xff1a; python opencv写视频——cv2.VideoWriter()_cv2.cv.videowriter(_翟羽嚄的博客-CSDN博客 import cv2 as cv import numpy as np#1. 打开原始视频 video_in cv.VideoCapture("../SampleVideos/Unity2D.mp4") video_width int(video_in.get(c…

带你了解前后端分离的秘密-Vue【vue入门】

&#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Vue》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一定基础的程序员&#xff0c;这个专栏…

无涯教程-JavaScript - ATAN2函数

描述 The ATAN2 function returns the arctangent, or inverse tangent, of the specified x- and ycoordinates, in radians, between -π/2 and π/2. 语法 ATAN2 (x_num, y_num)争论 Argument描述Required/OptionalX_numThe x-coordinate of the point.RequiredY_numThe…

用c++实现五子棋小游戏

五子棋是一款经典小游戏&#xff0c;今天我们就用c实现简单的五子棋小游戏 目录 用到的算法&#xff1a; 思路分析 定义变量 开始写代码 完整代码 结果图&#xff1a; 用到的算法&#xff1a; 合法移动的判断&#xff1a;isValidMove 函数通过检查指定位置是否在棋盘范…

游戏视频录制软件对比,哪款最适合你的需求?

随着电子竞技和游戏直播行业的迅速崛起&#xff0c;越来越多的玩家渴望记录并分享自己在游戏中的精彩瞬间。游戏视频录制软件正是满足这一需求的关键工具。本文将针对三款优秀的游戏视频录制软件进行对比分析&#xff0c;以便为读者提供选购建议。 游戏视频录制软件1&#xff1…

十天学完基础数据结构-第一天(绪论)

1. 数据结构的研究内容 数据结构的研究主要包括以下核心内容和目标&#xff1a; 存储和组织数据&#xff1a;数据结构研究如何高效地存储和组织数据&#xff0c;以便于访问和操作。这包括了在内存或磁盘上的数据存储方式&#xff0c;如何将数据元素组织成有序或无序的集合&…

移动端H5封装一个 ScrollList 横向滚动列表组件,实现向左滑动

效果&#xff1a; 1.封装组件&#xff1a; <template><div class"scroll-list"><divclass"scroll-list-content":style"{ background, color, fontSize: size }"ref"scrollListContent"><div class"scroll…

C# OpenVino Yolov8 Pose 姿态识别

效果 项目 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using OpenCvSharp;namespace OpenVino_Yolov8_Demo {public…

独立开发了一款Material3风格的RSS阅读器 - Agr Reader

截图 背景&#x1f4d6; 在之前接触到RSS后&#xff0c;发现RSS真是一个十分不错的信息聚合的方式&#xff0c;虽然现在看来RSS的时代已经开始落幕&#xff0c;但是至少目前还是处于能用的阶段。 在我用了Android上好几个RSS阅读App后&#xff0c;发现很多在全文解析方面不是…

Mapbox gl HTML经纬度点渲染,动态轨迹播放,自定义图形以及轨迹上显示箭头方向

Mapbox gl HTML经纬度点渲染&#xff0c;动态轨迹播放&#xff0c;自定义图形以及轨迹上显示箭头方向 1. 效果图2. 源码2.1 line.html2.2line_arrow.html 参考 今天要排查个问题&#xff0c;需要显示多个经纬度点连接成线段的方向&#xff0c;于是尝试下展示。 1. mapbox渲染经…

element plus封装el-select添加后缀图标并添加远程搜索和对话框功能

当提交的表单Form需要填某个实体的外键ID时&#xff0c;当然不可能使用el-input组件&#xff0c;这个适合提交字符串&#xff0c;然后用户又不可能记住某个引用的外键ID&#xff0c;这时候使用el-select还是必要的。 el-select组件一般都作为下拉选择框使用&#xff0c;但仅在…

远程计算机控制软件

远程控制软件允许您连接和控制位于不同位置的设备&#xff0c;对于 IT 帮助台技术人员来说&#xff0c;这是一个很好的工具&#xff0c;可以通过与用户协作、与他们聊天以及安全地访问他们的文件来轻松排除故障和修复远程设备。使用远程控制软件&#xff0c;距离不再是提供技术…

无涯教程-JavaScript - ATAN函数

描述 The ATAN function returns the arctangent, or inverse tangent, of a number. The returned angle is given in radians between -π/2 and π/2. The arctangent is the angle whose tangent is number. 语法 ATAN (number)争论 Argument描述Required/OptionalNumb…

Vulnhub实战-prime1

前言 VulnHub 是一个面向信息安全爱好者和专业人士的虚拟机&#xff08;VM&#xff09;漏洞测试平台。它提供了一系列特制的漏洞测试虚拟机镜像&#xff0c;供用户通过攻击和漏洞利用的练习来提升自己的安全技能。本次&#xff0c;我们本次测试的是prime1。 一、主机发现和端…

Jmeter性能实战之分布式压测

分布式执行原理 1、JMeter分布式测试时&#xff0c;选择其中一台作为调度机(master)&#xff0c;其它机器作为执行机(slave)。 2、执行时&#xff0c;master会把脚本发送到每台slave上&#xff0c;slave 拿到脚本后就开始执行&#xff0c;slave执行时不需要启动GUI&#xff0…

【机器学习】文本多分类

声明&#xff1a;这只是浅显的一个小试验&#xff0c;且借助了AI。使用的是jupyter notebook,所以代码是一块一块&#xff0c;从上往下执行的 知识点&#xff1a;正则删除除数字和字母外的所有字符、高频词云、混淆矩阵 参考&#xff1a;使用python和sklearn的中文文本多分类…