继承

struct Person {

int age;


int sex;

};
 
struct Teacher {

int age;


int sex;


int level;


int classId;

};

如上代码中可以看见,Teacher类与Person类都存在着相同的2个成员age和sex,那么这就相当于重复编写了,我们可以通过继承的方式避免这样重复的编写(当前类名称:要继承的类名称):

struct Teacher:Person {

int level;


int classId;

};

创建对象的对应反汇编代码如下,可以清晰的看见与我们正常的内存布局是一样的:

C++逆向分析——继承_父类

那么继承是什么?这就很好理解了,继承的本质就是数据复制,子类(派生类)继承(复制)父类(基类)的数据,在这里Person父类(基类),Teacher为子类(派生类);继承可以减少重复代码的编写。

假设,子类中存在一个与父类中相同的成员会如何?

struct Person {

int age;


int sex;

};
 

struct Teacher:Person { // Inherit


int age;


int classId;

};

我们可以创建一个对象来看一下对应的宽度和反汇编代码:

void main() {

 Teacher t;

 t.age = 30;


 t.sex = 1;


 t.classId = 20;

 

 printf("%d", sizeof(t));


 printf("%d", sizeof(t));


return;

}

首先看下数据宽度,我们会发现是16,那也就是说这里不管如何你只要继承了,在编译器中两个成员还是会直接添加过来,Teacher的成员依然是4个「4成员*4数据宽度(int类型) = 16」

C++逆向分析——继承_父类_02

再来看下反汇编代码:

C++逆向分析——继承_内存布局_03

之前我们已经看过了正常的内存布局了,在这里,很明显,少了一个0x10位置的成员,那么按照内存布局应该是这样的:

0x10 → Person.age

0x0C → Person.sex

0x08 → Teacher.age

0x04 → Teacher.classId

而在这里创建对象编译器使用的age成员默认就是当前类Teacher的成员;想要使用父类中的成员可以使用这种方式(对象名.父类名称::成员名称):

void main() {

 Teacher t;

 t.Person::age = 30; // Father


 t.age = 30;


 t.sex = 1;


 t.classId = 20;

 

return;

}

子类与父类成员重名的问题我们可以通过这种方式解决,但是在实际应用中还是尽量避免这种问题比较好。

 

我自己实践下内存布局:

#include <stdio.h>
struct Person {
	int age;
	int sex;
};

struct Teacher :Person { // Inherit
	int age2;
	int classId;
};

 
void main() {
	Teacher t;
	t.age = 30;//ebp-24
	t.age2 = 99;//ebp-16
	t.sex = 1;//ebp-20
	t.classId = 20;//ebp-12

	printf("%d", sizeof(t));
	printf("%d", sizeof(t));
	return;
}

 ODB里看到的:

C++逆向分析——继承_数据_04

vs 2022里反汇编,可以看到父类age的变量地址对应的是[t]这个基址。==》看后面那个图!

C++逆向分析——继承_内存布局_05

 

 

 

我们可以多次继承么,或者说继承仅仅局限于子、父关系么?如下代码,B继承了A,C继承了B,C是否只继承了B的v和n?

struct A {

int x;


int y;

};
 
struct B:A {

int v;


int n;

};
 
struct C:B {

int p;


int o;

};

我们可以来打印一下C的数据宽度:

C++逆向分析——继承_内存布局_06

结果是24,那么就说明C不仅仅继承了B,还继承了A;再换个说法就是,继承的本质是数据的复制,那也就是说当复制完(继承)后才是其本身,B的本身就是4个成员x、y、v、n。==》看这个图就完全明白了!!!

C++逆向分析——继承_数据_07

除了这种方式以外,我们想实现同样的效果可以使用多重继承(当前类:继承的类A, 继承的类B):

struct A {

int x;


int y;

};
 
struct B {

int v;


int n;

};
 

struct C:A,B { // Multiple


int p;


int o;

};

但这种方式在很多面向对象语言中是不允许时间的,在C++中是可以使用的,其内存分布也与第一种方式不一样:

C++逆向分析——继承_内存布局_08

最后:继承的类A和继承的类B的顺序,谁在前,谁就在内存分布中的前面;不推荐使用多重继承,这会增加程序的复杂度