当前课程知识点:C++语言程序设计进阶 >  第八章 多态性 >  虚函数 >  虚析构函数

返回《C++语言程序设计进阶》慕课在线视频课程列表

虚析构函数在线视频

虚析构函数

虚析构函数

为什么需要虚析构函数? - 可能通过基类指针删除派生类对象; - 如果你打算允许其他人通过基类指针调用对象的析构函数(通过delete这样做是正常的),就需要让基类的析构函数成为虚函数,否则执行delete的结果是不确定的。

一个不使用虚析构函数的例子

源代码:

#include <iostream>
using namespace std; 
class Base {
public:
    ~Base(); //不是虚函数
};
Base::~Base() {
    cout<< "Base destructor" << endl;
 }
class Derived: public Base{
public:
    Derived();
    ~Derived(); //不是虚函数
private:
    int *p;
};

#include <iostream>
using namespace std;
class Base {
public:
    virtual ~Base();
};
Base::~Base() {
    cout<< "Base destructor" << endl;
 }
class Derived: public Base{
public:
    Derived();
    virtual ~Derived();
private:
    int *p;
};



下一节:虚表与动态绑定

返回《C++语言程序设计进阶》慕课在线视频列表

虚析构函数课程教案、知识点、字幕

大家好

欢迎回来继续学习

C++语言程序设计

除了类的一般成员函数

可以是虚函数以外

析构函数也可以是虚函数

那么什么情况下

我们需要将析构函数

写成虚函数呢

如果你打算允许

其他的人

其他的程序代码通过基类的指针

去调用

派生类对象的析构函数

那就需要让基类的虚构函数

成为虚函数

比如说

我们用delete去释放空间的时候

就有可能出现这样的问题

那么在这种情况下

你不用虚函数的话

那么这个析构的过程呢

结果可能就是不确定的

大家可能还不太理解这句话

接下来我通过一个例子来演示

大家就清楚了

现在我们通过这个例子来看看

首先我们将析构函数

不写成虚函数

大家看

这是基类的虚构函数

这是继承了Base类的Derived类

派生类的析构函数

都不是虚函数

然后我们故意来制造一个错误

大家看看

有可能发生什么样的错误

那我们来看

在这些析构函数里头

都做了什么事情

在这个Derived析构函数中

它去释放了

一个指针指向的空间

那么这个指针指向的空间

是哪儿来的呢

看看

它是在构造函数中

通过动态内存分配

分配了一个整数

然后把这个整数的起始地址

复给了它的数据成员p

比如我们看这个Derived类

它有一个指针成员

这是关键

而这个指针成员呢

它所指向的空间

是什么呢

是在构造函数中

动态分配的内存空间

也就是说

指针p所得到的这个地址

是由动态内存分配得到的

我们知道在构造函数中

如果做了这种动态内存分配

对应的在析构函数中

一般都是对应在析构函数中

要去释放它

也就是说

当这个对象要消亡的时候

因它的存在而占用的资源

就应该释放

如果不释放

那么就会造成资源泄漏

比如说

这个整数的内存单元如果不释放

那么就造成了内存泄漏

所以

当我们释放Derived对象的时候

要执行析构函数

必须要执行析构函数

是很重要的

那么大家想

一个对象当它消亡的时候

析构函数是必然被执行的呀

还会有什么情况

说漏掉了

没执行它的析构函数吗

还真有可能有这种情况

我们来看

这里我们定义一个函数fun

在这个函数的参数中呢

它接收一个基类指针

其实希望用这个函数

能够进行统一的对象的释放

好 我们在主函数中

通过new

去构造了一个Derived对象

然后将这个对象的地址

传给了fun函数

由fun函数去释放它

由于这个时候析构函数

它不是虚函数

所以编译器在编译阶段

就要决定释放

删除这个指针b

所指向的空间的时候

释放这个空间的时候

应该调用哪个对象的析构函数

在静态绑定的情况下

它只能知道b是基类指针

只能知道这一点点信息

所以它只知道要调用

Base类的析构函数

所以我们看

只有Base类的析构函数运行了

而Derived的析构函数呢

根本有没有被运行

这样的话

我们用new分配的空间

就没有被释放

所以这就会造成内存泄漏

怎么办呢

也很简单

我们在析构函数之前

都加上这个virtual就行了

把它定义成虚析构函数

定义成虚析构函数以后呢

进行同样地测试

在这个地方编译器

就不会在编译的时候去确定

该调用谁的析构函数了

就会等到运行时

根据它指针b

所指向的实际对象

确定该调用谁的析构函数

当然这次它的实际指向的对象

是Derived对象

虽然指针类型是Base类型

但是对象是Derived对象

那这样的话呢

就会去调用Derived的构造函数

就调用这个构造函数

所以我们看到

Derived destructor

这个输出显示在这儿

在这个构造函数的函数体中呢

就会释放动态分配的这个整数

它所占用的内存空间

