C++:如何用简单的汇编指令,实现C++复杂抽象的面向对象概念?——「7、多继承(虚函数表/Thunk函数)」

149 阅读8分钟

C++是有多继承的,多继承时,动态绑定是怎么实现的呢?

C++代码如下:

#include <iostream>
/**
 * 继承:多继承 多个父类继承
 */

#include <iostream>
using namespace std;

class Father1
{

public:
    int a;
    virtual void print()
    {
        a = 1;
    }
};

class Father2
{
public:
    int b;
    virtual void print()
    {
        b = 2;
    }
};

class Father3
{
public:
    int c;
    virtual void print()
    {
        c = 3;
    }
};

class Father4
{
public:
    int d;
    virtual void print()
    {
        d = 4;
    }
};

class Father5
{
public:
    int e;
    virtual void print()
    {
        e = 5;
    }
};

class Son : public Father1, public Father2, public Father3, public Father4, public Father5
{

public:
    int s;
    void print() override
    {
        s = 12;
    }
};

int main()
{
    Son son;
    Father1 &f1 = son;
    f1.print();

    f1.Father1::print();

    Father2 &f2 = son;
    f2.print();

    Father3 &f3 = son;
    f3.print();

    Father4 &f4 = son;
    f4.print();

    Father5 &f5 = son;
    f5.print();
    return 0;
}

main()主函数汇编代码如下(其余的汇编在最下面):

main:
.LFB1528:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$144, %rsp
	movq	%fs:40, %rax
	movq	%rax, -8(%rbp)
	xorl	%eax, %eax

	leaq	-96(%rbp), %rax
	movq	%rax, %rdi
	call	_ZN3SonC1Ev # Son的构造函数,在这里面做了下面几件事:
						# 		1. 调用各个父类的构造函数
						#		2. 将Father1父类对象的前8个字节设置为Son类的虚函数表指针
						# 		3. 将Father2、345父类对象的前8个字节设置为它们对应的Thunk函数指针

	leaq	-96(%rbp), %rax
	movq	%rax, -136(%rbp)
	movq	-136(%rbp), %rax  # rbp-96
	movq	(%rax), %rax # Son类虚函数表首地址
	movq	(%rax), %rdx # Son类虚函数表的第一个元素的指针,即第一个虚函数
	movq	-136(%rbp), %rax # rbp-96,Son对象首地址
	movq	%rax, %rdi
	call	*%rdx # f1.print();,实际调用的是Son类虚函数表的第一个元素函数,即Son重写的那个print()函数

	movq	-136(%rbp), %rax
	movq	%rax, %rdi
	call	_ZN7Father15printEv # f1.Father1::print();,这就很像调用一个普通函数

	leaq	-96(%rbp), %rax
	addq	$16, %rax
	movq	%rax, -128(%rbp)
	leaq	_ZThn16_N3Son5printEv(%rip), %rdx
	movq	-128(%rbp), %rax
	movq	%rax, %rdi # rbp-96+16
	call	*%rdx # f2.print(); 调用Father2的Thunk指针,rdi传入的是Father2的首地址即rbp-96+16,在Thunk函数里会把指针-16, 修正为Son对象的首地址指针

	leaq	-96(%rbp), %rax
	addq	$32, %rax
	movq	%rax, -120(%rbp)
	leaq	_ZThn32_N3Son5printEv(%rip), %rdx
	movq	-120(%rbp), %rax
	movq	%rax, %rdi # rbp-96+32
	call	*%rdx # f3.print();  同 Father2

	leaq	-96(%rbp), %rax
	addq	$48, %rax
	movq	%rax, -112(%rbp)
	leaq	_ZThn48_N3Son5printEv(%rip), %rdx
	movq	-112(%rbp), %rax
	movq	%rax, %rdi
	call	*%rdx # f4.print();  同 Father2

	leaq	-96(%rbp), %rax
	addq	$64, %rax
	movq	%rax, -104(%rbp)
	leaq	_ZThn64_N3Son5printEv(%rip), %rdx
	movq	-104(%rbp), %rax
	movq	%rax, %rdi
	call	*%rdx # f5.print();  同 Father2

	movl	$0, %eax
	movq	-8(%rbp), %rcx
	xorq	%fs:40, %rcx
	je	.L15
	call	__stack_chk_fail@PLT

