首页 > 其他分享 >虚析构函数的作用是什么?

虚析构函数的作用是什么?

时间:2023-03-07 19:00:09浏览次数:30  
标签:slot 调用 函数 virtual func address 虚析构 作用

目录

virtual 析构函数的作用

虚析构函数使得在删除指向子类对象的基类指针时可以调用子类的析构函数达到释放子类中堆内存的目的,而防止内存泄露的.

调用时机

在谈论实现之前先明确一下析构函数的调用时机

  1. 局部变量在作用域结束的时候,程序会默认调用析构函数(如果有的话)
  2. 主动调用delete(delete [])方法, 首先会查看对象类型,确定是否需要调用虚构函数. 如果发现对象的析构函数是virtual 的,就会使用virtual调用机制.

对象布局

要想了解virtual调用机制,首先就要清楚对象布局

@图1
如果派生类增加成员,则简单的放在基类的成员后面

image
vptr 虚指针: 指向对象的虚函数表
vtbl 虚函数表 : 存储对象虚函数地址

派生类通过vptr 找到vtal,然后找到对应的虚函数进行调用

覆盖(overriding)

定义一个和基类中虚函数名字和类型都相同的函数,以使派生类的函数代替基类中的版本被放入vtal的技术成为 覆盖(overriding)

virtual 函数调用机制

本质 : 虚函数调用产生的目标代码首先简单的寻址vptr ,通过它找到对应的vtbl,然后调用其中正确的函数. 其代价大约调用了两次内存访问加上一次普通的函数调用.

Demo 实践检验真理

//
//  main.cpp
//  Test
//
//  Created by xdf_yanqing on 2023/3/7.
//

#include <iostream>

using namespace std;
class B {
public:
    virtual void f() const { cout << "B::f1()\n"; }
    virtual void f2() const { cout << "B::f2()\n"; }
    virtual void f3() const { cout << "B::f3()\n"; }
    virtual void f4() const { cout << "B::f4()\n"; }
    void g(){ cout << "B::g()\n"; }
};
struct D : public B{
public:
    void f() const override{ cout << "D::f1()\n"; }
    void f4() const override{ cout << "D::f4()\n"; }

    void g(){ cout << "D::g()\n"; }
};
struct DD : public  D {
public:
    void f() { cout << "DD::f1()\n"; }
    void g() { cout << "DD::g()\n"; }
};

typedef void (*func)();
void call(B& b ) {
    for(int i =0 ; i < 4 ; i++) { 
        unsigned long* vtbl = (unsigned long*)(*(unsigned long*)&base) + i; 
        cout << "slot address: " << vtbl << endl;
        cout << "func address: " << *vtbl << endl;
        func pfunc = (func)*(vtbl);
        pfunc();
    }
    b.g();
    cout << "++++++++++++++++++++++++++++++++++++++++++" << endl;
}

int main(int argc, const char * argv[]) {
        
    B b;
    D d;
    DD dd;
     
    call(b);
    call(d);
    call(dd);
     
    b.f();
    b.g(); 
	
    d.f();
    d.g();
    
    dd.f();
    dd.g(); 
    return 0;
}

结果:

slot address: 0x1000040e0
func address: 4294979760
B::f1()
slot address: 0x1000040e8
func address: 4294980032
B::f2()
slot address: 0x1000040f0
func address: 4294980080
B::f3()
slot address: 0x1000040f8
func address: 4294980128
B::f4()
B::g()
++++++++++++++++++++++++++++++++++++++++++
slot address: 0x100004120
func address: 4294979808
D::f1()
slot address: 0x100004128
func address: 4294980032
B::f2()
slot address: 0x100004130
func address: 4294980080
B::f3()
slot address: 0x100004138
func address: 4294980240
D::f4()
B::g()
++++++++++++++++++++++++++++++++++++++++++
slot address: 0x100004168
func address: 4294979808
D::f1()
slot address: 0x100004170
func address: 4294980032
B::f2()
slot address: 0x100004178
func address: 4294980080
B::f3()
slot address: 0x100004180
func address: 4294980240
D::f4()
B::g()
++++++++++++++++++++++++++++++++++++++++++
B::f1()
B::g()
D::f1()
D::g()
DD::f1()
DD::g()
Program ended with exit code: 0

