理解多态的实现原理

简介: 理解多态的实现原理

概念:

就是多个不同的对象,在完成某种相同动作时,会产生多种不同的状态。

就比如:一个音乐播放软件,会员和普通用户 能听的歌不一样,有些歌只有会员可以听,会员也可以选择听更好的音质。

class A
{
public:
  virtual void dunc()
  {
    cout << "Hello A" << endl;
  }
  int a;
};
class B : public A
{
public:
  virtual void dunc()
  {
    cout << "Hello B" << endl;
  } 
  int b;
};
class C : public A
{
public:
  virtual void dunc()
  {
    cout << "Hello c" << endl;
  }
  int c;
};
void func(A* ptr)
{
  ptr->dunc();
}
int main()
{
  A a;
  B b;
  C c;
  func(&a);
  func(&b);
  func(&c);
  return 0;
}

1d0e657f47ee4ad79bb3045cc2c7ad6e.jpg

多态有两个要求:

子类虚函数重写父类的虚函数(重写:函数名+返回值+参数相同、是虚函数)

由父类的指针或者引用去调用虚函数


特例:

协变:

class S
{};
class W :public S
{};
class A
{
public:
  virtual S* dunc()
  {
    S s;
    return &s;
  }
  int a;
};
class B : public A
{
public:
  W* dunc()
  {
    W w;
    return &w;
  } 
  int b;
};

只要父子关系的指针和引用做返回值都算 协变

子类虚函数没有写 virtual  父类虚函数写了是没有问题的


class A
{
public:
  ~A()
  {
    cout << "delete A" << endl;
  }
  int a;
};
class B : public A
{
public:
  ~B()
  {
    cout << "delete B" << endl;
  }
  int b;
};
int main()
{
  A* x = new A;
  delete x;
  A* y = new B;
  delete y;
  return 0;
}

大家觉得这段代码的运行结果是什么

d3f6e84e917c4857b498f48396de6e2f.jpg

普通调用就是正常的:

       x->destructor() + operator delete(x)

       y->destructor() + operator delete(y)

你这个变量是什么类型,就调用谁的析构函数。那如果加上virtual呢  

class A
{
public:
  virtual ~A()
  {
    cout << "delete A" << endl;
  }
  int a;
};
class B : public A
{
public:
  ~B()
  {
    cout << "delete B" << endl;
  }
  int b;
};
int main()
{
  A* x = new A;
  delete x;
  A* y = new B;
  delete y;
  return 0;
}

1a7d125edaa54486bb36cac877cd0b92.jpg

很明显这里就是一个多态调用了,先delete A 然后在子类的析构函数里面,先析构子类再析构父类

经过这也就得出一个结论:如果设计一个类,它可能作为基类的话,就需要给基类的析构函数添加上virtual 这样可以防止子类里面的资源泄露


final:

如果有不想要被继承或者被重写的类或者函数,就可以在类名 和 函数后面加上final

1. void func() final {}
2. 
3. class A final
4. {
5. };


override:

这个是用来放在子类里面去使用的,用于检查重写(函数名 + 参数 + 返回值)的完整性


抽象类、纯虚函数:

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

抽象类:包含纯虚函数的类叫做抽象类


抽象类不能实例出对象。被继承后派生类也不能实例出对象,只有派生类重写虚函数,才能实例化出对象,纯虚函数也表明派生类必须重写,也体现出了接口继承


查表:

一个指针是不知道自己指向的是 派生类切割过来的 还是 指向自己类型 。所以在调用虚函数的时候会去查对应的虚函数表

class A
{
public:
  virtual void dunc()
  {
    cout << "Hello A" << endl;
  }
  virtual void dunc1()
  {
    cout << "dunc1" << endl;
  }
  int a;
};
class B : public A
{
public:
  virtual void dunc()
  {
    cout << "Hello B" << endl;
  }
  void dunc2()
  {
    cout << "dunc2" << endl;
  }
  int b;
};
int main()
{
  A X;
  B y;
  return 0;
}

c0cf56d90e374320988ceacf3959b32f.jpg

大家仔细地看这段代码,和这两个变量的内容:

首先,这两个虚函数表的地址是不同的;

A类里面的dunc1 是虚函数但是并没有在B类中重写,依然也是在A类的虚函数表里面的;

B类里面的虚函数表里面的第一个函数已经展现不清楚了,但我们依然知道它是重写后的dunc;


动态绑定:

静态绑定又叫前期绑定,在程序编译期间就确定了程序的行为:静态多态,比如:函数重载

动态绑定又叫后期绑定,在程序运行期间,根据具体拿到的类型确定程序的具体行为:动态多态

目录
相关文章
|
弹性计算 Java 应用服务中间件
手动部署Java Web环境(Alibaba Cloud Linux 2)
本场景带您体验如何在Alibaba Cloud Linux 2.1903 LTS 64位操作系统的云服务器上部署Java Web环境。
|
网络协议 网络虚拟化 虚拟化
|
10月前
|
敏捷开发 监控 数据可视化
看板工具提升产研团队工作效率实操
本文介绍了看板管理在产品研发团队中的应用价值,通过可视化方式呈现任务状态,提高团队协作效率。文章详细解析了五种看板的应用场景,包括Sprint Board、产品迭代计划、用户反馈收集、周报看板及项目整体进度看板,旨在帮助团队更高效地管理和推进项目。
142 1
看板工具提升产研团队工作效率实操
|
存储 分布式计算 安全
MaxCompute Bloomfilter index 在蚂蚁安全溯源场景大规模点查询的最佳实践
MaxCompute 在11月最新版本中全新上线了 Bloomfilter index 能力,针对大规模数据点查场景,支持更细粒度的数据裁剪,减少查询过程中不必要的数据扫描,从而提高整体的查询效率和性能。
|
机器学习/深度学习 数据采集 PyTorch
高效数据加载与预处理:利用 DataLoader 优化训练流程
【8月更文第29天】 在深度学习中,数据加载和预处理是整个训练流程的重要组成部分。随着数据集规模的增长,数据加载的速度直接影响到模型训练的时间成本。为了提高数据加载效率并简化数据预处理流程,PyTorch 提供了一个名为 `DataLoader` 的工具类。本文将详细介绍如何使用 PyTorch 的 `DataLoader` 来优化数据加载和预处理步骤,并提供具体的代码示例。
2017 1
|
监控 Java 调度
Java面试题:描述Java线程池的概念、用途及常见的线程池类型。介绍一下Java中的线程池有哪些优缺点
Java面试题:描述Java线程池的概念、用途及常见的线程池类型。介绍一下Java中的线程池有哪些优缺点
213 1
|
消息中间件 前端开发 编译器
10种常见的软件架构模式简述
10种常见的软件架构模式简述
|
缓存 JavaScript
computed/watch深度监听
computed/watch深度监听
388 1
|
数据挖掘
高性能计算集群的主要应用场景
本文主要介绍弹性高性能计算集群的主要应用场景,您可以根据不同的应用场景配置不同的资源类型。
304 0
|
存储 网络协议 开发工具
WIFI DTU产品设计与实现(基于STM32F103+QT配置上位机案例设计分享)
WIFI DTU产品设计与实现(基于STM32F103+QT配置上位机案例设计分享)
505 0