在main函数对应的汇编代码里,首先调用Son类对象的构造函数,这构造函数主要做以下3件事:

1. 调用各个父类的构造函数
2. 将Father1父类对象的前8个字节设置为Son类的虚函数表指针
3. 将Father2、3、4、5父类对象的前8个字节设置为它们对应的Thunk函数指针

首先,子类对象与父类对象在栈内存的排布位置是,从子类起始地址开始,先放第1个父类对象,再放第2个,以此类推...5个父类对象都放完了,开始放子类的成员变量。

于是,就有了上述Son类构造函数的3件事,先调用父类构造函数把自己的变量该初始化的初始化一下,然后修改5个父类对象的前8个字节(虚函数表地址),第1个父类的虚函数表地址修改为Son类的虚函数表地址,其余的父类对象的虚函数表地址,全部都修改为它们自己对应的Thunk函数的地址。

而Thunk函数就只做1件事,把传入的参数(对应父类对象的地址),修改为Son类对象首地址放到rdi,然后调用子类的重写函数,实现Father类指针指向子类对象时动态绑定问题!这就是多继承下实现【动态绑定】的本质,其实原理很简单。

我的疑问是,【动态绑定】哪里动态了?不还是编译器提前都算好了?哪有堆内存动态分配的样子?那才是真的动态,直接问操作系统要堆内存,具体哪块有内存谁也不知道,每时每刻都不一样,这才是真正的动态啊,不懂。相对地址是被编译器计算好了,只不过真正调用时,需要动态修改函数指针,所以被称为“动态”吧!

第2个问题,如果上述所有对象都是全局变量,会怎么样?子类父类对象会分配到哪块内存?局部main函数中是分配在main函数的栈帧里的,那么全局变量放哪里?下篇文章探索下!

以下是上述C++代码的全部汇编程序:

	.file	"3-object-class-inherit-6.cpp"
	.text
	.section	.rodata
	.type	_ZStL19piecewise_construct, @object
	.size	_ZStL19piecewise_construct, 1
_ZStL19piecewise_construct:
	.zero	1
	.local	_ZStL8__ioinit
	.comm	_ZStL8__ioinit,1,1
	.section	.text._ZN7Father15printEv,"axG",@progbits,_ZN7Father15printEv,comdat
	.align 2
	.weak	_ZN7Father15printEv
	.type	_ZN7Father15printEv, @function
_ZN7Father15printEv:
.LFB1522:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movq	%rdi, -8(%rbp)
	movq	-8(%rbp), %rax
	movl	$1, 8(%rax)
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1522:
	.size	_ZN7Father15printEv, .-_ZN7Father15printEv
	.section	.text._ZN7Father25printEv,"axG",@progbits,_ZN7Father25printEv,comdat
	.align 2
	.weak	_ZN7Father25printEv
	.type	_ZN7Father25printEv, @function
_ZN7Father25printEv:
.LFB1523:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movq	%rdi, -8(%rbp)
	movq	-8(%rbp), %rax
	movl	$2, 8(%rax)
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1523:
	.size	_ZN7Father25printEv, .-_ZN7Father25printEv
	.section	.text._ZN7Father35printEv,"axG",@progbits,_ZN7Father35printEv,comdat
	.align 2
	.weak	_ZN7Father35printEv
	.type	_ZN7Father35printEv, @function
_ZN7Father35printEv:
.LFB1524:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movq	%rdi, -8(%rbp)
	movq	-8(%rbp), %rax
	movl	$3, 8(%rax)
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1524:
	.size	_ZN7Father35printEv, .-_ZN7Father35printEv
	.section	.text._ZN7Father45printEv,"axG",@progbits,_ZN7Father45printEv,comdat
	.align 2
	.weak	_ZN7Father45printEv
	.type	_ZN7Father45printEv, @function
_ZN7Father45printEv:
.LFB1525:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movq	%rdi, -8(%rbp)
	movq	-8(%rbp), %rax
	movl	$4, 8(%rax)
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1525:
	.size	_ZN7Father45printEv, .-_ZN7Father45printEv
	.section	.text._ZN7Father55printEv,"axG",@progbits,_ZN7Father55printEv,comdat
	.align 2
	.weak	_ZN7Father55printEv
	.type	_ZN7Father55printEv, @function
