什么网站能让小孩做算术题,网页制作教学设计,wordpress注册评论,烟台手机网站建设电话一、单类继承在父类中声明为私有的成员#xff0c;子类对象无法直接访问#xff0c;但是在子类对象的内存结构中#xff0c;父类私有的成员数据依然存在。C语法规定的访问限制仅限于编译层面#xff0c;在编译过程中进行语法检查#xff0c;因此访问控制不会影响对象的内存…一、单类继承在父类中声明为私有的成员子类对象无法直接访问但是在子类对象的内存结构中父类私有的成员数据依然存在。C语法规定的访问限制仅限于编译层面在编译过程中进行语法检查因此访问控制不会影响对象的内存结构。子类未提供构造函数或析构函数而父类却需要构造函数与析构函数时编译器会为子类提供默认的构造函数与析构函数。但是子类有构造函数而父类不存在构造函数且没有虚函数则编译器不会为父类提供默认的构造函数。1. 内存结构①先安排父类的数据②后安排子类新定义的数据说明基于上述的内存排列方法即父类数据成员被安排前面。不管是父类的对象还是子类的对象父类的数据成员在内存中相对于对象的首地址的偏移值都是一样的。而且成员数据访问都是基于this指针间接寻址的。所以对于子类对象而言使用父类指针或者子类指针都可以正确访问其父类数据。2. 虚表虚表的排列顺序是按虚函数在类继承层次中首次声明的顺序依次排列的。只要继承了父类其派生类的虚函数表中的父类部分的排列就与父类一样。子类新定义的虚函数会按照声明顺序紧跟其后。 3. 构造函数①先调用父类构造函数②然后按照声明顺序调用成员数据变量的构造函数和初始化列表中指定的成员③最后再执行子类构造函数的函数体。说明①父类构造函数虚表指针修改为指向父类的虚表所以在父类构造函数内调用虚函数调用的是父类的虚函数。②子类构造函数虚表指针修改为指向子类的虚表4. 析构函数①先调用子类析造函数②然后成员对象的析构函数按照声明的顺序以倒序方式依次调用成员对象的析构函数。③再执行父类构造函数说明析构函数执行会首先设置虚表指针为自身虚表再调用自身的析构函数。防止父类析构函数内调用子类对象的虚函数。类有派生与继承关系需要声明析构函数为虚函数。若析构函数不是虚函数时当使用父类指针指向堆中的子类对象并使用delete释放对象空间时编译器会按照指针类型调用父类的析构函数从而引发错误。识别类之间的关系先定位构造函数根据构造先后顺序得到与之相关的其他类。再根据虚表利用IDA中使用引用参考功能可得到所有的构造和析构函数。二、多重继承1. 内存排列数据成员的排列顺序由继承父类的先后顺序所决定从左向右依次排列。子类虚表指针的个数取决于所继承的父类的个数有几个父类便对应几个虚表指针虚基类除外。将一个子类对象赋值给某个父类指针时该父类指针便指向该父类所对应的虚表指针。三、单类继承与多重继承比较单继承类在类对象占用的内存空间中只保存一份虚表指针只有一个虚表指针对应的也只有一个虚表虚表中各项保存了类中各虚函数的首地址构造时先构造父类再构造自身并且只调用一次父类构造函数析构时限析构自身再析构父类并且只调用一次父类析构函数多重继承类在类中所占用的内存空间中根据继承父类的个数保存对应的虚表指针根据所保存的虚表指针的个数对应产生相应个数的虚表。转换父类指针时需要跳转到对象的首地址。构造时需要按照继承顺序调用多个父类构造函数。析构时先析构自身然后以与构造函数相反的顺序调用所有父类的析构函数当对象作为成员时整个类对象的内存结构和多重继承相似。当类中无虚函数时整个类对象内存结构和多重继承完全一样可酌情还原当父类或成员对象存在虚函数时通过过观察虚表指针的位置和构造函数、析构函数中填写虚表指针的数目及目标地址来还原继承或成员关系。四、工程案例分析1. 单类继承#include iostreamusing namespace std; class Base {public: Base(){ nBase 1;printf(CBase); } ~Base(){ printf(~CBase); } virtual void f() { printf(Base:f());} virtual void g() { printf(Base:g());}private: int nBase;}; class Derive : public Base {public: Derive(){ nDerive2;printf(Derive); } ~Derive(){ printf(~Derive); } virtual void g(){ printf(Dervie:g());} virtual void h(){ printf(Dervie:h());}private: int nDerive;}; int main(){ Derive d; Base *b d; b-g(); return 0;}1. 内存分布类Derive对象0019FD30 0139583C .rdata:const Derive::vftable0019FD34 00000001 Base.nBase0019FD38 00000002 Derive.nDerive 虚函数表0139583C 01391163 Base::f(void)01395840 0139110E Derive::g(void)01395844 013911AE Derive::h(void)2. 构造函数pop ecx ;this指针出栈mov [ebpthis], ecx ;保存this指针mov ecx, [ebpthis] call j_??0BaseQAEXZ ;调用基类构造函数Base::Base(void)mov eax, [ebpthis] ;eaxthis指针mov dword ptr [eax], offset ??_7Derive6B ;初始化虚表指针为const Derive::vftable3. 析构函数pop ecx ;this指针出栈mov [ebpthis], ecx ;保存this指针mov eax, [ebpthis]mov dword ptr [eax], offset ??_7Derive6B ;重置虚表指针为const Derive::vftablemov esi, esppush offset aDerive ; ~Derivecall ds:__imp__printfadd esp, 4cmp esi, espcall j___RTC_CheckEspmov ecx, [ebpthis] ;ecx传参this指针 call j_??1BaseQAEXZ ; 调用基类析构函数 Base::~Base(void)