您的位置:首页 > 娱乐 > 明星 > 前端培训机构有用吗_网上怎么赚钱最快最有效_91_seo教学网站

前端培训机构有用吗_网上怎么赚钱最快最有效_91_seo教学网站

2024/12/26 8:13:15 来源:https://blog.csdn.net/sundongsdu/article/details/143000937  浏览:    关键词:前端培训机构有用吗_网上怎么赚钱最快最有效_91_seo教学网站
前端培训机构有用吗_网上怎么赚钱最快最有效_91_seo教学网站

1. 关于虚继承

虚继承可以在菱形继承体系中,防止派生类中有多份重复祖基类内容。如下图所示,如果是常规继承,Class Final中会有两份Class Base的内容。通过虚继承,即Derived1 虚继承自Base, Derived2 也虚继承自Base, 那么Final中将最终保留一份Base部分的内容。

2. 代码示例

#include <iostream>class Base {
public:virtual void show() {std::cout << "Base::show() called" << std::endl;}int a = 1;
};class Derived1 : virtual public Base {
public:void show() override {std::cout << "Derived1::show() called" << std::endl;}int b = 2;
};class Derived2 : virtual public Base {
public:void show() override {std::cout << "Derived2::show() called" << std::endl;}int c = 3;
};class Final : public Derived1, public Derived2 {
public:void show() override {std::cout << "Final::show() called" << std::endl;}int d = 4;
};int main() {std::cout << "Base size: " << sizeof(Base) << std::endl;std::cout << "Derived1 size: " << sizeof(Derived1) << std::endl;std::cout << "Derived2 size: " << sizeof(Derived2) << std::endl;std::cout << "Final size: " << sizeof(Final) << std::endl;Derived1 d1;std::cout << "d1 addr: " << &d1 << std::endl;Base *bptr = &d1;std::cout << "bptr: " << bptr << std::endl;bptr->show();Final obj;std::cout << "final obj  addr: " << &obj << std::endl;Derived1 *d1_ptr = &obj;std::cout << "d1_ptr: " << d1_ptr << std::endl;d1_ptr->show();Derived2 *d2_ptr = &obj;std::cout << "d2_ptr: " << d2_ptr << std::endl;d2_ptr->show();Base *b_ptr = &obj;std::cout << "b_ptr: " << b_ptr << std::endl;b_ptr->show();return 0;
}

运行结果:

Base size: 16
Derived1 size: 32
Derived2 size: 32
Final size: 48
d1 addr: 0x7fffffffe240
bptr: 0x7fffffffe250
Derived1::show() called
final obj  addr: 0x7fffffffe210
d1_ptr: 0x7fffffffe210
Final::show() called
d2_ptr: 0x7fffffffe220
Final::show() called
b_ptr: 0x7fffffffe230
Final::show() called

3. Derived1 对象的内存布局

3.1 查看 Derived1对象的内存

(gdb) p d1
$1 = {<Base> = {_vptr.Base = 0x401158 <vtable for Derived1+56>, a = 1}, _vptr.Derived1 = 0x401138 <vtable for Derived1+24>, b = 2}

注意,虽然Base部分写在了前面,但是对于虚继承,实际内存中Base被放在了后面:

(gdb) x/8x &d1
0x7fffffffe240: 0x00401138      0x00000000      0x00000002      0x00000000
0x7fffffffe250: 0x00401158      0x00000000      0x00000001      0x00000000

可以看到,对象的开头内容是 0x00401138, 也就是_vptr.Derived1 = 0x401138 <vtable for Derived1+24>, 紧接着是Derived1的int b, 然后才是_vptr.Base = 0x401158 <vtable for Derived1+56>,然后是Base的数据成员int a.

3.2 查看虚表

1) 首先查看_vptr.Derived1 = 0x401138 <vtable for Derived1+24>

(gdb) x/x 0x401138
0x401138 <vtable for Derived1+24>:      0x00400c7c
(gdb) x/i 0x00400c7c0x400c7c <Derived1::show()>: push   %rbp

可见就是Derived1重写的show()

2) 继续查看 _vptr.Base = 0x401158 <vtable for Derived1+56>

(gdb) x/x 0x401158
0x401158 <vtable for Derived1+56>:      0x00400ca7
(gdb) x/i 0x00400ca70x400ca7 <virtual thunk to Derived1::show()>:        mov    (%rdi),%r10
(gdb) disass 0x00400ca7,+10
Dump of assembler code from 0x400ca7 to 0x400cb1:0x0000000000400ca7 <virtual thunk to Derived1::show()+0>:    mov    (%rdi),%r100x0000000000400caa <virtual thunk to Derived1::show()+3>:    add    -0x18(%r10),%rdi0x0000000000400cae <virtual thunk to Derived1::show()+7>:    jmp    0x400c7c <Derived1::show()>0x0000000000400cb0 <Derived2::show()+0>:     push   %rbp
End of assembler dump.

可以看到,虚表中存放的是thunk代码的地址。

这段thunk代码的用途:当通过基类指针指向Derived1对象时,即Base *bptr = &d1, 由于当前的bptr指向的是Derived1对象中的Base部分(此部分位于对象靠后的位置),而非Derived1对象的真正起始地址,因此通过bptr执行虚函数时,为了执行真正的Derived1中重写的虚函数Derived1::show(),需要调整this指针到Derived1对象的起始地址。

