结论

虚函数表指针 + 虚函数表 共同实现

演示

VS2017(32位)

基类有析虚构函数

一段代码演示

#include <iostream>
#include <memory> class shape
{
public:
virtual ~shape()
{
std::cout << "~shape\n\n";
}
}; class circle : public shape
{
public:
~circle()
{
std::cout << "~circle\n\n";
}
}; int main(int argc, char *argv[], char *env[])
{
std::unique_ptr<shape> pshape(new(std::nothrow) circle); return 0;
}

circle 继承 基类shape, main函数中用一个派生类的对象赋值给基类的指针 。

内存模型

circle的内存模型如下:

1>class circle	size(4):
1> +---
1> 0 | +--- (base class shape)
1> 0 | | {vfptr}
1> | +---
1> +---
1>
1>circle::$vftable@:
1> | &circle_meta
1> | 0
1> 0 | &circle::{dtor}

基类的析构函数是虚函数,故排在最前面的是虚函数表指针,接着是基类成员,然后是派生类成员。

注意:虚函数表中,存放了 派生类的的析构函数的地址(&circle::{dtor})。

分析

当发生析构时,派生类首先调用派生类的析构函数,再调用基类的析构函数。

pshape指向的派生类的对象,正因为存在虚函数表指针,析构时,根据虚函数表指针指向虚函数表中的析构函数(&circle::{dtor}),这样,就能精准定位派生类的析构函数。

基类无析构函数的情况

既然基类没有虚析构函数,尽管基类存在虚函数,发生析构时,派生类的虚函数表中没有存放析派生类的析构函数的地址,所以不能精准定位派生类的析构函数的地址,派生类的释放可能存在内存隐患。

一段代码

相对上面的代码,基类的析构函数去掉,额外增加一个虚函数。

#include <iostream>

class shape
{
public:
~shape()
{
std::cout << "~shape\n\n";
} virtual void run(){}
}; class circle : public shape
{
public:
~circle()
{
std::cout << "~circle\n\n";
}
}; int main(int argc, char *argv[], char *env[])
{
std::unique_ptr<shape> pshape(new(std::nothrow) circle); return 0;
}

内存模型

1>class circle	size(4):
1> +---
1> 0 | +--- (base class shape)
1> 0 | | {vfptr}
1> | +---
1> +---
1>
1>circle::$vftable@:
1> | &circle_meta
1> | 0
1> 0 | &shape::run

C++基础之虚析构函数原理的更多相关文章

  1. C++中基类虚析构函数的作用及其原理分析

    虚析构函数的理论前提是 执行完子类的析构函数,那么父类的虚构函数必然会被执行. 那么当用delete释放一个父类指针所实例化的子类对象时,如果没有定义虚析构函数,那么将只会调用父类的析构函数,而不会调 ...

  2. 虚析构函数? vptr? 指针偏移?多态数组? delete 基类指针 内存泄漏?崩溃?

    五条基本规则: 1.如果基类已经插入了vptr, 则派生类将继承和重用该vptr.vptr(一般在对象内存模型的顶部)必须随着对象类型的变化而不断地改变它的指向,以保证其值和当前对象的实际类型是一致的 ...

  3. C/C++中的虚析构函数和私有析构函数的使用

    代码: #include <iostream> using namespace std; class A{ public: A(){ cout<<"construct ...

  4. C++中基类的析构函数为什么要用virtual虚析构函数

    知识背景 要弄明白这个问题,首先要了解下C++中的动态绑定. 关于动态绑定的讲解,请参阅:  C++中的动态类型与动态绑定.虚函数.多态实现 正题 直接的讲,C++中基类采用virtual虚析构函数是 ...

  5. 基类的析构函数写成virtual虚析构函数

    虚函数作用:动态绑定,实现多态效果. 场景问题: 派生类中有资源需要回收,而在编程中采用多态,由基类的指针指向派生类,则在释放的时候,如果基类的析构函数不是virtual,则派生类的析构函数得不到释放 ...

  6. C++虚函数原理

    类中的成员函数分为静态成员函数和非静态成员函数,而非静态成员函数又分为普通函数和虚函数. Q: 为什么使用虚函数 A: 使用虚函数,我们可以获得良好的可扩展性.在一个设计比较好的面向对象程序中,大多数 ...

  7. C++虚函数表与虚析构函数

    1.静态联编和动态联编联编:将源代码中的函数调用解释为要执行函数代码. 静态联编:编译时能确定唯一函数.在C中,每个函数名都能确定唯一的函数代码.在C++中,因为有函数重载,编译器须根据函数名,参数才 ...

  8. 【C++】C++中基类的析构函数为什么要用virtual虚析构函数?

    正面回答: 当基类的析构函数不是虚函数,并且基类指针指向一个派生类对象,然后通过基类指针来删除这个派生类对象时,如果基类的析构函数不是虚析构函数,那么派生类的析构函数就不会被调用,从而产生内存泄漏 # ...

  9. C++(四十一) — 多态、虚函数、虚析构函数、纯虚函数

     1.多态 面向对象程序设计中,多态性表现为: (1)重载多态:函数重载.运算符重载: (2)运行多态:通过基类的指针(或引用)调用不同派生类的同名函数,表现出不同的行为: (3)模板多态:参数多态, ...

随机推荐

  1. MongoDB 安装/启动/基本操作命令

    1.MongoDB简介 MongoDB是一个基于分布式文件存储的数据库,由C++语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案. MongoDB是一个介于关系型数据库和非关系数据库之间的 ...

  2. 实时数仓(二):DWD层-数据处理

    目录 实时数仓(二):DWD层-数据处理 1.数据源 2.用户行为日志 2.1开发环境搭建 1)包结构 2)pom.xml 3)MykafkaUtil.java 4)log4j.properties ...

  3. Spark(四)【RDD编程算子】

    目录 测试准备 一.Value类型转换算子 map(func) mapPartitions(func) mapPartitions和map的区别 mapPartitionsWithIndex(func ...

  4. Scala(一)【安装和IDEA中开发】

    目录 一.下载 二.windows安装 三.linux环境安装 四.Ida开发Scala 1.在线下载Scala插件 2.离线下载Scala插件 3.验证 五.HelloWorld入门程序 1.新建M ...

  5. 寻找pair

    给定n个整数使其两两组合成一对pair,例如给定 1 ,2 可以组成的pair为(1,1),(1,2),(2,1),(2,2),然后在这些pair中寻找第k小的pair. 输入第一行包含两个数字,第一 ...

  6. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(七)-准备移植FatFs

    [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 [STM3 ...

  7. GO Exit Fatal panic

    Exit() 应用程序(不只是函数)退出执行 defer 不会被执行(因为程序都退出了) log.Fatal() 输出打印内容 应用程序退出 defer 不会被执行 panic() 函数停止执行(不是 ...

  8. ReactiveCocoa操作方法-线程\时间

    ReactiveCocoa操作方法-线程 deliverOn: 内容传递切换到制定线程中,副作用在原来线程中,把在创建信号时block中的代码称之为副作用. subscribeOn: 内容传递和副作用 ...

  9. LoadRunner中怎么设置密码参数化与用户名关联

    对密码参数化时从parameter里的"Select next row"列表中选择Same Line As这一选项,意思就是每一个密码参数化取值与对应行的用户名关联起来了

  10. Vue重要知识

    Event Bus 总线 Vue中的EventBus是一种发布订阅模式的实践,适用于跨组件简单通信. Vuex也可以用来组件中进行通信,更适用于多组件高频率通信. 使用方式: 1.把Bus注入到Vue ...