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容器来管理动态内存,从而避免显示使用析构函数。

上一篇

目录

下一篇

最新文章

  1. Android之获取数据库路径
  2. MySQL语句执行顺序
  3. PMP 第一章 引论
  4. DataGridView 的单元格的边框、 网格线样式的设定【转】
  5. 对list集合中的对象按照对象的某一属性进行排序
  6. yii2.0配置以pathinfo的形式访问
  7. javascript Math.pow 函数 详解
  8. matlab 画框(二) 去白边
  9. Matlab神经网络工具箱学习之二
  10. c语言编程之队列(链表实现)
  11. USB相关知识
  12. C语言 goto, return等跳转
  13. [转载]Web前端和后端之区分,以及面临的挑战
  14. bzoj 3174: [Tjoi2013]拯救小矮人
  15. 使用FFmpeg捕获一帧摄像头图像
  16. Windows10家庭版运行应用提示”管理员已阻止你运行此应用...“的解决办法
  17. 【Hadoop篇】--Hadoop常用命令总结
  18. 一个极好的JavaScript学习网址
  19. ES6 WeakMap和WeakSet的使用场景
  20. 使用并发工具实现 RPC 调用流量控制

热门文章

  1. Maven 项目报出警告:Warning:java: source value 1.5 is obsolete and will be removed in a future release
  2. JS创建快捷方式
  3. iOS,开发准备之申请证书 ---by吴帮雷
  4. SpringBoot树获取方法总结
  5. 3.k8s核心概念
  6. Solution -「ABC 219H」Candles
  7. ASP.NET Core 6框架揭秘-实例演示版[持续更新中&hellip;]
  8. Spring Boot 启动特别慢的问题
  9. spring中容器和对象的创建流程
  10. python3爬取中国药学科学数据