mov    (%rdi),%r10    # rdi中存放的是this指针,也就是指向Base part的指针,而非Derived对象的起始地址, 此操作将this的开头虚表地址条目也就是_vptr.Base = 0x401158 <vtable for Derived1+56> 放入r10

add    -0x18(%r10),%rdi  #  将_vptr.Base的虚表地址-0x18 也就是前移3个条目获取里面存放的offset, 然后将此offset 加到rdi, 执行此操作之前rdi是0x7fffffffe250, offset是0xfffffffffffffff0xf

执行add操作以后,可以得到Derived1对象的起始地址: 0x7fffffffe250 + 0xfffffffffffffff0 = 0x7fffffffe240

(gdb) x/2x 0x401158 - 0x18
0x401140 <vtable for Derived1+32>:      0xfffffff0      0xffffffff

3.3 Derived1 内存布局

 其中虚表中thunk地址前面的条目0x00401200 指向typeinfo

(gdb) x/x 0x00401200
0x401200 <typeinfo for Derived1>:       0x00601d98

4. Final 对象的内存布局

4.1 查看Final对象内存

(gdb) p obj
$22 = {<Derived1> = {<Base> = {_vptr.Base = 0x401060 <vtable for Final+88>, a = 1}, _vptr.Derived1 = 0x401020 <vtable for Final+24>,b = 2}, <Derived2> = {_vptr.Derived2 = 0x401040 <vtable for Final+56>, c = 3}, d = 4}

同样,这只是逻辑视图,而不是内存实际布局。

查看实际内存布局:

(gdb) x/12x &obj
0x7fffffffe210: 0x00401020      0x00000000      0x00000002      0x00000000
0x7fffffffe220: 0x00401040      0x00000000      0x00000003      0x00000004
0x7fffffffe230: 0x00401060      0x00000000      0x00000001      0x00000000

顺序依次是Derived1 part, Derived2 part, Base part

4.2 查看虚表

1)首先查看_vptr.Derived1 = 0x401020 <vtable for Final+24>

(gdb) x/32x 0x401020
0x401020 <vtable for Final+24>: 0x00400ce4      0x00000000      0x00000010      0x00000000
0x401030 <vtable for Final+40>: 0xfffffff0      0xffffffff      0x00401188      0x00000000
0x401040 <vtable for Final+56>: 0x00400d18      0x00000000      0xffffffe0      0xffffffff
0x401050 <vtable for Final+72>: 0xffffffe0      0xffffffff      0x00401188      0x00000000
0x401060 <vtable for Final+88>: 0x00400d0f      0x00000000      0x00401020      0x00000000
0x401070 <VTT for Final+8>:     0x004010b8      0x00000000      0x004010d8      0x00000000
0x401080 <VTT for Final+24>:    0x004010f8      0x00000000      0x00401118      0x00000000
0x401090 <VTT for Final+40>:    0x00401060      0x00000000      0x00401040      0x00000000
(gdb) x/i 0x00400ce40x400ce4 <Final::show()>:    push   %rbp

可见虚表中的虚函数地址就是<Final::show()>

2) 继续查看_vptr.Derived2 = 0x401040 <vtable for Final+56>

(gdb) x/x 0x401040
0x401040 <vtable for Final+56>: 0x00400d18
(gdb) x/i 0x00400d180x400d18 <non-virtual thunk to Final::show()>:       sub    $0x10,%rdi
(gdb) disass 0x00400d18,+10
Dump of assembler code from 0x400d18 to 0x400d22:0x0000000000400d18 <non-virtual thunk to Final::show()+0>:   sub    $0x10,%rdi0x0000000000400d1c <non-virtual thunk to Final::show()+4>:   jmp    0x400ce4 <Final::show()>End of assembler dump.

可以看到,当使用Derived2 *d2_ptr = &obj 来调用虚函数d2_ptr->show();时,需要调整this 指针,但是这个调整比较简单,就是用当前指向Derived2 part的this指针减去0x10

3) 继续查看_vptr.Base = 0x401060 <vtable for Final+88>

(gdb) x/x 0x401060
0x401060 <vtable for Final+88>: 0x00400d0f
(gdb) x/i 0x00400d0f0x400d0f <virtual thunk to Final::show()>:   mov    (%rdi),%r10
(gdb) disass 0x00400d0f,+10
Dump of assembler code from 0x400d0f to 0x400d19:0x0000000000400d0f <virtual thunk to Final::show()+0>:       mov    (%rdi),%r100x0000000000400d12 <virtual thunk to Final::show()+3>:       add    -0x18(%r10),%rdi0x0000000000400d16 <virtual thunk to Final::show()+7>:       jmp    0x400ce4 <Final::show()>

这个就类似于上面讨论Derived1内存布局时通过基类指针操作虚函数时的this指针调整。

4.3 Final 内存布局

其中虚表中thunk地址前面的条目0x00401188指向typeinfo :

(gdb) x/i 0x00401188
   0x401188 <typeinfo for Final>:       cwtl

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com