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)模板多态:参数多态, ...
随机推荐
- WPS for Linux 字体配置(字体缺失解决办法)
WPS for Linux 字体配置(字体缺失解决办法) 1. 背景 有些linux装完wps后提示"部分字体无法显示"或"some formula symbols mi ...
- python12对象初
- python——关变量下划线叙述
_xx:前置单下划线,私有化属性或方法,一般来讲,变量名_xx被看作是"私有 的",在模块或类外不可以使用.当变量是私有的时候,用_xx 来表示变量是很好的习惯.类对象和子类可以访 ...
- (转载)Java里新建数组及ArrayList java不允许泛型数组
java中新建数组: String[] s;//定义的时候不需要设置大小 s = new String[5];//为数组分配空间时就要设置大小 对于ArrayList, ArrayList< ...
- 🚀 RabbitMQ课程发布-KuangStudy
RabbitMQ课程上线(44集) 视频教程地址:https://www.kuangstudy.com/course/detail/1323452886432944129 专栏地址:https://w ...
- flink-----实时项目---day06-------1. 获取窗口迟到的数据 2.双流join(inner join和left join(有点小问题)) 3 订单Join案例(订单数据接入到kafka,订单数据的join实现,订单数据和迟到数据join的实现)
1. 获取窗口迟到的数据 主要流程就是给迟到的数据打上标签,然后使用相应窗口流的实例调用sideOutputLateData(lateDataTag),从而获得窗口迟到的数据,进而进行相关的计算,具体 ...
- 求最长子序列(非连续)的STL方法 - 洛谷P1020 [NOIP1999 普及组] 导弹拦截
先给出例题:P1020 [NOIP1999 普及组] 导弹拦截 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 大佬题解:P1020 [NOIP1999 普及组] 导弹拦截 - 洛谷 ...
- 使用Rapidxml重建xml树
需求 : 重建一棵xml树, 在重建过程中对原来的标签进行一定的修改. 具体修改部分就不给出了, 这里只提供重建部分的代码 code : /****************************** ...
- haproxy动态增减主机与keepalived高级应用
一:本文将详细介绍haproxy的配置使用以及高级功能的使用,比如通过haproxy进行动态添加删除负载集群中的后端web服务器的指定主机,另外将详细介绍keepalived的详细配置方法.配置实例及 ...
- Cocoapods 版本更新与更新到指定版本
1.本地现有的Cocoapods的版本号是1.1.0.rc.2,想升级到最新版本 1.先切换gem源 gem sources --remove https://rubygems.org/ gem so ...