玩命加载中 . . .

对象布局


#include <iostream>
using namespace std;

class Point2D {
public:
    Point2D(int _x, int _y): x(_x), y(_y) {}
    virtual void print() const {
        cout << x << "," << y << endl;
    }
    virtual ~Point2D() {}
protected:
    int x, y;
};

class Point3D: public Point2D {
public:
    Point3D(int _x, int _y, int _z): Point2D(_x, _y), z(_z) {}
    void print() const {
        cout << x << "," << y << "," << z << endl;
    }
private:
    int z;
};

int main() {
    Point2D *p2d = new Point3D(1, 2, 3);
    p2d->print();
    Point2D *p2d2 = new Point2D(1, 2);
    p2d2->print();
    return 0;
}

基类Point2D中有虚函数print和需析构函数

基类指针p2d指向了派生类对象,用gdb调试

(gdb) print p2d
$1 = (Point2D *) 0x555555768e70
(gdb) x/20x 0x555555768e70
0x555555768e70:	0x55755d10	0x00005555	0x00000001	0x00000002
0x555555768e80:	0x00000003	0x00000000	0x0000f181	0x00000000

查看基类指针,前8个字节是虚表指针,然后是4字节的x,4字节的y,4字节的z

查看虚函数表指针

(gdb) x/20x 0x555555755d10
0x555555755d10 <_ZTV7Point3D+16>:	0x55554d88	0x00005555	0x55554e08	0x00005555
0x555555755d20 <_ZTV7Point3D+32>:	0x55554e32	0x00005555	0x00000000	0x00000000

对应3个虚函数地址,分别查看对应哪个虚函数

第一个是Point3Dprint虚函数

(gdb) x/20x 0x555555554d88
0x555555554d88 <Point3D::print() const>:	0xe5894855	0x10ec8348	0xf87d8948	0xf8458b48
0x555555554d98 <Point3D::print() const+16>:	0x8908408b	0x3d8d48c6	0x0020127c	0xfffcb7e8
0x555555554da8 <Point3D::print() const+32>:	0x358d48ff	0x00000135	0xe8c78948	0xfffffc58
0x555555554db8 <Point3D::print() const+48>:	0x48c28948	0x8bf8458b	0xc6890c40	0xe8d78948
0x555555554dc8 <Point3D::print() const+64>:	0xfffffc94	0x12358d48	0x48000001	0x35e8c789

第二个是Point3D的虚析构函数

(gdb) x/20x 0x555555554e08
0x555555554e08 <Point3D::~Point3D()>:	    0xe5894855	0x10ec8348	0xf87d8948	0xf5158d48
0x555555554e18 <Point3D::~Point3D()+16>:	0x4800200e	0x48f8458b	0x8b481089	0x8948f845
0x555555554e28 <Point3D::~Point3D()+32>:	0xfed0e8c7	0xc990ffff	0x485590c3	0x8348e589

第三个是合成的Point3D的析构函数

(gdb) x/20x 0x555555554e32
0x555555554e32 <Point3D::~Point3D()>:	    0xe5894855	0x10ec8348	0xf87d8948	0xf8458b48
0x555555554e42 <Point3D::~Point3D()+16>:	0xe8c78948	0xffffffbe	0xf8458b48	0x000018be
0x555555554e52 <Point3D::~Point3D()+32>:	0xc7894800	0xfffbd5e8	0x0fc3c9ff	0x5741001f

可以反汇编看看这个析构函数

(gdb) disassemble 0x555555554e32
Dump of assembler code for function Point3D::~Point3D():
   0x0000555555554e32 <+0>:		push   %rbp
   0x0000555555554e33 <+1>:		mov    %rsp,%rbp
   0x0000555555554e36 <+4>:		sub    $0x10,%rsp
   0x0000555555554e3a <+8>:		mov    %rdi,-0x8(%rbp)
   0x0000555555554e3e <+12>:	mov    -0x8(%rbp),%rax
   0x0000555555554e42 <+16>:	mov    %rax,%rdi
   0x0000555555554e45 <+19>:	callq  0x555555554e08 <Point3D::~Point3D()>
   0x0000555555554e4a <+24>:	mov    -0x8(%rbp),%rax
   0x0000555555554e4e <+28>:	mov    $0x18,%esi
   0x0000555555554e53 <+33>:	mov    %rax,%rdi
   0x0000555555554e56 <+36>:	callq  0x555555554a30 <_ZdlPvm@plt>
   0x0000555555554e5b <+41>:	leaveq 
   0x0000555555554e5c <+42>:	retq   
