讲解目录:

   1.各类在内存中的表现形式   备注: 主要复习开发知识,和反汇编没有关系,但是是理解反汇编的前提.

      2.子类继承父类

     2.1 子类中有虚函数,父类中有虚函数    : 都有的情况下

       2.2 子类中没有虚函数,父类中有虚函数   : 子类没有,父类有的情况 2.1 2.2的情况都是一样的.

       2.3   子类中有虚函数,父类中没有虚函数  : 子有父没有的的情况下

       2.4 子类父类都没有虚函数的情况下

    第二专题大总结.    熟悉反汇编可以直接看这个总结,

   3.结合第二专题的成员对象有无虚表行为

     3.1成员对象有虚表的情况

     3.2成员对象没有虚表的情况

    第三专题大总结

    4.重载运算符的识别

   5.纯虚函数的反汇编  

   6.模版识别.

一丶各类在内存中的表现形式(复习开发知识)

讲解之前,我们首先要明白C/C++中的类的内存结构.继承之后的内存结构

普通类的内存结构:

class MyTest
{
public:
MyTest();
~MyTest();
public:
int m_int;
}; MyTest::MyTest(){} MyTest::~MyTest(){} int main(int argc, char* argv[])
{
MyTest test; //定义对象
return ;
}

对应内存结构图

  高级代码:

这是普通的一个类的内存结构图,因为我们只有一个成员,大小是一个4字节的,所以初始化为CC

总结: 普通类根据成员进行申请内存.

带有虚关键字的类(可能有虚函数或者虚构造)

PS: 类声明同上,但是析构前边加上了virtual 关键字,变为了虚析构

内存结构图:

可以看出,申请了八个字节,启动前4个字节是虚表指针,指向了虚表

后四个字节才是真正的为成员申请的内存.

总结: 带有虚函数(虚关键字)的时候,内存中会把前4个字节当做虚表指针,并且在构造的时候初始化.

子类继承父类,(都有虚函数的情况下)重要:

高级代码:

class MyFather
{
public:
MyFather();
virtual ~MyFather();
public:
int m_int;
}; MyFather::MyFather(){} MyFather::~MyFather(){} class MyChild : public MyFather //继承
{
public:
MyChild();
virtual ~MyChild();
  float m_flt; }; MyChild::MyChild(){} MyChild::~MyChild(){} int main(int argc, char* argv[])
{
MyChild test; //定义对象
return ;
}

内存结构图

总共申请了12个字节,前4个字节是虚表指针,后4个字节是父类的m_int成员,在后面才是子类的真正的成员.

说到这里我们就要说下复写虚表指针的操作.

首先我们知道:  子类构造的时候,会先构造父类,也就是说,父类的内存会先申请,并且把虚表指针填写到前4个字节位置,  而构造完毕父类之后,构造自己的时候,这时候虚表指针又写入子类的虚表指针了.产生了覆盖了.

流程图:

看上面图可以知道,我们子类继承父类,并且填写了虚表指针为子类的,此时 则可以写成  父类指针指向子类   例如:  Myfather *pFa = new MyChild;  pfa指向的位置就是父类区域的起始位置,

而且不会超过父类区域,所以是安全的,此时因为构造完毕,虚表指针是子类的,所以调用虚函数的时候,则是调用子类的虚函数了.

而且也说明了 为什么子类指针不能指向父类.这样会产生越界问题.

总结:

  子类继承父类时候,有虚函数的时候,会先把头4字节申请出来填写为虚表指针, 而且会产生复写(重复写入). 第一次, 构造父类,填写为父类指针,第二次构造完父类则会填写为子类的虚表指针.

  

二丶子类继承父类反汇编中的结构

2.1 子类中有虚函数,父类中有虚函数    : 都有的情况下

高级代码:

class MyFather
{
public:
MyFather();
virtual ~MyFather();
public:
int m_int;
}; MyFather::MyFather(){} MyFather::~MyFather(){} class MyChild : public MyFather
{
public:
MyChild();
virtual ~MyChild();
float m_flt;
}; MyChild::MyChild(){} MyChild::~MyChild(){} int main(int argc, char* argv[])
{
MyChild test; //定义对象
return ;
}

Debug下的反汇编

PS: 代码太多,只说明这个反汇编在哪个函数中

