【C++】 认识多态 + 多态的构成条件详细讲解

server/2024/12/22 18:24:28/

前言
C

目录

    • 1. 多态的概念
    • 2 多态的定义及实现
      • 2 .1 虚函数:
      • 2 .2 虚函数的重写:
        • 2 .2.1 虚函数重写的两个例外:
      • 2 .3 多态的两个条件(重点)
      • 2 .4 析构函数为啥写成虚函数
    • 3 新增的两个关键字
      • 3.1 final的使用:
      • 3.2 override :
    • 4 抽象类
      • 4.1 概念
    • 5 多态的原理

1. 多态的概念

通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态. 一个接口,多种行为。
重写其实是一种接口继承

2 多态的定义及实现

2 .1 虚函数:

虚函数: 即被 virtual 修饰的类成员函数称为虚函数。

class Person
{
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};

2 .2 虚函数的重写:

虚函数的重写(覆盖):

  • 派生类中有一个跟基类完全相同的虚函数
  • 即派生类虚函数与基类虚函数的返回值类型函数名字参数列表完全相同(三同)
  • 称子类的虚函数(重写)了基类的虚函数,也叫(覆盖)
    不符合重写,就构成隐藏。。。
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
virtual void BuyTicket() { cout << "买票-半价" << endl; }
/*注意:子类虚函数不加Virtual,依旧构成重写(实际最好加上)*/
2 .2.1 虚函数重写的两个例外:
  1. 协变—基类与派生类虚函数返回值类型不同:

派生类重写基类虚函数时,与基类虚函数返回值类型不同。
即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变

  • [ ] 协变的返回值类型可以不同,要求必须是(父子关系)的指针和引用
class A{};
class B : public A {};
class Person {
public:
virtual A* f() {return new A;}
};
class Student : public Person {
public:
virtual B* f() {return new B;}
};

2.析构函数的重写

  • 如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同
  • 虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor
class Person {
public:
virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:
virtual ~Student() { cout << "~Student()" << endl; }
};

2 .3 多态的两个条件(重点)

  • 必须通过基类的指针或者引用调用虚函数
  • 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
    在这里插入图片描述

2 .4 析构函数为啥写成虚函数

1.析构函数不构成多态的情况:
在这里,析构函数默认是隐藏关系

class Person {
public:~ Person () {cout << "~ Person( )" << endl;}
};
class Student : public Person {
public:~ Student () {cout << "~ Student ( )" << endl;}};

2.析构函数构成多态:

class Person {
public:virtual  ~ Person () {cout << "~ Person( )" << endl;}
};
class Student : public Person {
public:virtual ~ Student () {cout << "~ Student ( )" << endl;}};

其析构函数最好定义为虚函数。见下面的例子:

  • 指向谁,就调用谁析构。
    在这里插入图片描述
    在这里插入图片描述

3 新增的两个关键字

  • C++11提供了override和final两个关键字,可以帮助用户检测是否重写。

3.1 final的使用:

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

class Car
{
public:
virtual void Drive() final {}
};
class Benz :public Car
{
public:
virtual void Drive() {cout << "Benz-舒适" << endl;}
};

3.2 override :

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

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

重载、覆盖(重写)、隐藏(重定义)的对比
在这里插入图片描述

  • 同名的成员变量也是隐藏的关系

4 抽象类

4.1 概念

  • 在虚函数的后面写上 =0 ,则这个函数为纯虚函数。
  • 包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象
  • 派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。
  • 纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承
class Car
{
public:
virtual void Drive() = 0;
};
class Benz :public Car
{
public:
virtual void Drive()
{
cout << "Benz-舒适" << endl;
}
};

5 多态的原理

class Base
{
public:virtual void Func1()
{cout << "Func1()" << endl;
}
private:
int _b = 1;
};

通过观察测试我们发现b对象是8bytes,除了_b成员,还多一个__vfptr放在对象的前面(注意有些
平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫做虚函数表指针
在这里插入图片描述

  • 普通函数调用,编译链接时确定函数的地址,运行时直接调用。
  • **多态调用,**程序运行时去指向对象的虚表中找到函数的地址,进行调用。

总结:
多态的本质原理,当符合多态的两个条件时,
那么调用时,会到指向对象的虚表中找到对应的虚函数地址,进行调用

尾声
看到这里,相信大家对这个C++有了解了。
如果你感觉这篇博客对你有帮助,不要忘了一键三连哦


http://www.ppmy.cn/server/35642.html

相关文章

LSTM实战笔记(部署到C++上)——更新中

前几天由于自己的个人原因停止了学习 接下里继续更新一些自己项目中所用到的神经网络等 ——————————————————————————————————————————— LSTM代码介绍 建立LSTM模型时需要设置一些参数&#xff0c;包括输入数据的形状、LSTM层的…

【Web漏洞指南】XSS漏洞详细指南

【Web漏洞指南】XSS漏洞详细指南 概述XSS的三种类型执行任意 JS 代码的方式在原始HTML中注入绕过手法在 HTML标记内注入绕过手法在JavaScript代码中注入绕过手法其他绕过手法XSS常见有效载荷检索Cookies窃取页面内容键盘记录器查找内部IP地址端口扫描器自动填充密码捕获窃取 Po…

数据结构与算法:二叉树

文章目录 1 概述1.1 树1.2 二叉树 和 树 的区别 2 二叉树2.1 性质2.2 存储结构2.3 遍历2.3.1 先序遍历&#xff1a;DLR2.3.2 中序遍历&#xff1a;LDR2.3.3 后序遍历&#xff1a;LRD 3 扩展3.1 常考题 1 概述 1.1 树 树结构&#xff1a;是一种非常重要的 非线性结构&#xff…

Terraform创建模块

模块就是包含一组Terraform代码的文件夹&#xff0c;可以通过模块直接使用别人编写好的Terraform代码来创建资源。 Terraform模块是编写高质量Terraform代码&#xff0c;提升代码复用性的重要手段&#xff0c;可以说&#xff0c;一个成熟的生产环境应该是由数个可信成熟的模块组…

自制AI:Park_01修改bug

修改了一下不能存东西&#xff0c;不能打开东西的bug #include<bits/stdc.h> #include<windows.h> using namespace std; double mem10737418240; map<string,string> jishiben; string mulu"朴同学给你的一封信.memo\n"; int cntnote1; void sta…

蜜罐部署解析

蜜罐就是给黑客设置的一个陷阱&#xff0c;引导黑客攻击&#xff0c;但凡打蜜罐的都是真实攻击行为 蜜罐可以部署再外网 将节点部署在互联网区&#xff0c;用来感知互联网来自自动化蠕虫、竞争对手和境外的 真实威胁&#xff0c;甚至发现针对客户的 0day攻击&#xff0c;通过和…

java递归-(迷宫问题)

前面 这里我们来玩个有趣的事情&#xff0c;链接是0221_韩顺平Java_老鼠出迷宫1_哔哩哔哩_bilibili 我们要找的是小老鼠按路径走到右下点 要点 我们这里方法调用时对于引用类型&#xff1a;如java中引用数据类型有哪些&#xff1f;_java引用数据类型-CSDN博客 会共享引用类型…

DNS、ICMP、NAT以及代理服务器

目录 1. DNS 1.1. DNS 背景 1.2. 域名简介 1.3. 域名解析过程 2. ICMP 2.1. ICMP 的功能 2.2. ICMP 的报文格式 2.3. ping 命令 2.4. traceroute 命令 3. NAT和代理服务器 3.1. NAT 技术 3.2. NAT IP转换过程 3.3. NAT 技术的缺陷 3.4. 代理服务器 3.4.1. 正向…