_ZN7Father55printEv:
.LFB1526:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movq	%rdi, -8(%rbp)
	movq	-8(%rbp), %rax
	movl	$5, 8(%rax)
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1526:
	.size	_ZN7Father55printEv, .-_ZN7Father55printEv
	.section	.text._ZN3Son5printEv,"axG",@progbits,_ZN3Son5printEv,comdat
	.align 2
	.weak	_ZN3Son5printEv
	.type	_ZN3Son5printEv, @function
_ZN3Son5printEv:
.LFB1527:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movq	%rdi, -8(%rbp)
	movq	-8(%rbp), %rax
	movl	$12, 76(%rax)
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1527:
	.size	_ZN3Son5printEv, .-_ZN3Son5printEv
	.set	.LTHUNK0,_ZN3Son5printEv
	.weak	_ZThn64_N3Son5printEv
	.type	_ZThn64_N3Son5printEv, @function
_ZThn64_N3Son5printEv:
.LFB2029:
	.cfi_startproc
	endbr64
	subq	$64, %rdi
	jmp	.LTHUNK0
	.cfi_endproc
.LFE2029:
	.size	_ZThn64_N3Son5printEv, .-_ZThn64_N3Son5printEv
	.set	.LTHUNK1,_ZN3Son5printEv
	.weak	_ZThn48_N3Son5printEv
	.type	_ZThn48_N3Son5printEv, @function
_ZThn48_N3Son5printEv:
.LFB2030:
	.cfi_startproc
	endbr64
	subq	$48, %rdi
	jmp	.LTHUNK1
	.cfi_endproc
.LFE2030:
	.size	_ZThn48_N3Son5printEv, .-_ZThn48_N3Son5printEv
	.set	.LTHUNK2,_ZN3Son5printEv
	.weak	_ZThn32_N3Son5printEv
	.type	_ZThn32_N3Son5printEv, @function
_ZThn32_N3Son5printEv:
.LFB2031:
	.cfi_startproc
	endbr64
	subq	$32, %rdi
	jmp	.LTHUNK2
	.cfi_endproc
.LFE2031:
	.size	_ZThn32_N3Son5printEv, .-_ZThn32_N3Son5printEv
	.set	.LTHUNK3,_ZN3Son5printEv
	.weak	_ZThn16_N3Son5printEv
	.type	_ZThn16_N3Son5printEv, @function
_ZThn16_N3Son5printEv:
.LFB2032:
	.cfi_startproc
	endbr64
	subq	$16, %rdi
	jmp	.LTHUNK3
	.cfi_endproc
.LFE2032:
	.size	_ZThn16_N3Son5printEv, .-_ZThn16_N3Son5printEv
	.section	.text._ZN7Father1C2Ev,"axG",@progbits,_ZN7Father1C5Ev,comdat
	.align 2
	.weak	_ZN7Father1C2Ev
	.type	_ZN7Father1C2Ev, @function
_ZN7Father1C2Ev:
.LFB1531:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movq	%rdi, -8(%rbp)
	leaq	16+_ZTV7Father1(%rip), %rdx
	movq	-8(%rbp), %rax
	movq	%rdx, (%rax)
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1531:
	.size	_ZN7Father1C2Ev, .-_ZN7Father1C2Ev
	.weak	_ZN7Father1C1Ev
	.set	_ZN7Father1C1Ev,_ZN7Father1C2Ev
	.section	.text._ZN7Father2C2Ev,"axG",@progbits,_ZN7Father2C5Ev,comdat
	.align 2
	.weak	_ZN7Father2C2Ev
	.type	_ZN7Father2C2Ev, @function
_ZN7Father2C2Ev:
.LFB1534:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movq	%rdi, -8(%rbp)
	leaq	16+_ZTV7Father2(%rip), %rdx
	movq	-8(%rbp), %rax
	movq	%rdx, (%rax)
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1534:
	.size	_ZN7Father2C2Ev, .-_ZN7Father2C2Ev
	.weak	_ZN7Father2C1Ev
	.set	_ZN7Father2C1Ev,_ZN7Father2C2Ev
	.section	.text._ZN7Father3C2Ev,"axG",@progbits,_ZN7Father3C5Ev,comdat
	.align 2
	.weak	_ZN7Father3C2Ev
	.type	_ZN7Father3C2Ev, @function
