虚函数

C++通过关键字virtual来将函数声明为一个虚函数。当一个类包含虚函数后编译器就会给类的实例对象增加一个虚表指针,所谓的虚表指针就是指向虚表的指针。虚表就是一张地址表,它包含了这个类中所有虚函数的地址。对象所在内存的前四个字节就是虚表指针。

class Test
{
public:
int iNum = 1;
virtual void Proc1();
virtual void Proc2();
virtual void Proc3();
}; Test test();

例如以上示例中,如果实例化一个Test对象,在内存中查看对象内存地址。因为前四个字节存储的是虚表指针,我们查看对应的虚表地址发现对应的三个虚拟函数的地址。



对象在刚实例化时虚表指针中的值是无意义的,当对象调用构造函数时,构造函数会对虚表指针进行初始化为当前类的虚表地址。

所以当我们没有为类提供默认的构造函数时,为了能够初始化虚表指针编译器会为类提供一个默认的构造函数,从而在构造函数中对虚表指针进行初始化。

虚函数的直接调用

test.Proc1();

当我们没有使用对象指针或者对象引用类调用虚函数时,例如通过类对象调用虚函数时,编译器并不会去查虚表,而是直接调用对应类的虚函数。(因为查表是无意义的,此时其虚表中的函数地址一定是等于实际的虚函数地址的)

虚函数的间接调用

Test* test;
test = new Test;
test->Proc1();
test->Proc3();
test->Proc2();

当我们利用指针或者是引用来调用虚函数时,编译器就会通过虚表指针查询虚表来调用虚表中对应的虚函数的地址,继而形成多态。

多层继承中的虚函数

class Test
{
public:
Test()
{
Proc1();
} ~Test()
{
Proc1();
}
virtual void Proc1();
virtual void Proc2();
virtual void Proc3();
}; class Test1:public Test
{
public:
virtual void Proc1();
}; Test1 test1;

上例码从Test类派生出Test1类,并在Test1类中覆盖虚函数Proc1,Test类的构造函数和析构函数中调用虚函数Proc1。

实例化一个Test1类对象,Test1的构造函数会先调用基类的Test类的构造函数。注意其传递的this指针指向的是Test1类对象。



Test类的构造函数会利用传递过来的this指针来初始化虚表指针指向自己的虚表,但注意实际此虚表指针是Test1类对象的。这样我们在Test类中调用的虚函数Proc1()就失去了应该有的多态性,编译器直接将虚函数调用变为直接调用方式。



继续调用完Test类的构造函数后回到Test1类的构造函数中,其会将虚表指针指向自己的虚表。这样就可以让指针调用虚函数时能够通过查虚表实现多态。



接着当test1对象生命周期结束时,其会先调用Test1类的析构函数。然后在调用Test类的析构函数。



Test类的析构函数会现将虚表指针在次修改指向Test类的虚表,然后我们调用虚函数Proc1就失去了多态性,编译器直接将虚函数调用变为直接调用方式。



以上虚表指针变化过程如下



那么为什么在Test1的析构函数中为什么不更改虚表指针呢?个人觉得是因为其更改后虚拟指针不会变化所以编译器将其优化没了。

至此我们可以得知类的构造函数会对虚表指针初始化,而析构函数会将虚表指针还原。且构造函数和析构函数中调用虚函数会失去多态性,全部变为直接调用方式。那么为什么在析构函数中不直接把所有的虚函数调用直接变为直接调用,还要多此一举将修改虚表指针呢(在调用父类Test的构造函数时将虚表指针指向Test类自己的虚表,在调用子类Test的析构函数时将虚表指针还原为指向Test类自己的虚表)。 原因是有可能在Test的 构造函数/析构函数 时会调用一个成员函数,而这个成员函数有可能会调用虚函数,间接调用形成多态。如果不还原虚拟指针的话,因为此时如果虚表指针指向Test1的虚表,此成员函数调用的虚函数就有可能是Test1类的成员函数。

