《C++反汇编与逆向分析技术揭秘》笔记-第12章从内存角度看继承和多重继承

若没有另外说明,都是运行在VS2022,x86,Debug下。

12.1识别类和类之间的关系
1.例子如下。分析:构造顺序:先构造父类,然后按声明顺序构造自身的数据成员,最后才是自身的构造代码。所以,
(1)当父类存在构造函数时,但子类没有构造函数,编译器会提供默认的构造函数,以实现成员构造函数的调用(析构函数同理)。
(2)当子类存在构造函数时,而父类没有构造函数,编译器不会为父类提供默认的构造函数。(析构函数同理)。
(3)析构顺序和构造函数相反,即先析构子类,再析构父类。
(4)指向子类对象的指针,可以操作子类对象外,还能操作父类对象。
(5)【注意】指向父类对象的子类指针,如果访问的成员数据是父类对象定义的,则不会出错;如果访问的是子类派生的成员数据,则会造成访问越界。

反汇编如下。

Derived类构造函数如下。

2.例子如下。分析:
(1)执行过程:【子类构造函数->【父类构造函数->【设置虚表指针为父类虚表->直接调用父类的showSpeak()】->设置虚表指针为子类虚表】->子类析构函数->【设置虚表指针为子类虚表->父类析构函数->【设置虚表指针为父类虚表->直接调用父类的showSpeak()】】】
(2)观察执行过程,子类构造函数执行时,虚表指针已完成初始化,是子类的虚表,在析构函数执行时,为什么编译器还要再次将虚表指针设为子类虚表?因为子类有可能会被其他类继承,若被继承,子类成了父类,当前对象的析构函数开始执行时,其虚表也是当前对象的,所以执行到父类的析构函数时,虚表必须改写为父类的虚表。

Derived类构造函数如下。

Base类构造函数如下。

 Derived类析构函数如下。

 
12.2多重继承
1.例子如下,分析:
(1)数据成员的排列顺序由继承父类的顺序决定。即先构造父类Sofa,再构造父类Bed。
(2)在多重继承中,子类虚表指针的个数取决于继承的父类个数(虚基类除外)。这些虚表指针在将子类对象转换成父类指针时使用,每个虚表指针对应一个父类。

反汇编如下。

SofaBed类构造函数如下。

SofaBed类析构函数如下。

12.3抽象类
1.纯虚函数是一个没有实现只有声明的函数,它的存在就是为了让类具有抽象类的功能,让继承自抽象类的子类都具有虚表以及虚表指针。
2.例子如下,分析:抽象类Base的虚表中,因为纯虚函数没有实现代码,所以没有首地址。编译器为了防止误调用纯虚函数,将虚表中保存的纯虚函数的首地址项替换成函数__purecall,用于结束程序。

反汇编如下。

12.4虚继承
1.为了解决多继承时的命名冲突和冗余数据的问题,提出了虚继承,使得在派生类中只保留一份间接基类的成员。
2.例子如下,分析:
(1)使用虚继承可以避免共同派生出的子类产生多义性错误。
(2)虚基类偏移表保存的是偏移数据,第一项为-4,即虚基类偏移表所属类对应的对象首地址相对于虚基类偏移表的偏移值;第二项保存的是虚基类对象首地址相对于虚基类偏移表的偏移值。
(3)当子类存在多个虚基类时,会在虚基类偏移表中依次记录它们的偏移量。

#include<iostream>
using namespace std;


class Furniture
{
protected:
	int price;
public:
	Furniture():price(0){}
	virtual ~Furniture() { printf("destruct furniture\n"); }

	virtual int getPrice() { return price; }
};

class Sofa: virtual public Furniture
{
protected:
	int color;
public:
	Sofa() :color(2) { price = 1; }
	virtual ~Sofa() { printf("destruct sofa\n"); }

	virtual int  getColor() { return color; }
	virtual void sitDown() { printf("sit down on the sofa\n"); }
};

class Bed: virtual public Furniture
{
protected:
	int length;
	int width;
public:
	Bed() :length(4), width(5) { price = 3; }
	virtual ~Bed() { printf("destruct bed\n");}

	virtual int getArea() { return length * width; }
	virtual void sleep() { printf("sleep on the bed\n"); }
};

class SofaBed :public Sofa,public Bed
{
protected:
	int height;
public:
	SofaBed():height(6){}
	virtual ~SofaBed(){ printf("destruct sofabed\n"); }
	virtual int getHeight() { return height; }

	virtual void sitDown() { printf("sit down on the sofabad\n"); }
	virtual void sleep() { printf("sleep on the sofabed\n"); }
};


int main()
{
	SofaBed sofabed;
	Furniture* furniture = &sofabed;
	Sofa* sofa = &sofabed;
	Bed* bed = &sofabed;

	return 0;
}

SofaBed内存结构如下。

反汇编如下。


SofaBed类构造函数如下。


SofaBed类析构代理函数如下。


SofaBed类析构函数如下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值