End of assembler dump.

可以看到调用了地址为0x555555554e08的第二个析构函数

再看看基类指针p2d2,指向了Point2D对象

(gdb) n
1,2,3
28	    Point2D *p2d2 = new Point2D(1, 2);
(gdb) n
29	    p2d2->print();
(gdb) print p2d2
$2 = (Point2D *) 0x5555557692a0
(gdb) x/20x 0x5555557692a0
0x5555557692a0:	0x55755d38	0x00005555	0x00000001	0x00000002
0x5555557692b0:	0x00000000	0x00000000	0x0000ed51	0x00000000

前面8个字节是虚函数表指针,然后是4字节的x和4字节的y

查看虚函数表指针

(gdb) x/20x 0x555555755d38
0x555555755d38 <_ZTV7Point2D+16>:	0x55554ca2	0x00005555	0x55554cfe	0x00005555
0x555555755d48 <_ZTV7Point2D+32>:	0x55554d18	0x00005555	0xf7dc5438	0x00007fff

同样是3个指针

第一个是print虚函数

(gdb) x/20x 0x555555554ca2
0x555555554ca2 <Point2D::print() const>:	0xe5894855	0x10ec8348	0xf87d8948	0xf8458b48
0x555555554cb2 <Point2D::print() const+16>:	0x8908408b	0x3d8d48c6	0x00201362	0xfffd9de8
0x555555554cc2 <Point2D::print() const+32>:	0x358d48ff	0x0000021b	0xe8c78948	0xfffffd3e
0x555555554cd2 <Point2D::print() const+48>:	0x48c28948	0x8bf8458b	0xc6890c40	0xe8d78948
0x555555554ce2 <Point2D::print() const+64>:	0xfffffd7a	0x48c28948	0x12e0058b	0x89480020

第二个是Point2D的虚析构函数

(gdb) x/20x 0x555555554cfe
0x555555554cfe <Point2D::~Point2D()>:	    0xe5894855	0xf87d8948	0x2b158d48	0x48002010
0x555555554d0e <Point2D::~Point2D()+16>:	0x48f8458b	0x5d901089	0x485590c3	0x8348e589

第三个也是Point2D的虚析构函数

(gdb) x/20x 0x555555554d18
0x555555554d18 <Point2D::~Point2D()>:	    0xe5894855	0x10ec8348	0xf87d8948	0xf8458b48
0x555555554d28 <Point2D::~Point2D()+16>:	0xe8c78948	0xffffffce	0xf8458b48	0x000010be
0x555555554d38 <Point2D::~Point2D()+32>:	0xc7894800	0xfffcefe8	0x90c3c9ff	0xe5894855

反汇编看看

(gdb) disassemble 0x555555554d18
Dump of assembler code for function Point2D::~Point2D():
   0x0000555555554d18 <+0>:		push   %rbp
   0x0000555555554d19 <+1>:		mov    %rsp,%rbp
   0x0000555555554d1c <+4>:		sub    $0x10,%rsp
   0x0000555555554d20 <+8>:	    mov    %rdi,-0x8(%rbp)
   0x0000555555554d24 <+12>:	mov    -0x8(%rbp),%rax
   0x0000555555554d28 <+16>:	mov    %rax,%rdi
   0x0000555555554d2b <+19>:	callq  0x555555554cfe <Point2D::~Point2D()>
   0x0000555555554d30 <+24>:	mov    -0x8(%rbp),%rax
   0x0000555555554d34 <+28>:	mov    $0x10,%esi
   0x0000555555554d39 <+33>:	mov    %rax,%rdi
   0x0000555555554d3c <+36>:	callq  0x555555554a30 <_ZdlPvm@plt>
   0x0000555555554d41 <+41>:	leaveq 
   0x0000555555554d42 <+42>:	retq   
End of assembler dump.

也是调用了0x555555554cfe的第二个析构函数


文章作者: kunpeng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 kunpeng !
  目录