关于虚函数的一些总结

Last updated on 9 months ago

说到多态又可以联想到虚函数,虚函数又有很多相关问题,这次做个小总结

1 . 什么是虚函数

  • 虚函数在有多态的前提下才有的特性,现在问题是多态是什么?

    多态可以分为:

    ​ 静态多态:函数重载,根据参数不同,从而调用不同的函数(这里不做重点介绍)

    ​ 动态多态: 根据操作的对象不同,从而调用不同的函数(使用虚函数实现)

    总的来说: 同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结

​ 现在看下动态多态是怎么使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Shape {
public:
Shape() {}
~Shape() {}
virtual void area() {}
};
class Rectangle : public Shape {
public:
int x, y;
Rectangle(int a = 0, int b = 0) {
x = a;
y = b;
}
void area() { cout << " Rectangle area " << x * y << endl; }
};
class Round : public Shape {
public:
int x, y;
Round(int a = 0, int b = 0) {
x = a;
y = b;
}
void area() { cout << " Rount area " << 3.14 * y << endl; }
};
int main() {
Shape *t;
Round r(1, 2);
Rectangle y(2, 3);
t = &r; //基类指针指向 Round 类r
t->area(); //Rount area 6.28
t = &y; // 基类指针指向 Rectangle
t->area(); // Rectangle area 6
system("pause");
return 1;
}

基类用 virtual 声明了函数,用基类的指针指向不同的派生类,然后基类的指向的函数也有不同的效果

那这个的原理是怎样的呢?即虚函数如何实现的?

如果一个带有虚函数的类的大小他的大小是多大呢?
image-20220314163106660

先不说为什么空类大小为1 ,我们先说带有虚函数的类大小为4, 因为带有虚函数的类有一个指向函数数组的指针,而指针是4个字节的,指针指向的数组可以函数,那么能否直接自己直接通过指针来调用虚函数呢? 答案是可以的

image-20220315144827997

如上图所示,用通过 base 实例化的 b地址 可以调用类中的虚函数,通过偏移值的不同,调用不同的函数 ,可以看出 实例化后的 b是有个指针指向一个指针数组 ,就该base类而言,内存布局如下:

image-20220315150631707

现在我们知道了虚函数表的如何存在的,那如何通过这个实现多态的呢?

用一个基类的指针指向派生类时候,如果派生类中有函数重载了父类的虚函数,则会替换虚函数表中对应的函数
就是说 : 假如派生类也有函数 f(); 此时 虚表中 f()的函数地址 会被 派生类的 f() 函数替换(也称为动态联编)
与基类指针 指向 f() 时,执行的是派生类的 f()

image-20220315152052778

大致上的原理 就这样,其实还有多细节没有补充,想着通过反汇编来看下动态联编是怎么实现,后续再来吧..

一些虚函数的问题

什么函数不能声明为虚函数

  1. 构造函数

    虚表指针的初始化时在构造函数进行的,而虚函数需要放到虚表中。在调用虚函数前,必须首先知道虚表指针,所以不行

  2. 普通函数

    虚函数需要在继承多态下使用才有意义

  3. 静态函数

    静态函数可以直接调用不需要指针

  4. 内联函数

    内联函数属于静态联编,即是编译阶段就确定调用哪个函数了,虚函数是动态联

  5. 友元函数

    有元函数不支持继承