1.main函数中找到构造

2.构造中生成的反汇编

可以看出,构造中又有一个Call,这个Call是构造父类的,构造完毕之后填写自己的虚表指针.

3.父类构造

父类构造填写虚表指针,也就是对象的前4个字节修改为父类的虚表指针.而后通过第二步,得出,当构造完父类之后,其前4个字节会被子类重新写入.也就产生了复写过程

总结

  1.子类构造的时候会先构造父类,父类构造中先填写虚表指针.

  2.父类构造完成之后,子类会重新写入虚表指针.

  3..子类继承父类,都有虚函数的情况下,会产生复写行为, 对象首地址4个字节处填写虚表.

 2.2 子类中没有虚函数,父类中有虚函数 : 子类没有,父类有的情况

PS: 高级代码中,子类类声明去掉了虚函数

Debug下的反汇编代码:

 1.main函数下构造的反汇编

 2.构造内部反汇编

看到这一步我们明白了,首先构造父类,因为父类有虚函数,所以肯定会有虚表指针填写,而下方也填写了一次虚表指针.由此得出

父类有虚函数,子类没有虚函数则子类也会有虚表.也会产生复写行为.

总结:

  父有,子没有,子类也会有虚表,而且也会产生虚表指针复写行为.

  且只要父类有虚函数,不管子类有没有虚函数,子类都会产生虚表,且会复写虚表指针.

2.3 子类有虚函数,父类没有虚函数

高级代码子类中定义了虚函数,父类则把虚函数去掉了.

Debug下的反汇编代码

  1.main函数下构造

  2.构造内部

看其内部得出,父类没有虚函数的情况下,其对象 +4位置,跳过前边的4个字节,来构造父类,构造完毕之后填写子类虚表指针.

  3.父类构造内部

父类构造内部没有产生虚表指针填写行为

总结:

  子类有虚表,父类没有,则会跳过虚表指针的位置来构造父类,当构造完毕父类之后前4个字节填写子类的虚表指针.

2.4 子类,父类都没有虚函数的情况下

直接构造内存,没有虚表,也不会产生虚表指针复写,可以当做结构体还原.

第二专题大总结

    1.父类有虚函数,子类不管有没有虚函数,都会有虚表

    2.父类有虚函数构造的时候会填写虚表指针,且子类也会填写虚表指针,两者会产生虚表指针复写行为

    3.子类中有虚函数,父类没有,则会跳过虚表指针来构造父类,其子类会在构造完毕父类之后填写虚表指针,不会产生虚表指针复写行为.

三丶结合第二专题的成员对象有无虚表行为

3.1成员对象没有虚表的情况下

高级代码: 

class MyMemberObj           //成员对象
{
public:
MyMemberObj(){}
~MyMemberObj(){}
}; class MyFather //父类
{
public:
MyFather();
~MyFather();
public:
int m_int;
}; MyFather::MyFather(){} MyFather::~MyFather(){} class MyChild : public MyFather //子类继承父类
{
public:
MyChild();
virtual ~MyChild();
MyMemberObj m_memberobj; //成员对象
float m_flt;
}; MyChild::MyChild(){} MyChild::~MyChild(){} int main(int argc, char* argv[])
{
MyChild test; //定义对象
return ;
}

Debug下的反汇编

1.main函数下的构造

2.构造内部

1.构造父类,因为父类没有虚函数,所以+4构造一下,且父类有一个成员,所以申请了4个字节空间

2.成员变量的构造+8的位置开始构造,父类构造完毕之后构造,且此时成员对象没有虚函数.

3.子类在自己的头4个字节位置处填写虚表指针.

3.成员对象构造内部

成员对象内部不会产生写虚表的行为.

总结:

  成员对象没有虚函数的情况下,会在合适偏移位置处进行构造,注意合适位置处的用语,如果你是子类的成员对象,肯定会先构造父类,父类成员很多,则你的偏移位置则不固定.

3.2成员对象有虚表的情况下.

Debug下的汇编代码:

  因为其类之加了一个虚关键字,析构变为了虚析构,产生了虚表的动作.所以其汇编代码1,2步没有改变,同上.

  不同的是构造的时候,成员对象有了虚函数,构造的时候则会填写虚表.