_ZN7Father3C2Ev:
.LFB1537:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movq	%rdi, -8(%rbp)
	leaq	16+_ZTV7Father3(%rip), %rdx
	movq	-8(%rbp), %rax
	movq	%rdx, (%rax)
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1537:
	.size	_ZN7Father3C2Ev, .-_ZN7Father3C2Ev
	.weak	_ZN7Father3C1Ev
	.set	_ZN7Father3C1Ev,_ZN7Father3C2Ev
	.section	.text._ZN7Father4C2Ev,"axG",@progbits,_ZN7Father4C5Ev,comdat
	.align 2
	.weak	_ZN7Father4C2Ev
	.type	_ZN7Father4C2Ev, @function
_ZN7Father4C2Ev:
.LFB1540:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movq	%rdi, -8(%rbp)
	leaq	16+_ZTV7Father4(%rip), %rdx
	movq	-8(%rbp), %rax
	movq	%rdx, (%rax)
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1540:
	.size	_ZN7Father4C2Ev, .-_ZN7Father4C2Ev
	.weak	_ZN7Father4C1Ev
	.set	_ZN7Father4C1Ev,_ZN7Father4C2Ev
	.section	.text._ZN7Father5C2Ev,"axG",@progbits,_ZN7Father5C5Ev,comdat
	.align 2
	.weak	_ZN7Father5C2Ev
	.type	_ZN7Father5C2Ev, @function
_ZN7Father5C2Ev:
.LFB1543:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movq	%rdi, -8(%rbp)
	leaq	16+_ZTV7Father5(%rip), %rdx # Father5的构造函数地址
	movq	-8(%rbp), %rax
	movq	%rdx, (%rax) # rbp-96+64,Father5的构造函数地址放入Father5对象的首地址处,前8个字节
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1543:
	.size	_ZN7Father5C2Ev, .-_ZN7Father5C2Ev
	.weak	_ZN7Father5C1Ev
	.set	_ZN7Father5C1Ev,_ZN7Father5C2Ev
	.section	.text._ZN3SonC2Ev,"axG",@progbits,_ZN3SonC5Ev,comdat
	.align 2
	.weak	_ZN3SonC2Ev
	.type	_ZN3SonC2Ev, @function
_ZN3SonC2Ev:
.LFB1545:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp

	movq	%rdi, -8(%rbp) # -96(%rbp)
	movq	-8(%rbp), %rax
	movq	%rax, %rdi # -96(%rbp)
	call	_ZN7Father1C2Ev

	movq	-8(%rbp), %rax
	addq	$16, %rax  # rbp-96+16
	movq	%rax, %rdi
	call	_ZN7Father2C2Ev

	movq	-8(%rbp), %rax
	addq	$32, %rax  # rbp-96+32
	movq	%rax, %rdi
	call	_ZN7Father3C2Ev

	movq	-8(%rbp), %rax
	addq	$48, %rax  # rbp-96+48
	movq	%rax, %rdi
	call	_ZN7Father4C2Ev

	movq	-8(%rbp), %rax
	addq	$64, %rax  # rbp-96+64
	movq	%rax, %rdi
	call	_ZN7Father5C2Ev # Father5类的构造函数地址,放入Father5对象的首地址前8个字节处

	leaq	16+_ZTV3Son(%rip), %rdx # _ZN3Son5printEv
	movq	-8(%rbp), %rax # Son类对象首地址、也是Father1对象首地址
	movq	%rdx, (%rax) # 将Son类虚函数表指针,放到Father1类的首地址

	leaq	40+_ZTV3Son(%rip), %rdx # _ZThn16_N3Son5printEv
	movq	-8(%rbp), %rax
	movq	%rdx, 16(%rax) # main函数的rbp-96+16 Father2对象的首地址

	leaq	64+_ZTV3Son(%rip), %rdx # _ZThn32_N3Son5printEv
	movq	-8(%rbp), %rax
	movq	%rdx, 32(%rax) # main函数的rbp-96+32 Father3对象的首地址

	leaq	88+_ZTV3Son(%rip), %rdx # _ZThn48_N3Son5printEv
	movq	-8(%rbp), %rax
	movq	%rdx, 48(%rax) #  main函数的rbp-96+48 Father4对象的首地址

	leaq	112+_ZTV3Son(%rip), %rdx # _ZThn64_N3Son5printEv,调整This指针
	movq	-8(%rbp), %rax
	movq	%rdx, 64(%rax) # main函数的rbp-96+64 Father5对象的首地址
	nop
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1545:
	.size	_ZN3SonC2Ev, .-_ZN3SonC2Ev
	.weak	_ZN3SonC1Ev
	.set	_ZN3SonC1Ev,_ZN3SonC2Ev
	.text
	.globl	main
	.type	main, @function
