this指针
this指针是常指针
常对象的this指针是指向常量的常指针
由于this指针隐藏传参,所以常对象不能调用普通成员函数
普通成员
静态数据成员
类对象 做返回值或参数 值传递 不一定调用构造函数
1.类对象做参数:
没显式调用默认构造函数
逐成员复制到栈上
先定义后压栈
2.类对象 做返回值:
3.何时提供默认构造函数
隐藏的虚表和虚表指针
虚表指针:对象前4字节为虚表指针
虚表:虚表保存类定义的虚函数地址,先定义的虚函数排在前面
构造函数
虚表初始化:构造函数取得虚表地址保存在虚表指针
虚指针重写:
64位系统实例
Derived d;//源码
main函数调用Derived构造函数
0x0000000000400c3b <+23>: lea -0x40(%rbp),%rax
0x0000000000400c3f <+27>: mov %rax,%rdi
//通过rdi寄存器传栈变量首地址给构造函数初始化
0x0000000000400c42 <+30>: callq 0x400af8 <Derived::Derived()>
//直接调用
Dump of assembler code for function Derived::Derived():
0x0000000000400af8 <+0>: push %rbp
0x0000000000400af9 <+1>: mov %rsp,%rbp
0x0000000000400afc <+4>: push %rbx
0x0000000000400afd <+5>: sub $0x18,%rsp
0x0000000000400b01 <+9>: mov %rdi,-0x18(%rbp)
//待构造变量的首地址
0x0000000000400b05 <+13>: mov -0x18(%rbp),%rax
0x0000000000400b09 <+17>: mov %rax,%rdi
//再次传给base
0x0000000000400b0c <+20>: callq 0x400a1e <Base::Base()>
0x0000000000400b11 <+25>: mov -0x18(%rbp),%rax
0x0000000000400b15 <+29>: movq $0x400e90,(%rax)
//虚表指针重写
//没有参数列表
0x0000000000400b1c <+36>: mov $0x400e4e,%esi
//构造函数的函数体
0x0000000000400b21 <+41>: mov $0x6020e0,%edi
0x0000000000400b26 <+46>: callq 0x4008d0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x0000000000400b2b <+51>: mov $0x4008f0,%esi
0x0000000000400b30 <+56>: mov %rax,%rdi
0x0000000000400b33 <+59>: callq 0x4008e0 <_ZNSolsEPFRSoS_E@plt>
0x0000000000400b38 <+64>: jmp 0x400b54 <Derived::Derived()+92>
//像是根据函数体执行结果,可能失败,就回滚前面创建的base
0x0000000000400b3a <+66>: mov %rax,%rbx
0x0000000000400b3d <+69>: mov -0x18(%rbp),%rax
0x0000000000400b41 <+73>: mov %rax,%rdi
0x0000000000400b44 <+76>: callq 0x400a5e <Base::~Base()>
0x0000000000400b49 <+81>: mov %rbx,%rax
0x0000000000400b4c <+84>: mov %rax,%rdi
0x0000000000400b4f <+87>: callq 0x400920 <_Unwind_Resume@plt>
0x0000000000400b54 <+92>: add $0x18,%rsp //恢复栈结构
0x0000000000400b58 <+96>: pop %rbx
0x0000000000400b59 <+97>: pop %rbp
0x0000000000400b5a <+98>: retq
Dump of assembler code for function Base::Base():
0x0000000000400a1e <+0>: push %rbp
0x0000000000400a1f <+1>: mov %rsp,%rbp
0x0000000000400a22 <+4>: sub $0x10,%rsp
0x0000000000400a26 <+8>: mov %rdi,-0x8(%rbp)
//传入的待构造变量地址
0x0000000000400a2a <+12>: mov -0x8(%rbp),%rax
0x0000000000400a2e <+16>: movq $0x400ed0,(%rax)
//虚表地址初始化
//首地址=this=虚表指针
0x0000000000400a35 <+23>: mov -0x8(%rbp),%rax
//初始化列表的代码
0x0000000000400a39 <+27>: movl $0x6666,0x8(%rax)
=> 0x0000000000400a40 <+34>: mov $0x400e30,%esi
//构造函数的函数体
0x0000000000400a45 <+39>: mov $0x6020e0,%edi
0x0000000000400a4a <+44>: callq 0x4008d0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x0000000000400a4f <+49>: mov $0x4008f0,%esi
0x0000000000400a54 <+54>: mov %rax,%rdi
0x0000000000400a57 <+57>: callq 0x4008e0 <_ZNSolsEPFRSoS_E@plt>
0x0000000000400a5c <+62>: leaveq
0x0000000000400a5d <+63>: retq
End of assembler dump.
析构函数
还原虚表指针:
看反汇编码,析构函数在保存bp,获取析构对象首地址后,就回写虚表
Dump of assembler code for function Derived::~Derived():
0x0000000000400b5c <+0>: push %rbp
0x0000000000400b5d <+1>: mov %rsp,%rbp
0x0000000000400b60 <+4>: push %rbx
0x0000000000400b61 <+5>: sub $0x18,%rsp
0x0000000000400b65 <+9>: mov %rdi,-0x18(%rbp)
0x0000000000400b69 <+13>: mov -0x18(%rbp),%rax
//成员对象为空
//设置虚表
0x0000000000400b6d <+17>: movq $0x400e90,(%rax)
//函数体
...........
//父类析构函数
0x0000000000400b90 <+52>: mov -0x18(%rbp),%rax
0x0000000000400b94 <+56>: mov %rax,%rdi
0x0000000000400b97 <+59>: callq 0x400a5e <Base::~Base()>
//析构子类数据
//会有类似语句:
//jmp 0x400bcd <Derived::~Derived()+113>
//<_ZdlPv@plt> 分析见下面
.........
0x0000000000400bcd <+113>: add $0x18,%rsp
0x0000000000400bd1 <+117>: pop %rbx
0x0000000000400bd2 <+118>: pop %rbp
0x0000000000400bd3 <+119>: retq
End of assembler dump.
Dump of assembler code for function Base::~Base():
0x0000000000400a5e <+0>: push %rbp
0x0000000000400a5f <+1>: mov %rsp,%rbp
0x0000000000400a62 <+4>: sub $0x10,%rsp
0x0000000000400a66 <+8>: mov %rdi,-0x8(%rbp)
0x0000000000400a6a <+12>: mov -0x8(%rbp),%rax
//成员对象为空
//设置虚表
0x0000000000400a6e <+16>: movq $0x400ed0,(%rax)
........函数体
0x0000000000400a91 <+51>: mov $0x0,%eax
0x0000000000400a96 <+56>: test %eax,%eax
0x0000000000400a98 <+58>: je 0x400aa6 <Base::~Base()+72>
0x0000000000400a9a <+60>: mov -0x8(%rbp),%rax
0x0000000000400a9e <+64>: mov %rax,%rdi
0x0000000000400aa1 <+67>: callq 0x400880 <_ZdlPv@plt> //见下面
0x0000000000400aa6 <+72>: leaveq
0x0000000000400aa7 <+73>: retq
End of assembler dump.
<_ZdlPv@plt>
_ZdlPv@plt
再看main反汇编析构部分
0x0000000000400cbd <+153>: mov -0x18(%rbp),%rax
0x0000000000400cc1 <+157>: mov (%rax),%rax
0x0000000000400cc4 <+160>: add $0x8,%rax //虚表偏移8
0x0000000000400cc8 <+164>: mov (%rax),%rax
0x0000000000400ccb <+167>: mov -0x18(%rbp),%rdx
0x0000000000400ccf <+171>: mov %rdx,%rdi
0x0000000000400cd2 <+174>: callq *%rax // delete pb;
0x0000000000400cd4 <+176>: mov $0x0,%ebx
0x0000000000400cd9 <+181>: lea -0x40(%rbp),%rax
0x0000000000400cdd <+185>: mov %rax,%rdi
0x0000000000400ce0 <+188>: callq 0x400b5c <Derived::~Derived()> //栈变量d析构
derive的析构函数有2个
1、0x400b5c Derived::~Derived() (偏移0字节)
2、虚表偏移8字节
函数2调用函数1,并调用
callq 0x400880 _ZdlPv@plt
函数1为:
(gdb) disassemble 0x00400b5c
Dump of assembler code for function Derived::~Derived():
..........保存调用栈.....
..........rdi中取得入参--隐藏的待析构对象地址.....
..........还原虚表.....
..........函数体......
0x0000000000400b90 <+52>: mov -0x18(%rbp),%rax
0x0000000000400b94 <+56>: mov %rax,%rdi
0x0000000000400b97 <+59>: callq 0x400a5e <Base::~Base()>
0x0000000000400b9c <+64>: mov $0x0,%eax
0x0000000000400ba1 <+69>: test %eax,%eax
0x0000000000400ba3 <+71>: je 0x400bcd <Derived::~Derived()+113>
0x0000000000400ba5 <+73>: mov -0x18(%rbp),%rax
0x0000000000400ba9 <+77>: mov %rax,%rdi
0x0000000000400bac <+80>: callq 0x400880 <_ZdlPv@plt>
0x0000000000400bb1 <+85>: jmp 0x400bcd <Derived::~Derived()+113>
0x0000000000400bb3 <+87>: mov %rax,%rbx
0x0000000000400bb6 <+90>: mov -0x18(%rbp),%rax
0x0000000000400bba <+94>: mov %rax,%rdi
0x0000000000400bbd <+97>: callq 0x400a5e <Base::~Base()>
0x0000000000400bc2 <+102>: mov %rbx,%rax
0x0000000000400bc5 <+105>: mov %rax,%rdi
0x0000000000400bc8 <+108>: callq 0x400920 <_Unwind_Resume@plt>
0x0000000000400bcd <+113>: add $0x18,%rsp
0x0000000000400bd1 <+117>: pop %rbx
0x0000000000400bd2 <+118>: pop %rbp
0x0000000000400bd3 <+119>: retq
End of assembler dump.
(待补充。。。。
mov $0x0,%eax
test %eax,%eax
je 0x400bcd Derived::~Derived()+113
这些命令,实际执行时会跳转
)
虚析构函数:
多态
指针对象通过虚表间接调用虚函数
普通对象直接调用自己定义的函数
Base b;
Derived d;
....
d.f();
.....
pb=&b;
pb->f();
0x0000000000400c5b <+55>: lea -0x40(%rbp),%rax
0x0000000000400c5f <+59>: mov %rax,%rdi
0x0000000000400c62 <+62>: callq 0x400bfa <Derived::f()> //d.f();
0x0000000000400c67 <+67>: lea -0x30(%rbp),%rax //pb=&b;
0x0000000000400c6b <+71>: mov %rax,-0x18(%rbp)
0x0000000000400c6f <+75>: mov -0x18(%rbp),%rax
0x0000000000400c73 <+79>: mov (%rax),%rax //取虚表
0x0000000000400c76 <+82>: add $0x10,%rax
//虚表偏移10
0x0000000000400c7a <+86>: mov (%rax),%rax
0x0000000000400c7d <+89>: mov -0x18(%rbp),%rdx
0x0000000000400c81 <+93>: mov %rdx,%rdi
0x0000000000400c84 <+96>: callq *%rax
//pb->f();