深入C++虚函数

我一直认为 c++ 的精髓是 虚函数。

虚函数是运行时多状的基础。

今天就来扒一扒虚函数的内部机制。

没有虚函数

我们从 内存 来看 虚函数的 底层机制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>


class Base {
public:
int base_1;
int base_2;

};

int main() {
Base b;

std::cout << sizeof(b) << '\n'; // 8
std::cout << offsetof(Base, base_1) << '\n'; // 0
std::cout << offsetof(Base, base_2) << '\n'; // 4
}

// 结果很明显, 不需要多说什么。

只有一个虚函数

接下来,我们看一下,带虚函数的对象的内存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>


class Base {
public:
int base_1;
int base_2;
virtual void func() {}
};

int main() {
Base b;

std::cout << sizeof(b) << '\n'; // 16
std::cout << offsetof(Base, base_1) << '\n'; // 8
std::cout << offsetof(Base, base_2) << '\n'; // 12
}


可以看出来,对象前面多了八个字节。让我们来看看多了的是什么。

其实我们都知道,虚函数是在一个虚函数表里面。那么我们看看对象 b 的布局。

可以看到里面有个 指针,我们把里面的值打出来,看到是 一个函数指针,指向了 func()。

新定义一个变量 b1,查看 b1 的虚函数表,和内容,可以看到跟 b 是一摸一样的。说明一个类,只有一个虚函数表。

![image-20230605165441623](/Users/leoric/Library/Application Support/typora-user-images/image-20230605165441623.png)

多个虚函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>


class Base {
public:
int base_1;
int base_2;
virtual void func() {}
virtual void func1() {}
};

int main() {
Base b;

std::cout << sizeof(b) << '\n'; // 16
std::cout << offsetof(Base, base_1) << '\n'; // 8
std::cout << offsetof(Base, base_2) << '\n'; // 12
}


可以看到,对象对大小不变, 虚函数表里多了 func1。

![image-20230605170310030](/Users/leoric/Library/Application Support/typora-user-images/image-20230605170310030.png)

单继承且本身不存在虚函数

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
#include <iostream>


class Base {
public:
int base_1;
int base_2;

virtual void func() {}
virtual void func1() {}

};


class Derive : public Base
{
public:
int derive_1;
int derive_2;
};

int main() {
Base b;
Base b1;

std::cout << sizeof(b) << '\n';
std::cout << offsetof(Base, base_1) << '\n';
std::cout << offsetof(Base, base_2) << '\n';


Derive d;
std::cout << sizeof(b) << '\n'; //16

}

直接看内存, 虚函数表的地址不一样,但是指向了相同的函数。

![image-20230605172826693](/Users/leoric/Library/Application Support/typora-user-images/image-20230605172826693.png)

本身不存在虚函数但存在基类虚函数覆盖