main:
.LFB1528:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$144, %rsp
	movq	%fs:40, %rax
	movq	%rax, -8(%rbp)
	xorl	%eax, %eax

	leaq	-96(%rbp), %rax
	movq	%rax, %rdi
	call	_ZN3SonC1Ev # 在这里面做了下面几件事:
						# 		1. 调用各个父类的构造函数
						#		2. 将Father1父类对象的前8个字节设置为Son类的虚函数表指针
						# 		3. 将Father2、345父类对象的前8个字节设置为它们对应的Thunk函数指针

	leaq	-96(%rbp), %rax
	movq	%rax, -136(%rbp)
	movq	-136(%rbp), %rax  # rbp-96
	movq	(%rax), %rax # Son类虚函数表首地址
	movq	(%rax), %rdx # Son类虚函数表的第一个元素的指针,即第一个虚函数
	movq	-136(%rbp), %rax # rbp-96,Son对象首地址
	movq	%rax, %rdi
	call	*%rdx # f1.print();,实际调用的是Son类虚函数表的第一个元素函数,即Son重写的那个print()函数

	movq	-136(%rbp), %rax
	movq	%rax, %rdi
	call	_ZN7Father15printEv # f1.Father1::print();,这就很像调用一个普通函数

	leaq	-96(%rbp), %rax
	addq	$16, %rax
	movq	%rax, -128(%rbp)
	leaq	_ZThn16_N3Son5printEv(%rip), %rdx
	movq	-128(%rbp), %rax
	movq	%rax, %rdi # rbp-96+16
	call	*%rdx # f2.print(); 调用Father2的Thunk指针,rdi传入的是Father2的首地址即rbp-96+16,在Thunk函数里会把指针-16, 修正为Son对象的首地址指针

	leaq	-96(%rbp), %rax
	addq	$32, %rax
	movq	%rax, -120(%rbp)
	leaq	_ZThn32_N3Son5printEv(%rip), %rdx
	movq	-120(%rbp), %rax
	movq	%rax, %rdi # rbp-96+32
	call	*%rdx # f3.print();  同 Father2

	leaq	-96(%rbp), %rax
	addq	$48, %rax
	movq	%rax, -112(%rbp)
	leaq	_ZThn48_N3Son5printEv(%rip), %rdx
	movq	-112(%rbp), %rax
	movq	%rax, %rdi
	call	*%rdx # f4.print();  同 Father2

	leaq	-96(%rbp), %rax
	addq	$64, %rax
	movq	%rax, -104(%rbp)
	leaq	_ZThn64_N3Son5printEv(%rip), %rdx
	movq	-104(%rbp), %rax
	movq	%rax, %rdi
	call	*%rdx # f5.print();  同 Father2

	movl	$0, %eax
	movq	-8(%rbp), %rcx
	xorq	%fs:40, %rcx
	je	.L15
	call	__stack_chk_fail@PLT
.L15:
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1528:
	.size	main, .-main
	.weak	_ZTV3Son
	.section	.data.rel.ro.local._ZTV3Son,"awG",@progbits,_ZTV3Son,comdat
	.align 8
	.type	_ZTV3Son, @object
	.size	_ZTV3Son, 120
_ZTV3Son:
	.quad	0
	.quad	_ZTI3Son
	.quad	_ZN3Son5printEv
	.quad	-16
	.quad	_ZTI3Son
	.quad	_ZThn16_N3Son5printEv
	.quad	-32
	.quad	_ZTI3Son
	.quad	_ZThn32_N3Son5printEv
	.quad	-48
	.quad	_ZTI3Son
	.quad	_ZThn48_N3Son5printEv
	.quad	-64
	.quad	_ZTI3Son
	.quad	_ZThn64_N3Son5printEv
	.weak	_ZTV7Father5
	.section	.data.rel.ro.local._ZTV7Father5,"awG",@progbits,_ZTV7Father5,comdat
	.align 8
	.type	_ZTV7Father5, @object
	.size	_ZTV7Father5, 24
