面向对象编程(C++篇3)——析构
1. 概述
类的析构函数执行与构造函数相反的操作,当对象结束其生命周期,程序就会自动执行析构函数:
class ImageEx
{
public:
ImageEx()
{
cout << "Execute the constructor!" << endl;
}
~ImageEx()
{
cout << "Execute the destructor!" << endl;
}
};
int main()
{
ImageEx imageEx;
return 0;
}
那么同样的问题来了,为什么要有析构函数呢?
2. 详论
2.1. 对象生命周期
在经典C++中,需要通过new/delete来手动管理动态内存。如果我们在类中申请一个动态数组,并且通过自定义的函数Release()来释放它:
class ImageEx
{
public:
ImageEx()
{
cout << "Execute the constructor!" << endl;
data = new unsigned char[10];
}
~ImageEx()
{
cout << "Execute the destructor!" << endl;
}
void Release()
{
delete[] data;
data = nullptr;
}
private:
unsigned char * data;
};
int main()
{
{
ImageEx imageEx;
imageEx.Release();
}
return 0;
}
那么,当类对象离开作用域,结束生命周期之前,就必须显示调用一次成员函数Release(),否则就会造成内存泄漏:对象在调用析构函数之后,只会销毁数据成员data本身,而不是其指向的内存。
那么,一个合理的实现是,将成员函数Release()放入到析构函数:
class ImageEx
{
public:
ImageEx()
{
cout << "Execute the constructor!" << endl;
data = new unsigned char[10];
}
~ImageEx()
{
Release();
cout << "Execute the destructor!" << endl;
}
private:
unsigned char * data;
void Release()
{
delete[] data;
data = nullptr;
}
};
int main()
{
{
ImageEx imageEx;
}
return 0;
}
这样,当类对象离开作用域,结束生命周期之前,就自动通过析构函数,实现了动态数组的释放。好处是显而易见的:实现了类似于内置数据类型对象的生命周期管理,我们可以像使用内置数据类型对象一样使用类对象。这也体现了前文《面向对象编程(C++篇1)——引言》中提到的设计原则:类是抽象的自定义数据类型。
2.2. 不一定需要显式析构
在一些现代高级编程语言(C#、Java、Javascript)中,已经不用去手动管理动态内存,取而代之的,是其与操作系统的中间件(.net,jvm,浏览器)的GC(垃圾回收)机制。而在现代C++中,提倡通过智能指针(std::shared_ptr、std::unique_ptr、std::weak_ptr)来管理动态内存;对于动态数组,则使用标准容器std::vector则更好。在两者的内部都实现了前文提到的对象生命周期管理,在离开作用域后,通过析构函数自动释放管理的内存,无需再手动进行回收。
那么,一个显而易见的推论就出来了,如果我们在类中使用智能指针或者vector容器来替代new/delete管理动态内存,是不是就可以不用析构函数了?严格来说,是不用显式使用析构函数:
class ImageEx
{
public:
ImageEx():
data(10)
{
cout << "Execute the constructor!" << endl;
}
private:
std::vector<unsigned char> data;
};
int main()
{
ImageEx imageEx;
return 0;
}
实际上,并不是这个类不存在析构函数,而是编译器会为它生成一个合成的析构函数,在这个析构函数体中,什么也不用做。因为类中的动态内存,已经交由std::vector容器来管理。当类对象离开作用域调用析构函数之后,会销毁这个std::vector容器数据成员,进而触发其析构函数,释放其管理的内存。
2.3. 析构的必要性
根据上一节内容,不一定需要显式析构。因为现代C++的一些机制能够帮你自动管理动态内存。但是析构函数还是必要的,这是由于C++语言本身的性质决定的。作为C语言大部分内容的超集,需要兼容C语言手动管理内存的特性。更重要的是,现代操作系统几乎全部由C语言编写,与底层的交互不可避免的需要手动使用动态内存管理。
3. 总结
所以我们就能理解了,C++这门语言的设计哲学就是就是这样:既想要C语言的高性能,也想要高级语言高度抽象的特性。如果我们必须兼容C语言底层设计,那我们最好使用析构函数释放动态内存;否则多数情况下,我们应该使用智能指针或者stl容器来管理动态内存,从而避免显示使用析构函数。
面向对象编程(C++篇3)——析构的更多相关文章
- 面向对象编程(C++篇4)——RAII
目录 1. 概述 2. 详论 2.1. 堆.栈.静态区 2.2. 手动管理资源的弊端 2.3. 间接使用 2.4. 自下而上的抽象 3. 总结 4. 参考 1. 概述 在前面两篇文章<面向对象编 ...
- Python 第六篇(中):面向对象编程中级篇
面向对象编程中级篇: 编程思想概述: 面向过程:根据业务逻辑从上到下写垒代码 #最low,淘汰 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 #混口饭吃 def add(ho ...
- 面向对象编程(C++篇2)——构造
目录 1. 引述 2. 详述 2.1. 数据类型初始化 2.2. 类初始化 1. 引述 在C++中,学习类的第一课往往就是构造函数.根据构造函数的定义,构造函数式是用于初始化类对象的数据成员的.无论何 ...
- 面向对象编程(C++篇1)——引言
目录 1. 概述 2. 详论 2.1. 类与对象 2.2. 数据类型 3. 目录 1. 概述 现代C++与最原始的版本已经差不多是两种不同的语言了.不断发展的C++标准给C++这门语言带来了更多的范式 ...
- Python 第六篇(上):面向对象编程初级篇
面向:过程.函数.对象: 面向过程:根据业务逻辑从上到下写垒代码! 面向过程的编程弊:每次调用的时候都的重写,代码特别长,代码重用性没有,每次增加新功能所有的代码都的修改!那有什么办法解决上面出现的弊 ...
- python - 面向对象编程(初级篇)
写了这么多python 代码,也常用的类和对象,这里准备系统的对python的面向对象编程做以下介绍. 面向对象编程(Object Oriented Programming,OOP,面向对象程序设计) ...
- 面向对象编程-终结篇 es6新增语法
各位,各位,终于把js完成了一个段落了,这次的章节一过我还没确定下面要学的内容可能是vue也可能是前后端交互,但无论是哪个都挺兴奋的,因为面临着终于可以做点看得过去的大点的案例项目了,先憋住激动地情绪 ...
- 洗礼灵魂,修炼python(41)--巩固篇—从游戏《绝地求生-大逃杀》中回顾面向对象编程
声明:本篇文章仅仅以游戏<绝地求生>作为一个参考话题来介绍面向对象编程,只是作为学术引用,其制作的非常简易的程序也不会作为商业用途,与蓝洞公司无关. <绝地求生>最近很火,笼络 ...
- 学习Pytbon第十七篇,面向对象编程
面向对象技术简介 类(Class): 用来描述具有相同的属性和方法的对象的集合.它定义了该集合中每个对象所共有的属性和方法.对象是类的实例. 类变量:类变量在整个实例化的对象中是公用的.类变量定义在类 ...
随机推荐
- 收集有用的 Javascript 片段
内容目录 数组 arrayMax arrayMin chunk compact countOccurrences deepFlatten difference distinctValuesOfArra ...
- nodejs process uncaughtException
用过Node一段时间之后,发现那些在事件主循环里碰到的异常会导致Node进程退出.在许多应用场景下,特别是对那些希望永不当机的服务器程序来说,这都是不接受的.uncaughtException事件会提 ...
- ARC和MRC兼容和转换
1.ARC模式下如何兼容非ARC的类 转变为非ARC -fno-objc-arc 转变为ARC的, -f-objc-arc (不常用) 2.如何将MRC转换为ARC
- 手势仿QQ侧滑---秀清
// // SlideViewController.h // qqcehua // // Created by 张秀清 on 15/5/25. // Copyright (c) 2015年 张秀清. ...
- VC709E 基于FMC接口的FPGA XC7VX690T PCIeX8 接口卡
一.板卡概述 本板卡基于Xilinx公司的FPGA XC7VX690T-FFG1761 芯片,支持PCIeX8.两组 64bit DDR3容量8GByte,HPC的FMC连接器,板卡支持各种FMC子卡 ...
- find 查询命令 & 正则表达式
今日内容 find 正则表达式 Linux 三剑客之 grep 内容详细 一.find 按名称或属性查询文件 按名称查询 find [查找目录] [参数] [] 通配符 : * 表示匹配多个字符 ? ...
- Solution -「POJ 3710」Christmas Game
\(\mathcal{Decription}\) Link. 定义一棵圣诞树: 是仙人掌. 不存在两个同一环上的点,度数均 \(\ge 3\). 给出 \(n\) 棵互不相关的圣诞树,双人 ...
- 【软件实施面试】MySQL和Oracle联合查询以及聚合函数面试总结
软件实施面试系列文章第二弹,MySQL和Oracle联合查询以及聚合函数的面试总结.放眼望去全是MySQL,就不能来点Oracle吗?之前面过不少公司,也做过不少笔试题,现在已经很少做笔试题了.你肚子 ...
- 本地虚拟机在NAT网络连接模式下如何设置才可以访问外网以及使用Xshell远程连接
本文演示环境: 笔记本电脑系统:windows 7 虚拟机系统:CentOS 7 虚拟化软件:VMware Workstation 12 远程连接工具:Xshell 5 第一步: 打开虚拟网络编辑器 ...
- Abp 异常处理
Abp 异常处理 最近一直在读代码整洁之道,我在读到第三章函数的3.9 使用异常替代返回错误码,其实在我的开发经历中都是使用返回错误码给到前端,之前在阅读ABP官网文档中就有看到过使用异常替代异常的做 ...