《C++反汇编与逆向分析技术揭秘》--单类继承 读书笔记

单类继承

  • 在父类中声明为私有的成员,子类对象无法直接访问,但是在子类对象的内存结构中,父类私有的成员数据依然存在。C++语法规定的访问限制仅限于编译层面,在编译过程中进行语法检查,因此访问控制不会影响对象的内存结构。
  • 子类未提供构造函数或析构函数,而父类却需要构造函数与析构函数时,编译器会为子类提供默认的构造函数与析构函数。但是子类有构造函数,而父类不存在构造函数,且没有虚函数,则编译器不会为父类提供默认的构造函数

 构造函数

  1. 先调用父类构造函数
  2. 然后按照声明顺序调用成员数据变量的构造函数和初始化列表中指定的成员
  3. 最后再执行子类构造函数的函数体

注意:父类构造函数,虚表指针修改为指向父类的虚表,所以在父类构造函数内调用虚函数,调用的是父类的虚函数

   子类构造函数,虚表指针修改为指向子类的虚表

 析构函数

  1. 先调用子类析造函数
  2. 然后成员对象的析构函数,按照声明的顺序以倒序方式依次调用成员对象的析构函数。
  3. 再执行父类构造函数

注意:析构函数执行会首先设置虚表指针为自身虚表,再调用自身的析构函数。防止父类析构函数内调用子类对象的虚函数。

   类有派生与继承关系,需要声明析构函数为虚函数。若析构函数不是虚函数时,当使用父类指针指向堆中的子类对象,并使用delete释放对象空间时,编译器会按照指针类型调用父类的析构函数,从而引发错误

构造函数
1
pop ecx ;this指针出栈 2 mov [ebp+this], ecx ;保存this指针 3 mov ecx, [ebp+this] 4 call j_??0Base@@QAE@XZ ;调用基类构造函数Base::Base(void) 5 mov eax, [ebp+this] ;eax=this指针 6 mov dword ptr [eax], offset ??_7Derive@@6B@ ;初始化虚表指针为const Derive::`vftable'
 析构函数
1
pop ecx ;this指针出栈 2 mov [ebp+this], ecx ;=>保存this指针 3 mov eax, [ebp+this] 4 mov dword ptr [eax], offset ??_7Derive@@6B@ ;重置虚表指针为const Derive::`vftable' 5 mov esi, esp 6 push offset aDerive ; 7 call ds:__imp__printf 8 add esp, 4 9 cmp esi, esp 10 call j___RTC_CheckEsp 11 mov ecx, [ebp+this] ;ecx传参this指针 12 call j_??1Base@@QAE@XZ ;调用基类析构函数 Base::~Base(void)

总结:

  1. 在类对象占用的内存空间中,只保存一份虚表指针
  2. 只有一个虚表指针,对应的也只有一个虚表
  3. 虚表中各项保存了类中各虚函数的首地址
  4. 构造时先构造父类,再构造自身,并且只调用一次父类构造函数
  5. 析构时限析构自身,再析构父类,并且只调用一次父类析构函数


参考文献:

《C++反汇编与逆向分析技术揭秘》

《汇编语言程序设计》

原文地址:https://www.cnblogs.com/mysky007/p/12650398.html