_ZTV7Father5:
	.quad	0
	.quad	_ZTI7Father5
	.quad	_ZN7Father55printEv
	.weak	_ZTV7Father4
	.section	.data.rel.ro.local._ZTV7Father4,"awG",@progbits,_ZTV7Father4,comdat
	.align 8
	.type	_ZTV7Father4, @object
	.size	_ZTV7Father4, 24
_ZTV7Father4:
	.quad	0
	.quad	_ZTI7Father4
	.quad	_ZN7Father45printEv
	.weak	_ZTV7Father3
	.section	.data.rel.ro.local._ZTV7Father3,"awG",@progbits,_ZTV7Father3,comdat
	.align 8
	.type	_ZTV7Father3, @object
	.size	_ZTV7Father3, 24
_ZTV7Father3:
	.quad	0
	.quad	_ZTI7Father3
	.quad	_ZN7Father35printEv
	.weak	_ZTV7Father2
	.section	.data.rel.ro.local._ZTV7Father2,"awG",@progbits,_ZTV7Father2,comdat
	.align 8
	.type	_ZTV7Father2, @object
	.size	_ZTV7Father2, 24
_ZTV7Father2:
	.quad	0
	.quad	_ZTI7Father2
	.quad	_ZN7Father25printEv
	.weak	_ZTV7Father1
	.section	.data.rel.ro.local._ZTV7Father1,"awG",@progbits,_ZTV7Father1,comdat
	.align 8
	.type	_ZTV7Father1, @object
	.size	_ZTV7Father1, 24
_ZTV7Father1:
	.quad	0
	.quad	_ZTI7Father1
	.quad	_ZN7Father15printEv
	.weak	_ZTI3Son
	.section	.data.rel.ro._ZTI3Son,"awG",@progbits,_ZTI3Son,comdat
	.align 8
	.type	_ZTI3Son, @object
	.size	_ZTI3Son, 104
_ZTI3Son:
	.quad	_ZTVN10__cxxabiv121__vmi_class_type_infoE+16
	.quad	_ZTS3Son
	.long	0
	.long	5
	.quad	_ZTI7Father1
	.quad	2
	.quad	_ZTI7Father2
	.quad	4098
	.quad	_ZTI7Father3
	.quad	8194
	.quad	_ZTI7Father4
	.quad	12290
	.quad	_ZTI7Father5
	.quad	16386
	.weak	_ZTS3Son
	.section	.rodata._ZTS3Son,"aG",@progbits,_ZTS3Son,comdat
	.type	_ZTS3Son, @object
	.size	_ZTS3Son, 5
_ZTS3Son:
	.string	"3Son"
	.weak	_ZTI7Father5
	.section	.data.rel.ro._ZTI7Father5,"awG",@progbits,_ZTI7Father5,comdat
	.align 8
	.type	_ZTI7Father5, @object
	.size	_ZTI7Father5, 16
_ZTI7Father5:
	.quad	_ZTVN10__cxxabiv117__class_type_infoE+16
	.quad	_ZTS7Father5
	.weak	_ZTS7Father5
	.section	.rodata._ZTS7Father5,"aG",@progbits,_ZTS7Father5,comdat
	.align 8
	.type	_ZTS7Father5, @object
	.size	_ZTS7Father5, 9
_ZTS7Father5:
	.string	"7Father5"
	.weak	_ZTI7Father4
	.section	.data.rel.ro._ZTI7Father4,"awG",@progbits,_ZTI7Father4,comdat
	.align 8
	.type	_ZTI7Father4, @object
	.size	_ZTI7Father4, 16
_ZTI7Father4:
	.quad	_ZTVN10__cxxabiv117__class_type_infoE+16
	.quad	_ZTS7Father4
	.weak	_ZTS7Father4
	.section	.rodata._ZTS7Father4,"aG",@progbits,_ZTS7Father4,comdat
	.align 8
	.type	_ZTS7Father4, @object
	.size	_ZTS7Father4, 9
_ZTS7Father4:
	.string	"7Father4"
	.weak	_ZTI7Father3
	.section	.data.rel.ro._ZTI7Father3,"awG",@progbits,_ZTI7Father3,comdat
	.align 8
	.type	_ZTI7Father3, @object
	.size	_ZTI7Father3, 16
