深入浅出之虚函数原理篇(笔记三)

本文深入解析虚函数如何通过虚函数表实现多态性,包括虚函数表的工作原理、成员函数的内存布局及派生类对虚函数的重写过程。

上一节,我们讲到了虚函数,那么你知道虚函数是如何做到多态的吗?

虚函数是通过后期绑定,在执行时间接通过一张虚函数表,间接调用欲绑定的函数。表中的每一个元素都指向虚函数的地址。当然,编译器也会为类增加一项成员变量,此成员变量是一个指向虚函数表的指针。可用图解表示如下:

 

注意:

成员函数memfuc()经过编译之后,形成代码,然后放在内存中的代码区,并不是数据段。成员函数都是放在一起,而且由同一个类的所有对象共享。,用sizeof(a)计算的结果是不包括代码段的。

以下几点,等下我们一一论证:

1.每一个由此类派生出来的对象,都有一个vptr。当我们通过对象调用虚函数时,实际上是通过vptr找到虚函数表,然后通过虚函数表找到真正的虚函数地址。

2.虚函数表中的内容是根据类中的虚函数声明顺序一个一个填 入函数指针。

3.当派生类继承了基类后,同时也继承了虚函数表,当我们在派生类中修改了虚函数时,其对应的虚函数表中所指向的函数地址,就不再是基类的了,而是派生类的了。此点我们用图解如下:

 

现在我们来分析一下到底是不是这样的?这一节讲解以下面的代码为例(在上一节的例子基础上修改而成)

 

ContractedBlock.gifExpandedBlockStart.gifCode
#include <iostream.h>
#include 
<stdio.h>
#include 
<string.h>

class CEmployee
{
public:
    
int m_name;
public:
    CEmployee(){};
    
    
virtual int computerPay()
    {
        printf(
"这是基类的计算方法");
        
return(2);
    }
    
virtual void display()
    {
        printf(
"职员");
    }

};

class CWage:public CEmployee
{
public:
    
int m_wage;
    
int m_hour;
public:
    CWage(){};

    
void set(int wage,int hour)
    {
        m_wage
=wage;
        m_hour
=hour;
    }
    
virtual    int computerPay()
    {
        
return m_wage*m_hour;
    }
    
virtual void display()
    {
        printf(
"时薪职员");
    }
};

class CSales:public CWage
{
public:
    
int n_sale;
    
int n_hour;
public:
    CSales(){};
    
    
void set(int sale,int hour)
    {
        n_sale
=sale;
        n_hour
=hour;
    }
    
virtual    int computerPay()
    {
        
return n_sale*n_hour+CWage::computerPay();
    }
    
virtual void display()
    {
        printf(
"销售员");
    }

};

void main()
{
    cout
<<sizeof(CEmployee)<<endl;
    cout
<<sizeof(CWage)<<endl;
    cout
<<sizeof(CSales)<<endl;
    CEmployee a;
    CWage     b;
    CSales    c;
    b.m_name
=1;
    b.m_wage
=2;
    b.m_hour
=3;
    cout
<<b.m_name<<endl;
    cout
<<b.m_wage<<endl;
    cout
<<b.m_hour<<endl;

    cout
<<&b<<endl;
    cout
<<&(b.m_name)<<endl;
    cout
<<&(b.m_wage)<<endl;
    cout
<<&(b.m_hour)<<endl;
    cout
<<&c<<endl;
}

运行后的结果如下:

 

我们来分析一下:

1.sizeof(CEmployee)的大小为8,而这个类中只有一个整型是4个字节,那么还有四个字节是谁占了呢,对了,就是指向虚函数表的vptr成员变量。说明确实在类中只要有虚函数,就一定会有vptr这个成员变量。

2.sizeof(CWage)的大小为16,实际上是基类的整型+本类的两个整型+从基类继承的vptr也占用了四个字节,刚好说明派生类继承了虚函数表。

3.看清0x0012ff68这个内存地址,它是类CWage对象的起始地址,这个地址中的内容刚好是vptr,我们跟踪程序,调试时可查,我把图取上来大家看一下:

 

4.虚函数表中的内容是根据类中的虚函数声明顺序一个一个填 入函数指针。大家看上图先是压入CWag::computerPay,然后再是CWage::display。

5.当派生类继承了基类后,同时也继承了虚函数表,当我们在派生类中修改了虚函数时,其对应的虚函数表中所指向的函数地址,就不再是基类的了,而是派生类的了。现在我们看到的是b对象(即CWage),转到C对象时,会发现虚函数表中的地址都发生变化了。

今天就先写到这儿,本人才shu学浅,有什么意见或见意,希望能与大家一同探讨。

 

标题基于Python的自主学习系统后端设计与现AI更换标题第1章引言介绍自主学习系统的研究背景、意义、现状以及本文的研究方法创新点。1.1研究背景与意义阐述自主学习系统在教育技术领域的重要性应用价值。1.2国内外研究现状分析国内外在自主学习系统后端技术方面的研究进展。1.3研究方法与创新点概述本文采用Python技术栈的设计方法系统创新点。第2章相关理论与技术总结自主学习系统后端开发的相关理论技术基础。2.1自主学习系统理论阐述自主学习系统的定义、特征理论基础。2.2Python后端技术栈介绍DjangoFlask等Python后端框架及其适用场景。2.3数据库技术讨论关系型非关系型数据库在系统中的应用方案。第3章系统设计与现详细介绍自主学习系统后端的设计方案现过程。3.1系统架构设计提出基于微服务的系统架构设计方案。3.2核心模块设计详细说明用户管理、学习资源管理、进度跟踪等核心模块设计。3.3关键技术现阐述个性化推荐算法、学习行为分析等关键技术的现。第4章系统测试与评估对系统进行功能测试性能评估。4.1测试环境与方法介绍测试环境配置采用的测试方法。4.2功能测试结果展示各功能模块的测试结果问题修复情况。4.3性能评估分析分析系统在高并发等场景下的性能表现。第5章结论与展望总结研究成果并提出未来改进方向。5.1研究结论概括系统设计的主要成果技术创新。5.2未来展望指出系统局限性并提出后续优化方向。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值