C++ 多态 纯干货讲解 复制可调试(1)

news2024/11/29 2:37:32

💯 博客内容:多态

😀 作  者:陈大大陈

🚀 个人简介:一个正在努力学技术的准C++后端工程师,专注基础和实战分享 ,欢迎私信!

💖 欢迎大家:这里是CSDN,我总结知识和写笔记的地方,喜欢的话请三连,有问题请私信 😘 😘 😘

目录

虚函数继承的条件

普通调用

多态调用

例:

易错练习题

override 和 final

纯虚函数 

多态的本质

为什么对象不能实现多态?


 

虚函数继承的条件

1.虚函数重写

2.必须是父类的引用或者指针来调用 

普通调用

调用函数的类型是谁,就调用哪个类型的函数

多态调用

调用指针或者引用指向的对象,指向父类就调用父类,指向子类就调用子类。 

例:
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class Person
{
public:
	virtual void BuyTickets()
	{
		cout << "全价买票" << endl;
	}
};
class Student :public Person
{
	virtual void BuyTickets()
	{
		cout << "半价买票" << endl;
	}
};
void Func(Person& p)
{
	p.BuyTickets();
}
int main()
{
	Student st;
	Func(st);
	Person p;
	Func(p);

}

 上面的代码要是把引用去了,就是两个全价买票,因为普通调用只看参数类型。

#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class Person
{
public:
	virtual void BuyTickets()
	{
		cout << "全价买票" << endl;
	}
	~Person()
	{
		cout << "~Person" << endl;
	}
};
class Student :public Person
{
public:
	virtual void BuyTickets()
	{
		cout << "半价买票" << endl;
	}
	~Student()
	{
		cout << "~Student" << endl;
	}
};
void Func(Person& p)
{
	p.BuyTickets();
}
int main()
{
	Student st;
	Func(st);
	Person p;
	Func(p);

}

一般情况下,析构没有问题。

#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class Person
{
public:
	virtual void BuyTickets()
	{
		cout << "全价买票" << endl;
	}
	~Person()
	{
		cout << "~Person" << endl;
	}
};
class Student :public Person
{
public:
	virtual void BuyTickets()
	{
		cout << "半价买票" << endl;
	}
	~Student()
	{
		cout << "~Student" << endl;
	}
};
void Func(Person& p)
{
	p.BuyTickets();
}
int main()
{
	/*Student st;
	Func(st);
	Person p;
	Func(p);*/
	Person* ptr = new Person;
	delete ptr;
	ptr = new Student;
	delete ptr;

}

 在new的情况下就会有问题,因为析构没有满足多态的条件,所以析构是普通调用。

加上virtual即可。

接下来来看一道易错练习题。

易错练习题

 答案是B,容易选成D,需要注意的是,虚函数继承声明,重写实现。

派生类可以不加virtual,所以B的func接口继承了A。

所以val的值是1。

之后B对象切片后传参调用A的test,A的test调用B的func。

如果调用的语句是,p->func(),就不构成多态,答案会是B->0。

override 和 final

从上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来debug会得不偿失,因此:C++11提供了override和final两个关键字,可以帮助用户检测是否重写。

1. final:修饰虚函数,表示该虚函数不能再被重写

2. override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

class Car

{ public:

virtual void Drive() final {}

};

class Benz :public Car

{ public:

virtual void Drive() {cout << "Benz-舒适" << endl;}

};


 

class Car{

public:

virtual void Drive(){}

};

class Benz :public Car {

public:

virtual void Drive() override {cout << "Benz-舒适" << endl;}

}; 

纯虚函数 

 在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。


class Car
{ public:

virtual void Drive() = 0;
};

class Benz :public Car

{ public:

virtual void Drive()

{

cout << "Benz-舒适" << endl;

} };

class BMW :public Car

{ public:

virtual void Drive()

{

cout << "BMW-操控" << endl;

} };

void Test()

{

Car* pBenz = new Benz;

pBenz->Drive();

Car* pBMW = new BMW;

pBMW->Drive(); }
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class base
{
public:
	virtual void func1()
	{
		cout << "func1()" << endl;
	}

	virtual void func2()
	{
		cout << "func2()" << endl;
	}

	void func3()
	{
		cout << "func3()" << endl;
	}
private:
	int _b = 1;
	char _ch;
};

