当前位置: 首页 > news >正文

深入理解C++面向对象特性之一 多态

欢迎来到干货小仓库,堪比沙漠!!!

从“Hello World”到改变世界,中间隔着千万次'再试一次'.


1.多态的概念

多态的概念:通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会 产生出不同的状态

例如:在买火车票时,若是普通人买票时,是全价买票;学生买票时,是半价买票;军人买票时,是优先买票。

2.多态的定义及实现

2.1多态的构成条件

多态是在不同继承关系的类对象,去调用同一函数,产生不同的行为。比如Student继承了Person。Person对象买全价票,Student对象买半价票。

示例:

那么在继承中要构成多态的条件有两个条件:

1、必须通过基类的指针或引用调用虚函数。

2、被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。

2.2虚函数

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

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

2.3虚函数的重写

条件

①是虚函数

②函数名、返回值和参数类型必须相同

但是有两个例外:

1.协变(基类和派生类的虚函数返回值类型可以不同),但是要求返回值类型必须是父子关系的指针和引用。

2.派生类的重写虚函数可以不加 virtual,基类的重写虚函数必须加上。(建议都加上)

示例:返回值的不同

class A
{ };
class B:public A
{ };

class Person
{
public:
	virtual const A& Buyticket()
	{
		cout << "买票全价" << endl;
		return A();
	}
};
class Student :public Person
{
public:
	virtual const B& Buyticket()
	{
		cout << "买票半价" << endl;
		return B();
	}
};

2.4重载、重写(覆盖)和重定义(隐藏)的对比

3.C++ 11 override 和 final 关键字

3.1 final

作用:

①修饰虚函数,使该虚函数不能被重写。

②修饰类,使该类不能被继承。

示例:

3.2 override

作用:帮助派生类检查是否对虚函数完成重写,没有重写会报错。

4.抽象类

在虚函数的后面写上 =0,则这个函数为 纯虚函数。

包含纯虚函数的类叫做 抽象类(接口类).

4.1虚函数的重写(接口继承)

示例:以下程序的输出结果是什么?

  A: A->0     B: B->1    C: A->1    D: B->0     E: 编译出错     F: 以上都不正确
class A
   {
   public:
       virtual void func(int val = 1){ std::cout<<"A->"<< val <<std::endl;}
       virtual void test(){ func();}
   };
   
   class B : public A
   {
   public:
       void func(int val=0){ std::cout<<"B->"<< val <<std::endl; }
   };
   
   int main()
   {
       B*p = new B;
       p->test();
       return 0;
   }

正确答案:B

解析:构成虚函数的重写,调用的是B对象对重写的虚函数,但是 虚函数重写的是实现,参数都是基类的那部分(接口继承)。
变形:
答案:D

4.2内联函数、静态成员函数和构造函数是否可以是虚函数?

5.多态的底层原理

5.1虚函数表

示例:以下 sizeof(Base) 是多少?


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

解析:通过编译得到 sizeof(Base) 占 8个字节,多存了一个指针(指向虚函数表)。

注意:对基类的虚函数进行了重写,其对应的虚函数表指针指向的地址也会发生变化。

1、基类b对象和派生类d对象虚表是不一样的,这里我们发现Func1完成了重写,所以d的虚表中存的是重写的Derive::Func1,所以虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法,覆盖是原理层的叫法。

示例:

2、虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个nullptr。

3、总结一下派生类的虚表生成:

a.先将基类中的虚表内容拷贝一份到派生类虚表中。

b.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数。

c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。

示例:

4、派生类对基类的虚函数重写了,故派生类产生的对象,共用一份虚表

示例:

5.2虚函数表,存在哪里?

对比验证法:根据多态的存储模型,该对象的前面四个字节存放的是虚表的地址,分别与栈、静态区、堆、常量区对比。

5.3动态绑定与静态绑定

1. 静态绑定又称为前期绑定(早绑定)在程序编译期间确定了程序的行为也称为静态多态,如:函数重载

2. 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态

6.多继承的多态

6.1多继承的虚函数表

观察下图可以看出:多继承派生类的为重写的虚函数放在第一个继承基类部分的虚函数表中

为什么重写func1(),Base1 和 Base2 的虚表中的地址不一样,但结果一样?

需要从底层的汇编进行深入刨析,根据其会变得调用,分析得到 编译器做了处理,目的是为了修改 this 指针。

6.2菱形虚拟继承的多态


觉得不错的可以点赞+收藏咯!!!

相关文章:

  • Linux驱动开发进阶(六)- 多线程与并发
  • Redis到底能不能做主数据库?
  • xv6-labs-2024 lab1
  • QML面试笔记--UI设计篇03导航控件
  • 国内数据安全传送简述
  • python 微信小程序支付、查询、退款使用wechatpy库
  • 神经探针与价值蓝海:AI重构需求挖掘的认知拓扑学
  • 深度学习 Deep Learning 第19章 近似推理
  • 基于SpringBoot的在线拍卖系统(源码+数据库+万字文档+ppt)
  • 【LeetCode 题解】算法:34.在排序数组中查找元素的第一个和最后一个位置
  • Kafka 中的 offset 提交问题
  • Qt 资源文件(.qrc 文件)
  • 基于SpringBoot的“高校社团管理系统”的设计与实现(源码+数据库+文档+PPT)
  • 基于ensp的mpls的解决bgp域内黑洞及MPLS VPN的应用
  • 心脏滴血漏洞(CVE-2014-0160)漏洞复现
  • 探秘PythonJSON解析深度剖析json.loads处理嵌套JSON字符串的奥秘
  • 《UNIX网络编程卷1:套接字联网API》第3章 套接字编程简介
  • MBR的 扩展分区 和 逻辑分区 笔记250407
  • 循环神经网络 - 机器学习任务之同步的序列到序列模式
  • 计算机网络学习前言
  • 国税总局上海市税务局通报:收到王某对刘某某及相关企业涉税问题举报,正依法依规办理
  • 上海高院与上海妇联签协议,建立反家暴常态化联动协作机制
  • 中日东三省问题的源起——《1905年东三省事宜谈判笔记》解题
  • 30平米的无障碍酒吧里,我们将偏见折叠又摊开
  • 日月谭天丨这轮中美关税会谈让台湾社会看清了什么?
  • 美国明尼苏达州发生山火,过火面积超80平方公里