释放这个空间

就把Derived对象

存续期间的资源释放了

当然

在每个派生类它的析构函数

执行完之后

紧接着会自动调用

它基类的析构函数

所以这个时候呢

Base类的析构函数

依然是会调用的

所以我们看到

Base destructor

在这儿显示

也就是进到这个函数里面了

所以我们在很多情况下

都是需要写虚析构函数的

C++语言程序设计进阶课程列表:

第七章 继承与派生

-导学

--导学

-继承的基本概念和语法

--继承的基本概念和语法

-第七章 继承与派生--继承的基本概念和语法习题

-继承方式

--继承方式简介及公有继承

--私有继承和保护继承

-第七章 继承与派生--继承方式

-基类与派生类类型转换

--基类与派生类类型转换

-第七章 继承与派生--基类与派生类类型转换

-派生类的构造和析构

--派生类的构造函数

--派生类的构造函数举例

--派生类的复制构造函数

--派生类的析构函数

--第七章 继承与派生--派生类的构造和析构

-派生类成员的标识与访问

--访问从基类继承的成员

--虚基类

-第七章 继承与派生--派生类成员的标识与访问

-小结

--小结

-综合实例

--第七章综合实例

-实验七

--实验七

-第七章讲义

第八章 多态性

-导学

--导学

-第八章 多态性--导学

-运算符重载

--运算符重载的规则

--双目运算符重载为成员函数

--单目运算符重载为成员函数

--运算符重载为非成员函数

-第八章 多态性--运算符重载

-虚函数

--虚函数

--虚析构函数

--虚表与动态绑定

-第八章 多态性--虚函数

-抽象类

--抽象类

--第八章 多态性--抽象类

-override与final

--override与final

-第八章 多态性--override与final

-小结

--第八章小结

-综合实例

--第八章综合实例

-实验八

--实验八

- 第八章讲义

第九章 模板与群体数据

-导学

--导学

-模板

--函数模板

--类模板

-第九章 模板与群体数据--模板

-线性群体

--线性群体的概念

-第九章 模板与群体数据--线性群体

-数组

--数组类模板

--例9-4数组类应用举例

-链表

--链表的概念与结点类模板

--链表类模板

-第九章 模板与群体数据--链表

-栈

--栈类模板

--栈类模板课后习题

--例9-9 栈的应用

--例9-9 栈的应用课后习题

-队列

--队列类模板

-第九章 模板与群体数据--队列

-排序

--排序概述

--插入排序

--选择排序

--交换排序

-第九章 模板与群体数据--排序

-查找

--查找

--查找课后习题

-小结

--小结

-综合实例

--综合实例

-实验九

--实验九

- 第九章讲义

第十章 泛型程序设计与C++标准模板库

-导学

--导学

-泛型程序设计及STL的结构

--泛型程序设计的基本概念

--STL简介

-第十章 泛型程序设计与C++标准模板库--泛型程序设计及STL的结构

-迭代器

--迭代器

-第十章 泛型程序设计与C++标准模板库--迭代器

-容器的基本功能与分类

--容器的基本功能与分类

-第十章 泛型程序设计与C++标准模板库--容器的基本功能与分类

-顺序容器

--顺序容器的基本功能

--顺序容器的特征

--顺序容器的插入迭代器与适配器

--第十章 泛型程序设计与C++标准模板库--顺序容器

-关联容器

--关联容器分类和基本功能

--集合

--映射

--多重集合和多重映射

-第十章 泛型程序设计与C++标准模板库--关联容器

-函数对象

--函数对象

--函数适配器

-算法

--算法

-小结

--第十章小结

-综合实例

--综合实例

-实验十

--实验十

- 第十章讲义

第十一章 流类库与输入/输出

-导学

--导学

-I/O流的概念及流类库结构

--I/O流的概念及流类库结构

-第十一章 流类库与输入/输出--I/O流的概念及流类库结构

-输出流

--输出流概述

--向文本文件输出

--向二进制文件输出

--向字符串输出

-第十一章 流类库与输入/输出--输出流

-输入流

--输入流概述

--输入流应用举例

--从字符串输入

-第十一章 流类库与输入/输出--输入流

-输入/输出流

--输入/输出流

-第十一章 流类库与输入/输出--输入/输出流

-小结

--小结

-综合实例

--综合实例

-实验十一

--实验十一

- 第十一章讲义

第十二章 异常处理

-导学

--第12章导学

-异常处理的思想与程序实现

--异常处理的思想与程序实现

-第十二章 异常处理--异常处理的思想与程序实现

-异常处理中的构造与析构

--异常处理中的构造与析构

-第十二章 异常处理--异常处理中的构造与析构

-标准程序库异常处理

--标准程序库异常处理

-第十二章 异常处理--标准程序库异常处理

-小结

--第12章小结

-综合实例

--综合实例

-实验十二

--实验十二

- 第十二章讲义

虚析构函数笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。