int main()
{
	cout << sizeof(base) << endl;
	base b;

	return 0;
}

虚函数里会存一个虚函数表指针来保存地址。

vfptr(virtual function pointer);

它是一个函数指针数组。 

父类和派生类的虚表指针不尽相同,重写了哪个虚函数,哪个虚表指针就会被覆盖。 

也就是说:

至此,我们明白了多态的本质

多态的本质

多态为什么能指向父类调父类,指向子类调子类?

虚函数的重写也叫虚函数的覆盖。

编译器首先在对象里找到虚函数表的地址。

查找虚函数表中存储的地址,是父类就调用父类,是子类就切割成父类对象。

是因为虚表指针会去寻找所调用的类的地址。 

为什么对象不能实现多态?

 因为指针和引用可以指向子类对象中切割出来的父类的一部分。

而对象的拷贝不会拷贝虚表指针。

如果拷贝虚表指针的话,父类和子类的虚函数以及析构函数极易混淆,造成报错。

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

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

相关文章

0007Java安卓程序设计-ssm基于Android的校园新闻管理系统

文章目录 **摘** **要**目 录开发环境 编程技术交流、源码分享、模板分享、网课教程 &#x1f427;裙&#xff1a;776871563 摘 要 网络的广泛应用给生活带来了十分的便利。所以把校园新闻管理与现在网络相结合&#xff0c;利用java技术建设校园新闻管理系统app&#xff0c;实…

【漏洞复现】Drupal_小于7.32版本 _“Drupalgeddon” SQL注入漏洞(CVE-2014-3704)

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证 说明内容漏洞编号CVE-2014-3704漏洞名称Drupal “Drupalgeddon” SQL注入漏洞漏洞…

搬家两年随笔

不知不觉中&#xff0c;我已经搬到这个地方两年多了。 回首这段时间&#xff0c;我感触颇深。 尽管这里地理位置较为偏僻&#xff0c;交通不是特别方便&#xff0c;但环境优美&#xff0c;绿树成荫&#xff0c;空气清新。 只是相对于之前的生活环境&#xff0c;这里离上班的地方…

【Linux】基本命令

和光同尘_我的个人主页 一直游到海水变蓝。 --余华 Linux基本命令 &#x1f56f;️前言基础指令lspwdcdtouchmkdirrmdirrmman**cp**mvcat**less指令**输出重定向 >追加重定向 >>输入重定向 <headtail&#xff08;查看日志常用&#xff09;管道时间相关指令dateca…

五:Day11_SpringMVC03

一、拦截器 SpringMVC给出了拦截器来实现单元方法的拦截&#xff0c;拦截器的执行是在DispatcherServlet之后和单元方法之前的。 注意&#xff1a;只有URL匹配到了控制单元&#xff0c;拦截器才能生效。 2. 使用拦截器 2.1 创建拦截器类 public class MyInterceptor implem…

S5PV210(十):LCD