_ZTI7Father3:
	.quad	_ZTVN10__cxxabiv117__class_type_infoE+16
	.quad	_ZTS7Father3
	.weak	_ZTS7Father3
	.section	.rodata._ZTS7Father3,"aG",@progbits,_ZTS7Father3,comdat
	.align 8
	.type	_ZTS7Father3, @object
	.size	_ZTS7Father3, 9
_ZTS7Father3:
	.string	"7Father3"
	.weak	_ZTI7Father2
	.section	.data.rel.ro._ZTI7Father2,"awG",@progbits,_ZTI7Father2,comdat
	.align 8
	.type	_ZTI7Father2, @object
	.size	_ZTI7Father2, 16
_ZTI7Father2:
	.quad	_ZTVN10__cxxabiv117__class_type_infoE+16
	.quad	_ZTS7Father2
	.weak	_ZTS7Father2
	.section	.rodata._ZTS7Father2,"aG",@progbits,_ZTS7Father2,comdat
	.align 8
	.type	_ZTS7Father2, @object
	.size	_ZTS7Father2, 9
_ZTS7Father2:
	.string	"7Father2"
	.weak	_ZTI7Father1
	.section	.data.rel.ro._ZTI7Father1,"awG",@progbits,_ZTI7Father1,comdat
	.align 8
	.type	_ZTI7Father1, @object
	.size	_ZTI7Father1, 16
_ZTI7Father1:
	.quad	_ZTVN10__cxxabiv117__class_type_infoE+16
	.quad	_ZTS7Father1
	.weak	_ZTS7Father1
	.section	.rodata._ZTS7Father1,"aG",@progbits,_ZTS7Father1,comdat
	.align 8
	.type	_ZTS7Father1, @object
	.size	_ZTS7Father1, 9
_ZTS7Father1:
	.string	"7Father1"
	.text
	.type	_Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2027:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp
	movl	%edi, -4(%rbp)
	movl	%esi, -8(%rbp)
	cmpl	$1, -4(%rbp)
	jne	.L18
	cmpl	$65535, -8(%rbp)
	jne	.L18
	leaq	_ZStL8__ioinit(%rip), %rdi
	call	_ZNSt8ios_base4InitC1Ev@PLT
	leaq	__dso_handle(%rip), %rdx
	leaq	_ZStL8__ioinit(%rip), %rsi
	movq	_ZNSt8ios_base4InitD1Ev@GOTPCREL(%rip), %rax
	movq	%rax, %rdi
	call	__cxa_atexit@PLT
.L18:
	nop
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE2027:
	.size	_Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
	.type	_GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB2028:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$65535, %esi
	movl	$1, %edi
	call	_Z41__static_initialization_and_destruction_0ii
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE2028:
	.size	_GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
	.section	.init_array,"aw"
	.align 8
	.quad	_GLOBAL__sub_I_main
	.hidden	__dso_handle
	.ident	"GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0"
	.section	.note.GNU-stack,"",@progbits
	.section	.note.gnu.property,"a"
	.align 8
	.long	 1f - 0f
	.long	 4f - 1f
	.long	 5
0:
	.string	 "GNU"
1:
	.align 8
	.long	 0xc0000002
	.long	 3f - 2f
2:
	.long	 0x3
3:
	.align 8
4:

上述代码主要看main函数的注释就能明白,其实多继承动态绑定的实现原理,本质就是,第1个父类对象的虚函数表地址,直接替换为子类的虚函数表地址,后面所有父类对象的虚函数表地址,全都替换为它们对应的那个Thunk函数的地址,在这个函数里会跳转this指针,本来指向的是父类对象,调整后直接指向了子类对象的虚函数表地址了,这时调用的就是子类对象的虚函数表里面的函数,也就是子类重写的那些函数,这就是多继承下实现【动态绑定】的本质,其实原理很简单。

我的疑问是,【动态绑定】哪里动态了?不还是编译器提前都算好了?哪有堆内存动态分配的样子?那才是真的动态,直接问操作系统要堆内存,具体哪块有内存谁也不知道,每时每刻都不一样,这才是真正的动态啊,不懂。

无语了,为啥发不出去,真难用,决定转到csdn了