总结:

  1.有成员对象的时候其成员对象内部没有虚表产生,则会在合适位置构造成员对象.

  2.有成员对象的时候,其成员对象内部有虚表产生,则在合适位置填写虚表指针,并且构造成员对象.

四丶反汇编中重载运算符的识别

在说重载运算符的时候,我们首先熟悉一下运算符重载的高级代码:

简单的运算符重载

函数类型 operator 运算符名称 (形参表列)
    {
        // 对运算符的重载处理
    }

高深一点的可以参考博客,这里不再重复讲解.复习开发知识可以参考博客链接 http://c.biancheng.net/cpp/biancheng/view/215.html

高级代码:

int operator+(MyChild& a,MyFather& b)
{
return (int)a.m_flt + b.m_int;
}
int main(int argc, char* argv[])
{
MyChild a; //定义对象
MyFather b;
cout << a + b << endl;
return ;
}

在反汇编中,其实运算符重载就是调用函数.只不过换了一种函数的认知方式.

其实不难.当做函数还原就好.

说道这里,我们可以说下运算符重载的额外认知.

比如我们熟悉的

1.数学中的中缀式   a + b / c - d * e 这种表达式就是中缀表达式

2.波兰式     -+a/bc*de  中缀转化为了波兰式,我们学习数据结构的树的时候就学习过这种方式,这个是编译原理中的.适用于计算机的识别.

怎么转换的

