《coredump问题原理探究》Linux x86版6.6节单继承
来源:互联网 发布:年底做网络推广的好处 编辑:程序博客网 时间:2024/05/16 07:35
在C++里,类的多态是通过继承由虚函数来体现的。那么在单继承中,类的成员变量和虚函数的分布又是怎样的呢?
看一下例子:
1 #include <stdio.h> 2 class xuzhina_dump_c06_s4_base 3 { 4 private: 5 int m_a; 6 public: 7 xuzhina_dump_c06_s4_base() { m_a = 1; } 8 virtual void inc() 9 { 10 m_a++; 11 } 12 virtual void print() 13 { 14 printf( "m_a:%d\n", m_a ); 15 } 16 }; 17 18 class xuzhina_dump_c06_s4_derived: public xuzhina_dump_c06_s4_base 19 { 20 private: 21 int m_b; 22 int m_c; 23 public: 24 xuzhina_dump_c06_s4_derived() 25 { 26 m_b = 0; 27 m_c = 2*m_b; 28 } 29 virtual void mul() 30 { 31 m_c *= m_b; 32 } 33 34 virtual void print() 35 { 36 printf( "m_b:%d, m_c:%d\n", m_b, m_c ); 37 } 38 virtual void dec() 39 { 40 m_c -= m_b; 41 } 4243 virtual void inc() 44 { 45 m_b++; 46 m_c += m_b; 47 } 48 }; 49 50 int main() 51 { 52 xuzhina_dump_c06_s4_base* p = new xuzhina_dump_c06_s4_derived; 53 if ( p != NULL ) 54 { 55 p->inc(); 56 p->print(); 57 } 58 59 return 0; 60 }
看一下汇编:
(gdb) disassemble mainDump of assembler code for function main: 0x080485b0 <+0>: push %ebp 0x080485b1 <+1>: mov %esp,%ebp 0x080485b3 <+3>: push %ebx 0x080485b4 <+4>: and $0xfffffff0,%esp 0x080485b7 <+7>: sub $0x20,%esp 0x080485ba <+10>: movl $0x10,(%esp) 0x080485c1 <+17>: call 0x80484a0 <_Znwj@plt> 0x080485c6 <+22>: mov %eax,%ebx 0x080485c8 <+24>: mov %ebx,(%esp) 0x080485cb <+27>: call 0x8048654 <_ZN27xuzhina_dump_c06_s4_derivedC2Ev> 0x080485d0 <+32>: mov %ebx,0x1c(%esp) 0x080485d4 <+36>: cmpl $0x0,0x1c(%esp) 0x080485d9 <+41>: je 0x8048600 <main+80> 0x080485db <+43>: mov 0x1c(%esp),%eax 0x080485df <+47>: mov (%eax),%eax 0x080485e1 <+49>: mov (%eax),%eax 0x080485e3 <+51>: mov 0x1c(%esp),%edx 0x080485e7 <+55>: mov %edx,(%esp) 0x080485ea <+58>: call *%eax 0x080485ec <+60>: mov 0x1c(%esp),%eax 0x080485f0 <+64>: mov (%eax),%eax 0x080485f2 <+66>: add $0x4,%eax 0x080485f5 <+69>: mov (%eax),%eax 0x080485f7 <+71>: mov 0x1c(%esp),%edx 0x080485fb <+75>: mov %edx,(%esp) 0x080485fe <+78>: call *%eax 0x08048600 <+80>: mov $0x0,%eax 0x08048605 <+85>: mov -0x4(%ebp),%ebx 0x08048608 <+88>: leave 0x08048609 <+89>: ret End of assembler dump.
由上一节可知,要看虚函数表和成员变量的内存布局,直接看构造函数的汇编就行了。
(gdb) disassemble _ZN27xuzhina_dump_c06_s4_derivedC2EvDump of assembler code for function _ZN27xuzhina_dump_c06_s4_derivedC2Ev: 0x08048654 <+0>: push %ebp 0x08048655 <+1>: mov %esp,%ebp 0x08048657 <+3>: sub $0x18,%esp 0x0804865a <+6>: mov 0x8(%ebp),%eax 0x0804865d <+9>: mov %eax,(%esp) 0x08048660 <+12>: call 0x804860a <_ZN24xuzhina_dump_c06_s4_baseC2Ev> 0x08048665 <+17>: mov 0x8(%ebp),%eax 0x08048668 <+20>: movl $0x80487c8,(%eax) 0x0804866e <+26>: mov 0x8(%ebp),%eax 0x08048671 <+29>: movl $0x0,0x8(%eax) 0x08048678 <+36>: mov 0x8(%ebp),%eax 0x0804867b <+39>: mov 0x8(%eax),%eax 0x0804867e <+42>: lea (%eax,%eax,1),%edx 0x08048681 <+45>: mov 0x8(%ebp),%eax 0x08048684 <+48>: mov %edx,0xc(%eax) 0x08048687 <+51>: leave 0x08048688 <+52>: ret End of assembler dump.
可见,类xuzhina_dump_c06_s4_derived的构造函数,先调用基类xuzhina_dump_c06_s4_base构造函数(即_ZN24xuzhina_dump_c06_s4_baseC2Ev),再设置虚函数表,然后初始化自己变量。在这里,有两个疑问:
1. 类xuzhina_dump_c06_s4_derived的成员变量是从this+8的位置开始的,而不是从this+4的位置开始.那么this+4放着什么东西?
2. 虚函数表指针0x80487c8放着的是类xuzhina_dump_c06_s4_derived的虚函数表,还是基类xuzhina_dump_c06_s4_base的虚函数表?
在回答这两个问题之前,先看一下基类xuzhina_dump_c06_s4_base的构造函数:
(gdb) disassemble _ZN24xuzhina_dump_c06_s4_baseC2EvDump of assembler code for function _ZN24xuzhina_dump_c06_s4_baseC2Ev: 0x0804860a <+0>: push %ebp 0x0804860b <+1>: mov %esp,%ebp 0x0804860d <+3>: mov 0x8(%ebp),%eax 0x08048610 <+6>: movl $0x80487e0,(%eax) 0x08048616 <+12>: mov 0x8(%ebp),%eax 0x08048619 <+15>: movl $0x1,0x4(%eax) 0x08048620 <+22>: pop %ebp 0x08048621 <+23>: ret End of assembler dump.
从基类的构造函数可知,上面两个疑问的答案如下:
1. this+4的地方是存放着m_a,初始化为1.
2. 地址0x80487e0放着的则是基类的虚函数表。而地址0x80487c8放着的是子类的虚函数表。
看一下这两个虚函数表指针的内容。
基类虚函数表指针:
(gdb) x /4wx 0x80487e00x80487e0 <_ZTV24xuzhina_dump_c06_s4_base+8>: 0x08048622 0x08048636 0x75783732 0x6e69687a(gdb) info symbol 0x08048622xuzhina_dump_c06_s4_base::inc() in section .text(gdb) info symbol 0x08048636xuzhina_dump_c06_s4_base::print() in section .text
子类虚函数表指针:
(gdb) x /8wx 0x80487c80x80487c8 <_ZTV27xuzhina_dump_c06_s4_derived+8>: 0x080486e6 0x080486a4 0x0804868a 0x080486cc0x80487d8 <_ZTV24xuzhina_dump_c06_s4_base>: 0x00000000 0x08048830 0x08048622 0x08048636(gdb) info symbol 0x080486e6xuzhina_dump_c06_s4_derived::inc() in section .text(gdb) info symbol 0x080486a4xuzhina_dump_c06_s4_derived::print() in section .text(gdb) info symbol 0x0804868axuzhina_dump_c06_s4_derived::mul() in section .text(gdb) info symbol 0x080486ccxuzhina_dump_c06_s4_derived::dec() in section .text
也就是说子类xuzhina_dump_c06_s4_derived的对象构造经历了下面的步骤
由上面可以看到子类虚函数表的分布规律:
1.重载基类的虚函数,按照基类虚函数的声明顺序排列,和子类声明顺序无关。
2.子类独有的虚函数,按照虚函数的声明顺序排列,追加在重载虚函数后面。
- 《coredump问题原理探究》Linux x86版6.6节单继承
- 《coredump问题原理探究》Linux x86版6.8节多继承coredump例子
- 《coredump问题原理探究》Linux x86版6.7节多继承
- 《coredump问题原理探究》windows版6.4节单继承
- 《coredump问题原理探究》Linux x86版3.8节栈布局之栈溢出coredump例子
- 《coredump问题原理探究》Linux x86版4.5节函数的逆向之coredump例子
- 《coredump问题原理探究》Linux x86版6.3节有成员变量的类coredump例子
- 《coredump问题原理探究》Linux x86版6.5节虚函数的coredump例子
- 《coredump问题原理探究》Linux x86版7.2节vector coredump例子
- 《coredump问题原理探究》Linux x86版7.4节List coredump例子
- 《coredump问题原理探究》Linux x86版7.6节 Map coredump例子
- 《coredump问题原理探究》Linux x86版第一章前言
- 《coredump问题原理探究》Linux x86版3.1节栈布局之概述
- 《coredump问题原理探究》Linux x86版3.2节栈布局之函数桢
- 《coredump问题原理探究》Linux x86版3.3节栈布局之局部变量
- 《coredump问题原理探究》Linux x86版3.4节栈布局之函数参数
- 《coredump问题原理探究》Linux x86版3.4节栈布局之函数参数 .
- 《coredump问题原理探究》Linux x86版3.6节栈布局之gcc内嵌关键字
- Should you teach Python or R for data science?
- android service与activity交互的方试
- 分布式与集群的区别
- Tomcat下热部署
- 数学篇----参数估计之最大似然估计法[概率论]
- 《coredump问题原理探究》Linux x86版6.6节单继承
- 15.02.04,C数据类型笔记
- 背包问题——“01背包”最优方案总数的求解
- Error: EMFILE, too many open files 'xxx.ejs'
- cocoapods 找不到头文件
- Play on Words——字母首尾相连
- ArrayList
- 常用CSS样式总结
- 本文整理了四种在MySQL中修改root密码的方法