代码分析

对象b 通过虚函数表分别调用了自己的f f1 f2 f3

对应的函数地址:
f : 4294979760
f2: 4294980032
f3: 4294980080
f4: 4294980128

对象d 通过虚函数表分别调用了 父类的f2 f3 调用了覆盖函数 f f4

对应函数地址:
f: 4294979808
f2: 4294980032
f3: 4294980080
f4: 4294980240

对象dd 通过虚函数表分别调用了 父类的父级的f2 f3 调用了父级的函数 f f4

对应函数地址:
f: 4294979808
f2: 4294980032
f3: 4294980080
f4: 4294980240

virtual 是如何实现的呢?

通过上面的理解和具体的demo结果可知,虚函数的实现是如何的,即通过虚指针和虚函数表来实现具体函数调用

虚析构函数的作用呢?

和刚刚开始说明的作用一样,就是为了解决这种父类指针指向子类对象时,对象销毁时调用子类的析构函数销毁数据避免内存泄露的产生

标签:slot,调用,函数,virtual,func,address,虚析构,作用
From: https://www.cnblogs.com/zhao-jie-li/p/17189042.html

相关文章

  • python-函数
    函数的定义先定义再使用def定义函数的时候代码不执行调用函数的时候代码才执行函数的参数形参定义函数时候的参数形参必须是......
  • 函数
    函数的定义与调用:1.函数必须先定义,后调用2.函数在定义阶段,如果有参数,调用阶段也需要给对应的参数3.函数在定义阶段只检测语法是否正确,不执行具体的代码功能4.函数......
  • mysql 去除重复字符串的函数
    CREATEDEFINER=`root`@`localhost`FUNCTION`deleteManyChar`(in_strvarchar(2000))RETURNSvarchar(2000)CHARSETutf8COLLATEutf8_unicode_ciBEGINDECLAREv_r......
  • cuda错误检查函数
    cuda大部分库函数的返回值都是cudaError_t,所以可以用一个函数来接收其他库函数的返回值,从而判断该库函数是否正常执行这个函数可以用宏来实现#defineCHECK(call)......
  • 阿里云函数计算 FC 助力高德 RTA 广告投放系统架构升级
    作者:赵庆杰(阿里云函数计算)、林雪清(阿里云函数计算)、杜玲玲(高德)、王壁成(高德)导言2023年春节,经历了三年的疫情后,我们终于在春天迎来了曙光。国人的出行热情空前高涨:......
  • 阿里云函数计算 FC 助力高德 RTA 广告投放系统架构升级
    作者:赵庆杰(阿里云函数计算)、林雪清(阿里云函数计算)、杜玲玲(高德)、王壁成(高德)导言2023年春节,经历了三年的疫情后,我们终于在春天迎来了曙光。国人的出行热情空前高涨:回家看......
  • 生成函数相关
    P6295有标号DAG计数考虑不一定弱联通的DAG的EGF,ln一下得到答案。\(F[i]\):\(i\)个点的有标号DAG数量\(F[i]=\sum_{j=1}^{i}(-1)^{j-1}\dbinom......
  • 从青铜到王者,揭秘 Serverless 自动化函数最佳配置
    作者:丛霄背景介绍全托管的Serverless计算平台能给用户带来更少的运维代价、更强的稳定性和更快的弹性能力。Serverless的目标之一是免运维,但仍旧存在一些障碍,在Serv......
  • C++笔记-函数指针
    函数指针语法://fcnPtrisapointertoafunctionthattakesnoargumentsandreturnsanintegerint(*fcnPtr)();特点:函数指针的类型(参数和返回值)都必须和......
  • Java函数(方法)的默认值问题
    Java函数(方法)的默认值问题 Java不能为函数(方法)设置默认参数。原因是“默认参数”和“方法重载”同时支持的话有二义性的问题,但使用“方法重载”可以间接地实现”默认......