反汇编探寻C++继承
Posted NONE
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了反汇编探寻C++继承相关的知识,希望对你有一定的参考价值。
一,简单继承:
#include <iostream> class TableTennisPlayer { private: int id; public: TableTennisPlayer(int id); void sayHi(); }; TableTennisPlayer::TableTennisPlayer(int n) { id = n; } void TableTennisPlayer::sayHi() { std::cout << "Hi,I am player" << id << std::endl; } class MasterPlayer :public TableTennisPlayer { private: int id; public: MasterPlayer(int id); void fuck(); }; MasterPlayer::MasterPlayer(int n) :TableTennisPlayer(n) { id = n; } void MasterPlayer::fuck() { std::cout << "fuck you" << std::endl; } int main(void) { using std::cout; using std::cin; TableTennisPlayer player(1); MasterPlayer master(2); player.sayHi(); master.sayHi(); master.fuck(); cout << sizeof(player) << std::endl; cout << sizeof(master) << std::endl; cin.get(); }
父类成员变量id在构造函数初始化。
子类也有成员变量id,在子类构造函数初始化。
反汇编代码看出,子类构造函数初始化子类时首先调用父类构造函数初始化父类--具体是初始化父类成员变量。
所以:
这是父类在内存中的存储 00 00 00 01 占4字节
这是子类在父类的存储:
00 00 00 02 00 00 00 02 占8字节,前四字节是父类成员变量,后四字节是子类成员变量
由此看出,子类对父类是包含关系。具体是指包含父类成员变量。另外,构造函数没什么特殊的。其实就是类建立时自动调用的函数。去初始化类成员,也就是为成员变量申请内存。子类初始化,先调用父类构造方法初始化,父类成员变量。然后调用自己的,初始化自己成员变量。
二,虚函数
由于C++中存在指针的概念。所以,必须引出虚函数。这一点你如果不从内存角度思考,去真正理解C++中对象、以及指向对象的指针的概念是什么,你是无法真正理解虚函数的!!!
如果一个指针指向子类。那么:
#include<iostream> using namespace std; class A { public: void print() { cout<<"This is A"<<endl; } }; class B : public A { public: void print() { cout<<"This is B"<<endl; } }; int main() { A a; B b; a.print(); b.print(); return 0; }
运行结果:指向子类的指针,调用的却是父类的print函数。
这便是引入虚函数概念的原因:
#include<iostream> using namespace std; class A { private: int a = 1; public: virtual void print() { cout << "This is A" << endl; } }; class B : public A { private: int b = 2; public: virtual void print() { cout << "This is B" << endl; } }; class C : public B { private: int c = 3; public: virtual void print() { cout << "This is C" << endl; } }; int main() { A a; B b; C c; A *p1 = &a; A *p2 = &b; A *p3 = &c; p1->print(); p2->print(); p3->print(); cin.get(); return 0; }
所以利用指针来操作的话,虽然调用的是指向子对象的指针,其实那里存储的是子对象里包含着的父对象的空间。
加上virtual修饰符后,则是完全另一种结果。
带有虚函数的对象是另外一种初始化方式:在构造函数中会将this指针指向虚函数表(对象内存地址里首先保存的是虚函数表)。
也就是说:带有虚函数的对象,对象地址首先保存的是,指向虚函数表的指针(地址),然后才是类成员变量。
子类继承了,带有虚函数的父类。对象地址首先保存的是,指向虚函数表的指针(地址),然后是父类成员变量,然后才是子类成员变量。
三、多重继承
继续上面的例子,C继承B,B继承A(A为虚函数)。那么C在内存中是什么样子呢?前四个字节是虚函数表指针,然后是A类成员变量,然后是B类成员变量,最后才是自己的成员变量。
四、多继承
如下代码所示,类C继承类B、类A
#include<iostream> using namespace std; class A { private: int a = 1; public: virtual void print() { cout << "This is A" << endl; } }; class B { private: int b = 2; public: virtual void print() { cout << "This is B" << endl; } }; class C : public B,public A { private: int c = 3; public: virtual void print() { cout << "This is C" << endl; } }; int main() { C c; A *p = &c; p->print(); cin.get(); return 0; }
由反汇编结果可知,内存中存储结果为:指向虚函数表的指针、类B成员变量、指向虚函数表的指针、类A成员变量、最后是自己的成员变量。
五,总结
由上面几个例子可以知道。
1 C++为了实现面向对象语言中的“向上转型”,特别引入了虚函数的概念。为何如此?因为C++有指针!!!!!!
Java是不需要的,因为java没有指针。
2 虚函数表具体是什么样的?具体可以看这篇博客:http://blog.csdn.net/haoel/article/details/1948051/
类似这张图,虚函数表中,父类的虚函数在子类虚函数前面。
以上是关于反汇编探寻C++继承的主要内容,如果未能解决你的问题,请参考以下文章
C++反汇编第三讲,反汇编中识别继承关系,父类,子类,成员对象
Android 逆向使用 Python 解析 ELF 文件 ( Capstone 反汇编 ELF 文件中的机器码数据 | 创建反汇编解析器实例对象 | 设置汇编解析器显示细节 )(代码片段