【C++】多态中虚函数的底层理解

简介: 【C++】多态中虚函数的底层理解

1 多态的基本概念


1.1 什么是多态?


  • 多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为,通常是父类调用子类的重写函数,在C++中就是 父类指针指向子类对象,此时父类指针的向下引用就可以实现多态
  • 比如看下面的代码:
class Animal
{
public:
  //虚函数
  virtual void speak()
  {
    cout << "动物在说话" << endl;
  }
  virtual void eat(int a )
  {
    cout << "动物在吃饭" << endl;
  }
};
class Cat :public Animal
{
public:
  void speak()
  {
    cout << "小猫在说话" << endl;
  }
  void eat(int a)
  {
    cout << "小猫在吃饭" << endl;
  }
};
class Dog :public Animal
{
public:
  void speak()
  {
    cout << "小狗在说话" << endl;
  }
};

这两个类实现集成关系,简单来说就是 Animal是父类,Cat是子类,通过父类引用调用子类函数,这就是多态(字面意思就是一个对象多个状态),这样就 符合** 高内聚低耦合** 的设计原则,更容易 后期维护与修改


1.2 怎么实现多态


说了这么多,那么多态到底怎么实现呢?

  • 想必上面代码中也已经透露出了一些信息,并且十分关键。没错,就是virtual,字面意思是 虚拟的,而也的确如此,这个关键字修饰的 函数,叫做虚函数,拥有虚函数的 类,被称为 抽象类(虚类),virtual是实现多态的必要不充分条件
  • 同时 ,子类要重写父类的虚函数,什么叫重写?就是函数类型,函数名,参数列表完全相同。
  • 并且父类指针还要指向子类对象,正如下所示:
void doSpeak(Animal & animal) //Animal & animal = cat;
{
  animal.speak();
}
void test01()
{
  Cat cat;
  doSpeak(cat);
  Dog dog;
  doSpeak(dog);
}

如此方能满足实现多态的三个条件

  • 注:如果没有多态,那么父类指向子类时就无法调用子类函数,也就是此时该函数地址早就绑定好了,只能是animal说话,属于静态联编(静态);如果想调用小猫说话,这个时候函数的地址就不能早就绑定好,而是在运行阶段再去绑定函数地址,属于地址晚绑定,叫动态联编,(动态)多态的实现就是依靠动态联编。


2 虚函数的底层原理


  • 那么虚函数到底是怎么工作的呢?
void test02()
{
  Animal * animal = new Cat;
  ((void(*)()) (*(int *)*(int *)animal)) ();
  typedef void( __stdcall *FUNPOINT)(int);
  (FUNPOINT (*((int*)*(int*)animal + 1)))(10);
}
Animal * animal = new Cat;
  • 就是animal->speak();
((void(*)()) (*(int *)*(int *)animal)) ();
  • 这里 *(int *)animal 解引用到虚函数表中,先将animal类型的指针强转为int *,然后解引用得到int类型的值放到虚函数表中,然后偏移相应位置指向speak()
((void(*)()) (*(int *)*(int *)animal)) ();


image.pngimage.png

  • 调用猫说话,因为C/C++默认调用惯例 __cdecl,而用下列调用时 是__stdcall
typedef void( __stdcall *FUNPOINT)(int);
(FUNPOINT (*((int*)*(int*)animal + 1)))(10);


image.pngimage.png

🌹写在最后💖: 路漫漫其修远兮,吾将上下而求索!伙伴们,明天见!🌹🌹🌹

相关文章
|
5月前
|
C++
C++一分钟之-虚函数与抽象类
【6月更文挑战第21天】在C++中,虚函数与抽象类是多态的基础,增进类间耦合与灵活性。虚函数实现动态绑定,抽象类定义不可实例化的接口。关键点包括:记得使用`virtual`,避免滥用虚函数,确保派生类实现纯虚函数。抽象类不能直接实例化,派生类必须实现所有纯虚函数。通过实例代码学习和实践,能更好地掌握这些概念以优化代码设计。
50 2
|
1月前
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
38 2
C++入门12——详解多态1
|
1月前
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
79 1
|
3月前
|
编译器 C++ 索引
C++虚拟成员-虚函数
C++虚拟成员-虚函数
|
3月前
|
存储 编译器 C++
|
4月前
|
存储 编译器 C++
【C++】深度解剖多态(下)
【C++】深度解剖多态(下)
53 1
【C++】深度解剖多态(下)
|
4月前
|
存储 编译器 C++
|
3月前
|
存储 编译器 C++
C++多态实现的原理:深入探索与实战应用
【8月更文挑战第21天】在C++的浩瀚宇宙中,多态性(Polymorphism)无疑是一颗璀璨的星辰,它赋予了程序高度的灵活性和可扩展性。多态允许我们通过基类指针或引用来调用派生类的成员函数,而具体调用哪个函数则取决于指针或引用所指向的对象的实际类型。本文将深入探讨C++多态实现的原理,并结合工作学习中的实际案例,分享其技术干货。
74 0
|
4月前
|
机器学习/深度学习 算法 C++
C++多态崩溃问题之为什么在计算梯度下降时需要除以批次大小(batch size)
C++多态崩溃问题之为什么在计算梯度下降时需要除以批次大小(batch size)
|
4月前
|
Java 编译器 C++
【C++】深度解剖多态(上)
【C++】深度解剖多态(上)
54 2