C++逆向分析----虚函数与多层继承的更多相关文章

  1. c++内存分布之虚函数(单一继承)

    系列 c++内存分布之虚函数(单一继承) [本文] c++内存分布之虚函数(多继承) 结论 1.虚函数表指针 和 虚函数表 1.1 影响虚函数表指针个数的因素只和派生类的父类个数有关.多一个父类,派生 ...

  2. c++内存分布之虚函数(多继承)

    系列 c++内存分布之虚函数(单一继承) c++内存分布之虚函数(多继承) [本文] 结论 1.虚函数表指针 和 虚函数表 1.1 影响虚函数表指针个数的因素只和派生类的父类个数有关.多一个父类,派生 ...

  3. Visual C++ 8.0对象布局的奥秘:虚函数、多继承、虚拟继承(VC直接输出内存布局)

    原文:VC8_Object_Layout_Secret.html 哈哈,从M$ Visual C++ Team的Andy Rich那里又偷学到一招:VC8的隐含编译项/d1reportSingleCl ...

  4. VC++对象布局的奥秘:虚函数、多继承、虚拟继承

    哈哈,从M$ Visual C++ Team的Andy Rich那里又偷学到一招:VC8的隐含编译项/d1reportSingleClassLayout和/d1reportAllClassLayout ...

  5. 虚函数重载(overwrite) 继承覆盖问题

    引言 类接口需要添加默认参数,以适应不同情况调用, 但是clang-tidy 不允许在接口上设置默认参数,ps: 可能担心继承类里接口重新设置新默认参数而导致误用的情况 #include <st ...

  6. C++虚函数与多继承

    虚函数 C++用虚函数实现运行时多态,虚函数的实现是由两个部分组成的,虚函数指针与虚函数表. 虚函数指针(vptr)是指向虚函数表的指针,在一个被实例化的对象中,它总是被存放在该对象的地址首位.而虚函 ...

  7. C++中的继承与虚函数各种概念

    虚继承与一般继承 虚继承和一般的继承不同,一般的继承,在目前大多数的C++编译器实现的对象模型中,派生类对象会直接包含基类对象的字段.而虚继承的情况,派生类对象不会直接包含基类对象的字段,而是通过一个 ...

  8. C++ 派生类函数重载与虚函数继承详解

    目录 一.作用域与名字查找 1.作用域的嵌套 2.在编译时进行名字查找 3.名字冲突与继承 4.通过作用域运算符来使用隐藏的成员 二.同名函数隐藏与虚函数覆盖 1.几种必须区分的情况 2.一个更复杂的 ...

  9. C++反汇编与逆向分析技术揭秘

    C++反汇编-继承和多重继承   学无止尽,积土成山,积水成渊-<C++反汇编与逆向分析技术揭秘> 读书笔记 一.单类继承 在父类中声明为私有的成员,子类对象无法直接访问,但是在子类对象的 ...

随机推荐

  1. ch1_6_2求解删除公共字符问题

    输入两个字符串,从第一字符串中删除第二个字符串中所有的字符.例如,输入"They are students."和"aeiou",则删除之后的第一个字符串变成&q ...

  2. P1090 合并果子(JAVA语言)

    题目描述 在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和.可 ...

  3. TypeError: 'str' object does not support item assignment Python常见错误

    1.string是一种不可变的数据类型 2.尝试使用 range()创建整数列 有时你想要得到一个有序的整数列表,所以 range() 看上去是生成此列表的不错方式. 需要记住 range() 返回的 ...

  4. java面试-强引用、软引用、弱引用和幻象引用有什么区别

    在Java语言中,除了基本数据类型外,其他的都是指向各类对象的对象引用:Java中根据其生命周期的长短,将引用分为4类. 不同的引用类型,主要体现的是对象不同的可达性状态和对垃圾收集的影响. 1 .强 ...

  5. java面试-JVM常用的基本配置参数有哪些?

    1.-Xms 初始大小内存,默认为物理内存 1/64,等价于 -XX:InitialHeapSize 2.-Xmx 最大分配内存,默认为物理内存的 1/4,等价于 -XX:MaxHeapSize 3. ...

  6. C#中SQLite的使用及工具类

    目录 SQLite简介 存储类 亲和类型 引用System.Data.SQLite.dll 软件包分类 使用本机库预加载 常用部署包 工具类 参考资料 SQLite简介 SQLite是一款轻型的数据库 ...

  7. Element源码:项目初始化和webpack配置

    0x00.项目初始化 由于整个过程像素级 copy element,所以将不使用vue-cli初始化项目. 创建项目 新建一个空的文件夹,使用npm init 来初始化项目,并安装vue模块. 修改目 ...

  8. 【Prometheus学习笔记】主机监控 -node_exporter

    Exporter for machine metrics prometheus/node_exporter 安装 Prometheus sudo tar -zxvf prometheus-*.tar. ...

  9. SQL Server 实用语句

    创建临时表 #Test CREATE TABLE #Test( ID INT, Name VARCHAR(50) ) INSERT INTO #Test( ID, Name ) VALUES ( 1, ...

  10. 消息中间件-ActiveMQ支持的消息协议

    package com.study.mq.a1_example.helloworld.queue; import org.apache.activemq.ActiveMQConnectionFacto ...