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)模板多态:参数多态, ...
随机推荐
- 洛谷 P5897 - [IOI2013]wombats(决策单调性优化 dp+线段树分块)
题面传送门 首先注意到这次行数与列数不同阶,列数只有 \(200\),而行数高达 \(5000\),因此可以考虑以行为下标建线段树,线段树上每个区间 \([l,r]\) 开一个 \(200\times ...
- python-3.x- 序列操作
1. list操作 A.添加元素 1 list = ["C++","C", "Java", "Python"] 2 &q ...
- MPI 学习笔记
目录 MPI学习笔记 MPI准备 概述 前置知识补充 环境部署 1.修改IP及主机名 2.关闭防火墙 3.实现免密码SSH登录 4.配置MPI运行环境 5.测试 程序的执行 编译语句 运行语句 MPI ...
- Spark(九)【RDD的分区和自定义Partitioner】
目录 spark的分区 一. Hash分区 二. Ranger分区 三. 自定义Partitioner 案例 spark的分区 Spark目前支持Hash分区和Range分区,用户也可以自定义分区 ...
- 从源码看Thread&ThreadLocal&ThreadLocalMap的关系与原理
1.三者的之间的关系 ThreadLocalMap是Thread类的成员变量threadLocals,一个线程拥有一个ThreadLocalMap,一个ThreadLocalMap可以有多个Threa ...
- 【区间dp】- P1880 [NOI1995] 石子合并
记录一下第一道ac的区间dp 题目:P1880 [NOI1995] 石子合并 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 代码: #include <iostream> ...
- Can we use function on left side of an expression in C and C++?
In C, it might not be possible to have function names on left side of an expression, but it's possib ...
- 最小化安装centos ubuntu基础命令
# yum install vim iotop bc gcc gcc-c++ glibc glibc-devel pcre \ pcre-devel openssl openssl-devel zip ...
- 解决Spring MVC @ResponseBody出现问号乱码问题
原因是SpringMVC的@ResponseBody使用的默认处理字符串编码为ISO-8859-1,而我们前台或者客户端的编码一般是UTF-8或者GBK.现将解决方法分享如下! 第一种方法: 对于需要 ...
- Pycharm, 添加requirements.txt
引用:https://www.jetbrains.com/help/pycharm/managing-dependencies.html 1) 2) 3)