C++基础之虚析构函数原理
结论
虚函数表指针 + 虚函数表 共同实现
演示
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++基础之虚析构函数原理的更多相关文章
- C++中基类虚析构函数的作用及其原理分析
虚析构函数的理论前提是 执行完子类的析构函数,那么父类的虚构函数必然会被执行. 那么当用delete释放一个父类指针所实例化的子类对象时,如果没有定义虚析构函数,那么将只会调用父类的析构函数,而不会调 ...
- 虚析构函数? vptr? 指针偏移?多态数组? delete 基类指针 内存泄漏?崩溃?
五条基本规则: 1.如果基类已经插入了vptr, 则派生类将继承和重用该vptr.vptr(一般在对象内存模型的顶部)必须随着对象类型的变化而不断地改变它的指向,以保证其值和当前对象的实际类型是一致的 ...
- C/C++中的虚析构函数和私有析构函数的使用
代码: #include <iostream> using namespace std; class A{ public: A(){ cout<<"construct ...
- C++中基类的析构函数为什么要用virtual虚析构函数
知识背景 要弄明白这个问题,首先要了解下C++中的动态绑定. 关于动态绑定的讲解,请参阅: C++中的动态类型与动态绑定.虚函数.多态实现 正题 直接的讲,C++中基类采用virtual虚析构函数是 ...
- 基类的析构函数写成virtual虚析构函数
虚函数作用:动态绑定,实现多态效果. 场景问题: 派生类中有资源需要回收,而在编程中采用多态,由基类的指针指向派生类,则在释放的时候,如果基类的析构函数不是virtual,则派生类的析构函数得不到释放 ...
- C++虚函数原理
类中的成员函数分为静态成员函数和非静态成员函数,而非静态成员函数又分为普通函数和虚函数. Q: 为什么使用虚函数 A: 使用虚函数,我们可以获得良好的可扩展性.在一个设计比较好的面向对象程序中,大多数 ...
- C++虚函数表与虚析构函数
1.静态联编和动态联编联编:将源代码中的函数调用解释为要执行函数代码. 静态联编:编译时能确定唯一函数.在C中,每个函数名都能确定唯一的函数代码.在C++中,因为有函数重载,编译器须根据函数名,参数才 ...
- 【C++】C++中基类的析构函数为什么要用virtual虚析构函数?
正面回答: 当基类的析构函数不是虚函数,并且基类指针指向一个派生类对象,然后通过基类指针来删除这个派生类对象时,如果基类的析构函数不是虚析构函数,那么派生类的析构函数就不会被调用,从而产生内存泄漏 # ...
- C++(四十一) — 多态、虚函数、虚析构函数、纯虚函数
1.多态 面向对象程序设计中,多态性表现为: (1)重载多态:函数重载.运算符重载: (2)运行多态:通过基类的指针(或引用)调用不同派生类的同名函数,表现出不同的行为: (3)模板多态:参数多态, ...
随机推荐
- shell 脚本在linux中的应用
shell脚本在linux中应用广泛,之前一直选用python写脚本来进行一些文件操作,但是最后发现shell脚本非常方便,所以特意来学习下皮毛,便于提高自己效率 定义变量 1 country=&qu ...
- 完美png图片添加水印类
完美png图片添加水印类 被添加水印图片和水印图片都可以是png,保证透明无色背景,可调节透明度 <?phpclass Imgshuiyin{ /* 缩略图相关常量定义 */ const THU ...
- 工作学习2-gcc升级引发的崩溃
分享一下调查gcc 8.0下,函数漏写返回值崩溃问题,调查记录. 现在新的硬件,基本操作系统都是redhat 8.0,升级后测试时,发现了一个崩溃问题,记录一下. ================== ...
- Android editttext只能输入不能删除(选中后被软键盘遮住)
感谢https://www.dutycode.com/post-20.html: 解决方法:在布局外外嵌一层scrollview.
- 巩固javaweb的第三十一天
巩固内容 变量的作用范围 如果要访问的信息在 pageScope.requestScope.sessionScope 和 applicationScope 中存储, 则使用表达式语言访问的时候可以直接 ...
- Slay 全场!Erda 首次亮相 GopherChina 大会
来源|尔达 Erda 公众号 相关视频:https://www.bilibili.com/video/BV1MV411x7Gm 2021 年 6 月 26 日,GopherChina 大会准时亮相北京 ...
- 如何将List集合中相同属性的对象合并
在实际的业务处理中,我们经常会碰到需要合并同一个集合内相同属性对象的情况,比如,同一个用户短时间内下的订单,我们需要将各个订单的金额合并成一个总金额.那么用lambda表达式和HashMap怎么分别处 ...
- spring jdbc 配置数据源连接数据库
概述 在XML配置数据源,并将数据源注册到JDBC模板 JDBC模板关联业务增删改查 在XML配置数据源 <?xml version="1.0" encoding=" ...
- MyBatis(2):CRUD操作
编写接口 import com.shandx.pojo.User; import java.util.List; public interface UserMapper { <span clas ...
- Oracle 表结构管理
表其实是数据的'容器'.oracle有几种类型的表: 普通表(ordinary table)又叫堆组织表. 聚簇表(clustered table) 分区表(partition table) 外部表( ...