本文主要探讨210的LCD相关知识。 LCD LCD称液晶(透光背光呈色),可在电信号驱动下使液晶分子旋转,呈现不同的颜色(被动发光) lcd接口为TTL接口(5V为1&#xff0c;0V为0),不能传输太远,远距离传输方式:SoC(TTL) ->VGA-> LCD(TTL) 其他显设备:CRT(…

Java基础篇 | 多线程详解

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色专栏&#xf…

【数据结构】深入浅出讲解计数排序【图文详解,搞懂计数排序这一篇就够了】

计数排序 前言一、计数排序算法核心思路映射 概念补充绝对映射相对映射 二、计数排序算法核心实现步骤三、码源详解四、效率分析&#xff08;1&#xff09;时间复杂度 — O&#xff08;Max&#xff08;N&#xff0c;range&#xff09;&#xff09;&#xff08;2&#xff09;空间…

74HC138逻辑芯片

文章目录 74系列逻辑芯片——74HC138基础信息描述特征应用范围 功能信息封装引脚基本电路 扩展性能分析 74系列逻辑芯片——74HC138 基础信息 描述 74HC138器件设计用于需要极短传播延迟时间的高性能存储器解码或数据路由应用&#xff1b;在高性能存储系统中&#xff0c;可使用…

【C语言】扫雷游戏的一步一步的实现

文章目录 一、扫雷游戏分析和设计1.1 扫雷游戏的功能说明1.2 游戏的分析和设计1.2.1 数据结构的分析1.2.2 ⽂件结构设计 二、扫雷游戏代码实现总结 一、扫雷游戏分析和设计 1.1 扫雷游戏的功能说明 • 使⽤控制台实现经典的扫雷游戏 • 游戏可以通过菜单实现继续玩或者退出游…

时序分解 | Matlab实现EMD经验模态分解时间序列信号分解

时序分解 | Matlab实现EMD经验模态分解时间序列信号分解 目录 时序分解 | Matlab实现EMD经验模态分解时间序列信号分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现EMD经验模态分解时间序列信号分解 Matlab语言 算法新颖小众&#xff0c;用的人很少&#xf…

面试10000次依然会问的【ReentrantLock】,你还不会?

引言 在并发编程的世界中&#xff0c;ReentrantLock扮演着至关重要的角色。它是一个实现了重入特性的互斥锁&#xff0c;提供了比synchronized关键字更加灵活的锁定机制。ReentrantLock属于java.util.concurrent.locks包&#xff0c;是Java并发API的一部分。 与传统的synchro…

如何使用CodeceptJS、Playwright和GitHub Actions构建端到端测试流水线

介绍 端到端测试是软件开发的一个重要方面&#xff0c;因为它确保系统的所有组件都能正确运行。CodeceptJS是一个高效且强大的端到端自动化框架&#xff0c;与Playwright 结合使用时&#xff0c;它成为自动化Web、移动甚至桌面 (Electron.js) 应用程序比较好用的工具。 在本文中…

2023-11 | 短视频批量下载/爬取某个用户的所有视频 | Python

这里以鞠婧祎的个人主页为demo https://www.douyin.com/user/MS4wLjABAAAACV5Em110SiusElwKlIpUd-MRSi8rBYyg0NfpPrqZmykHY8wLPQ8O4pv3wPL6A-oz 【2023-11-4 23:02:52 星期六】可能后面随着XX的调整, 方法不再适用, 请注意 找到接口 找到https://www.douyin.com/aweme/v1/web/…

rust入门基础案例:猜数字游戏

案例出处是《Rust权威指南》&#xff0c;书中有更加详细的解释。从这个例子中&#xff0c;我们可以了解到 rust 的两个操作&#xff1a; 如何从控制台读取用户输入rust 如何生成随机数 代码格式化 编译器可在保存时对代码做格式化处理&#xff0c;底层调用 rustfmt 来实现&a…

【gpt redis】原理篇

用的黑马程序员redis课程的目录&#xff0c;但是不想听讲了。后续都是用gpt文档获取的。 1.课程介绍(Av766995956,P145) 2.Redis数据结构-动态字符串(Av766995956,P146) sds 1M是个界限 其实他是个由c语言实现的结构体 有这么几个参数 len alloc flag char[] len是实际长度 …

【漏洞复现】Apache Log4j Server 反序列化命令执行漏洞(CVE-2017-5645)

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证 1.5、深度利用1、反弹Shell 说明内容漏洞编号CVE-2017-5645漏洞名称Log4j Server …

Python 中的 Gzip 解压

我们将介绍Python中的gzip解压。 我们还将介绍如何使用 gzip 解压缩来解压缩压缩内容。 Python 中的 Gzip 解压 Python 中构建了许多用于压缩和解压缩目的的库&#xff0c;但我们将介绍 Gzip 库。 它是一种流行的数据压缩工具。 我们可以使用 gzip 通过将数据编码为人类无法读…

字符型液晶显示器LCD 1602的显示控制(Keil+Proteus)

前言 趁机把LCD 1602的实验完成了&#xff0c;那个电路图有几个地方没弄懂&#xff0c;但是去掉也没有报错&#xff0c;就没管了。 LCD1602_百度百科 (baidu.com)https://baike.baidu.com/item/LCD1602/6014393?frge_ala LCD1602液晶显示屏通过电压来改变填充在两块平行板之…

Python算法例5 x的n次幂

1. 问题描述 实现函数Pow&#xff08;x&#xff0c;n&#xff09;&#xff0c;计算并返回x的n次幂。 2. 问题示例 Pow&#xff08;2.1&#xff0c;2&#xff09;4.41&#xff1b;Pow&#xff08;0&#xff0c;1&#xff09;0&#xff1b;Pow&#xff08;1&#xff0c;0&…