C++虚析构函数
2021/11/11 17:10:30
本文主要是介绍C++虚析构函数,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
link
-
在类中,构造函数用于初始化对象及相关操作,构造函数是不能声明为虚函数的,因为在执行构造函数前对象尚未完成创建,虚函数表尚不存在,此时就无法去查询虚函数表,因此也就无法得知该调用哪一个构造函数了。
-
析构函数则用于销毁对象时完成相应的资源释放工作,析构函数可以被声明为虚函数。我们先通过一个例子来说明析构函数声明为虚函数的必要性。
#include<iostream> using namespace std; class base { public: base(); ~base(); private: int * a; }; class derived: public base { public: derived(); ~derived(); private: int * b; }; base::base() { cout<<"base constructor!"<<endl; a = new int[10]; } base::~base() { cout<<"base destructor!"<<endl; delete[] a; } derived::derived() { cout<<"derived constructor!"<<endl; b = new int[1000]; } derived::~derived() { cout<<"derived destructor!"<<endl; delete[] b; } int main() { base* p; p = new derived; delete p; return 0; }
在本例中,派生类和基类中都分别定义了自己的构造函数和析构函数,它们各有一个 int 型指针成员变量,并且:
- 在基类的构造函数中,给指针变量 a 分配了 10 个 int 型空间,在基类的析构函数则用于将是将 a 所指向的空间释放掉;
- 在派生类的构造函数中,指针成员变量被分配了 1000 个整型空间,派生类的析构函数则是为了释放掉 b 指针所指向的存储空间。
在主函数中,我们创建一个基类类型的指针,指针指向一个派生类对象,之后释放 p 指针所指向的对象的存储空间。最后程序运行结果如下:
base constructor! derived constructor! base destructor!
当我们用 new 操作符创建一个派生类对象时会先调用基类构造函数,然后再调用派生类构造函数。观察程序运行结果,程序打印出了“base constructor!”这串字符,则说明基类的构造函数被调用了,之后又打印出了“derived constructor!”这串字符,同样地派生类的构造函数也被调用了,程序输出结果与我们料想的是一致的。
之后程序打印出了“base destructor!”字符串,这说明基类的析构函数被调用了,a 指针所指向的 10 个整型内存空间被释放了。但是之后却并未调用派生类的析构函数,这意味着 b 指针所指向的 1000 个整型存储空间没有被释放,如此一来就造成了内存泄露。
内存泄露问题肯定是我们程序设计人员需要极力避免的。本例中出现内存泄漏问题,是因为派生类的析构函数未被调用。为了解决这个问题,我们将基类的析构函数声明为虚函数,修改后基类的定义如下:
class base { public: base(); virtual ~base(); private: int * a; };
修改基类的定义后,程序运行结果如下:
base constructor! derived constructor! derived destructor! base destructor!
将基类的析构函数声明为虚函数之后,派生类的析构函数也自动成为虚析构函数。在主函数中,基类指针 p 指向的是派生类对象,当 delete 释放 p 指针所指向的存储空间时,会执行派生类的析构函数,派生类的析构函数执行完之后会紧接着执行基类的析构函数,以释放从基类继承过来的成员变量所消耗的资源。如此一来就不会存在内存泄漏问题了。
从此例中,我们很明显可以看出析构函数声明为虚函数的必要性。但是,如果不管三七二十一的将所有的基类的析构函数都声明为虚函数,这也是不合适的。通常来说,如果派生类中存在一个指向动态分配内存的成员变量,并且派生类的析构函数中定义了释放该动态分配内存的代码,则应该将基类的析构函数声明为虚函数。
这篇关于C++虚析构函数的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享