Sub(add(a,Div(b,c),Imul(d,e); 转为汇编代码,比如a + b /c 我们则写成  add(a,div(b,c),然后转为汇编表达式即可.最终的结果则是上面写的波兰式.只不过按照语义,变为符号化了.

五丶纯虚函数的反汇编 

我们知道,纯虚函数是为了子类实现了,自己不能实现,但是反汇编代码中其实实现了,只不过里面调用了提示错误的API.就是为了你不小心调用的时候提示不能创建xxx对象的实例.等等一些列的错误.

高级代码:

class MyFather              //父类
{
public:
MyFather();
~MyFather();
virtual void show() = ; //纯虚函数
}; MyFather::MyFather(){} MyFather::~MyFather(){} class MyChild : public MyFather //子类继承父类
{
public:
MyChild();
virtual ~MyChild();
virtual void show();
}; MyChild::MyChild(){} MyChild::~MyChild(){} void MyChild::show()
{
cout << << endl;
} int main(int argc, char* argv[])
{
MyChild a; //定义对象
a.show();
return ;
}

Debug下反汇编

我们直接看纯虚函数内部了,在子类构造的时候父类会构造,父类构造自己的时候会填写虚表指针,我们直接找父类的虚表指针即可.然后定位虚表中的第二项.

第一项是父类的虚析构,第二项才是我们的.

纯虚函数在低版本就是19h,并且调用__amsg_exit,且如果弄了签名,则是__purecall

高版本不太一样,高版本不是简单的这样调用了(vs系列)它会保存当时的寄存器信息啊,什么的,然后写日志用的.反正结果是一样的.

高版本自己可以试试看一看有什么不同.

六丶模版识别.

模版和运算符重载一样,都是函数,编译为反汇编的代码都是函数调用.而且函数和函数的重载不同,它生成的反汇编代码有多处.

高级代码:

template <typename T>
T MySub(T a,T b)
{
return a - b;
} int main(int argc, char* argv[])
{
printf("%d\r\n",MySub(,));
   printf("%f\r\n",MySub(3.0f,1.0f));
  printf("%lf\r\n",MySub(8.3,4.3)); return ;
}

运行结果:

Debug下反汇编.

虽然都是一样调用,但是其内部是不同的.每个函数都有自己的汇编代码.

转载于:

作者:IBinary
出处:http://www.cnblogs.com/iBinary/

C++反汇编第三讲,反汇编中识别继承关系,父类,子类,成员对象的更多相关文章

  1. C++反汇编第四讲,反汇编中识别继承关系,父类,子类,成员对象

    C++反汇编第四讲,反汇编中识别继承关系,父类,子类,成员对象 讲解目录: 1.各类在内存中的表现形式   备注: 主要复习开发知识,和反汇编没有关系,但是是理解反汇编的前提.     2.子类继承父 ...

  2. C++反汇编第三讲,反汇编中识别虚表指针,以及指向的虚函数地址

    C++反汇编第三讲,反汇编中识别虚表指针,以及指向的虚函数地址 讲解之前,了解下什么是虚函数,什么是虚表指针,了解下语法,(也算复习了) 开发知识为了不码字了,找了一篇介绍比较好的,这里我扣过来了,当 ...

  3. 在Entity Framework 中实现继承关系映射到数据库表

    继承关系映射到数据库表中有多种方式: 第一种:TPH(table-per-hiaerachy) 每一层次一张表 (只有一张表) 仅使用名为父类的类型名的一张表,它包含了各个子类的所有属性信息,使用区分 ...

  4. Style在Android中的继承关系

    Style在Android中的继承关系 Android的Styles(样式)和Themes(主题)非常类似Web开发里的CSS,方便开发者将页面内容和布局呈现分开.Style和Theme在Androi ...

  5. Dom中的继承关系

    首先声明,一些内容基于个人猜测,如果哪里有错误,请立即联系在下! 我们用js操作Dom时,会经常用到一些个方法比如基于获取到的元素选择其子元素: <!DOCTYPE html> <h ...

  6. 初步学习C++中的继承关系

    继承机制是面向对象程序设计使代码能够复用的最重要的手段,它同意程序猿在保持原有类特性的基础上进行扩展,添加功能. 这样产生新的类,称派生类.继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂 ...

  7. java中的继承关系

    1.定义 java中的继承是单一的,一个子类只能拥有一个父类:java中所有类的父类是java.lang.Object,除了这个类之外,每个类只能有一个父类: 而一个父类可以有多个子类,可以被多个子类 ...

  8. c++中的继承关系

    1 什么是继承 面向对象的继承关系指类之间的父子关系.用类图表示如下: 2 为什么要有继承?/ 继承的意义? 因为继承是面向对象中代码复用的一种手段.通过继承,可以获取父类的所有功能,也可以在子类中重 ...

  9. android 中组件继承关系图,一目了然

    View继承关系图 Adapter适配器继承关系图 Activity继承关系图

随机推荐

  1. [转][C#].Net反编译利器

    来自:https://www.cnblogs.com/zsuxiong/p/5117465.html 有以下8款非常不错的.Net反编译利器: 1.Reflector Reflector是最为流行的. ...

  2. [转]Html 页面常用单词

    Html 页面常用单词 本文来自:https://gitee.com/opencc/SchoolAppDemo 第2批: JavaScript ready 准备 function 函数 disappe ...

  3. 以太坊Geth通过私钥导入新地址到钱包步骤

    Open TextEdit Paste key into TextEdit without any extra characters or quotations Save the file as pk ...

  4. Swift 变量

    变量是一种使用方便的占位符,用于引用计算机内存地址. Swift 每个变量都指定了特定的类型,该类型决定了变量占用内存的大小,不同的数据类型也决定可存储值的范围. 包括整形Int.浮点数Double和 ...

  5. flask的post,get请求及获取不同格式的参数

    flask的post,get请求及获取不同格式的参数 1 获取不同格式参数 1.0 获取json参数 Demo from flask import Flask, request, jsonify ap ...

  6. QString std::string 相互转 含中文

    std::string cstr;QString qstring; //QString str1 = " D:\\参考手册\\BIM\\osg\\build1.OSGB"; //从 ...

  7. linux---学习3

    1.free命令可以显示当前系统未使用的和已使用的内存数目,还可以显示被内核使用的内存缓冲区. //-m:以MB为单位显示内存使用情况: free -m 2.vmstat命令的含义为显示虚拟内存状态, ...

  8. MyEclipse的Git配置

    1.下载:git的插件egit  并解压 插件 下载地址:http://www.eclipse.org/egit/download/ 所有版本:http://wiki.eclipse.org/EGit ...

  9. java.lang.NoClassDefFoundError: org/springframework/aop/TargetSource

    在使用Spring框架时 报错 :java.lang.NoClassDefFoundError: org/springframework/aop/TargetSource 原因:为引入spring-a ...

  10. HDFS数据定期清理

    HDFS数据清理一些办法: datanode数据做reblance清理临时目录.日志目录文件全量分区表历史分区清理使用lzo,orc格式进行数据压缩清理或者归档历史冷数据增加datanode横向扩容附 ...