从内存角度看待对象-静态分析

1. 源码

debug

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <stdio.h>

class Base {
public:
	Base() {
		printf("Base()\n");
	}
	virtual ~Base() {
		printf("~Base()\n");
	}
	void setNumber(int n) {
		base = n;
	}
	int getNumber() {
		return base;
	}

private:
	int base;
};

class Derive : public Base {
public:
	Derive() {
		printf("Derive()\n");
	}

	virtual ~Derive() {
		printf("~Derive()\n");
	}

	void showNumber(int n) {
		setNumber(n);
		
		derive = n + 1;

		printf("%d\n", getNumber());
		printf("%d\n", derive);
	}

private:
	int derive;
};

int main() {
	int num;

	printf("please enter a value : ");
	scanf_s("%d", &num);

	Derive derive;
	derive.showNumber(num);

	return 0;
}

2. x86反汇编分析

首先来看main方法,创建局部对象Derive,首先调用Derive::Derive(void)构造函数

接着调用Base::Base(void)构造函数

可以看到Base构造函数,将this对象的前4字节设置为Base的虚表地址

Base对象的虚表就存放了一个它自己的虚析构函数

现在回到Derive构造函数中,发现在调用Base构造函数后,又将将this对象的前4字节设置为Derive的虚表地址

通过发现,Derive和Base的虚表都是只有4个字节的有效数据,都是存放它们各自的析构函数,说明这两个对象不存在其他虚函数。

再回到main中,继续往下分析Derive::showNumber(int)方法。

调用Derive::showNumber方法,首先传参num,再通过eax传递this指针

Derive::showNumber方法中,可以发现调用了Base::setNumber(int)Base::getNumber(void)这两个方法;通过观察可以发现成员Derive::derive,它在this对象的8~12字节空间存储

现在,分析Base::setNumber(int)方法,可以发现Base::base成员存放在this对象4~7字节空间

再次回到main中,调用完Derive::showNumber(int)方法后,调用Derive对象的析构函数Derive::~Derive(void);在Derive::~Derive(void)方法里会调用Base对象的析构函数Base::~Base(void)

Base::~Base(void)方法如下

3. 总结

可以发现Derive对象占用12字节空间,创建的Derive对象内部布局如下:

1
2
3
4
5
struct Derive {
	dword : vftable
	dword : Base::base
	dword : Derive::derive
}

Derive对象的构造方法的执行顺序:Base::Base(void) => Derive::Derive(void)

与之相反的是它的析构方法的执行顺序:Derive::~Derive(void) => Base::~Base(void)

0%