浅谈虚析构函数如何解决子类内存泄漏

引言

内存泄漏是指程序中有一部分内存未被释放,造成系统资源浪费的问题。子类内存泄漏是指在创建子类对象时,由于某些原因导致子类的内存未被释放,造成系统资源浪费的问题。如何使用虚析构函数解决子类内存泄漏问题。看很多博客其实大家都在说这个问题是什么,而没有说为什么,就像高中学化学,大学做实验一样,我也翻找了很多资料,发现看《汇编语言》才能更好的理解。我将在本文中尽量的详细讲解。

提出问题

何谓虚析构函数?

虚析构函数是指在类的析构函数中声明为 virtual 的析构函数。虚析构函数主要用于多态类的析构,即在删除多态类的对象时,能够保证多态类的析构函数和基类的析构函数都会被正确调用。
虚析构函数具有以下特点:
1.虚析构函数必须在类的析构函数中声明为 virtual。如果类中的析构函数不是虚析构函数,那么在删除多态类的对象时,只会调用基类的析构函数,而不会调用多态类的析构函数。这样会导致子类的内存无法被释放,从而导致内存泄漏。
2.虚析构函数不能被重载。由于在继承体系中,基类析构函数和派生类析构函数的名称都是相同的,因此如果虚析构函数被重载,编译器将无法判断在删除多态类的对象时应该调用哪一个析构函数,从而导致程序出错。

什么是内存泄漏?

子类内存泄漏是指在创建子类对象时,由于某些原因导致子类的内存未被释放,造成系统资源浪费的问题。常见的原因包括:
1.子类中有动态分配的内存,但在析构函数中没有释放。
2.子类中的某些数据成员指向了外部资源,但在析构函数中没有释放。
3.子类继承了父类,但父类的析构函数不是虚函数,导致子类内存无法被释放。

子类内存泄漏会对系统造成严重后果,包括:
造成系统资源浪费,导致系统性能下降、导致程序崩溃等。

对提出问题做出演示

代码

#include<iostream>
using namespace std;

class Base {
public:
	Base() {
		cout << "Base的构造函数" << endl;
	}
	 ~Base() {
		cout << "Base的析构函数" << endl;
	}
	  void fun() {

	}
};
class Son :public Base {
public:
	Son() {
		cout << "Son类的构造函数" << endl;
	}
	 ~Son() {
		cout << "Son 类的析构函数" << endl;
	}
	void fun() {
		cout << "Son" << endl;
	}
};
int main() {
	Base* base = new Son;
	delete base;
}

运行结果
结果
从运行结果看:父类指针指向子类对象,只有父类的析构函数被调用,而子类却没有,这会导致刚刚在堆区创建的子类内存没有释放掉。
那么如何解决这个问题呢?答案是只需声明父类的析构函数是“virtual。1

virtual ~Base() {
		cout << "Base的析构函数" << endl;
	}

在这里插入图片描述

为什么声明析构函数为virtual就可以解决子类内存泄露问题?

虚函数表

打开VS控制台查看类内存布局
在这里插入图片描述
发现声明父类析构函数声明为虚析构函数,父类和子类布局中会多出来一个隐形成员——虚函数表指针2
虚函数表指针是存储在类的实例中的,而不是在类的定义中,所以父类的虚函数表指针和构造函数、析构函数一样不会被继承(其实能不能继承是存争议性的)。

在这里插入图片描述
写到这里又有疑问了,不继承虚函数表指针,那怎么当父类指针被delete时,又是如何调起子类的析构函数呢?。在这里插入图片描述
其实基类虚析构函数的虚函数指针指向的虚函数表里面多了一个可以访问子类析构函数的地址。这样我们就可以使得的基类指针指向子类时,仍然可以调用子类析构函数。

涉及**多态**的特殊规则:对一个类层次而言,虽然~Base和~Son是不同名的函数,但是在多态意义上是同一个函数。因此,virtual ~Base意味指向Son对象的Base指针(或引用)在析构时将调用~Son。所有的函数中,这是唯一的特例。这是析构函数的特殊性与多态机制结合的产物。

如果基类的析构函数被声明为虚函数,则基类的虚函数表中会多一个指向析构函数的函数指针。当创建派生类的对象时,派生类的虚函数表会复制基类的虚函数表,并在复制的基础上添加派生类特有的虚函数。如果派生类重写了基类的析构函数,则派生类的虚函数表中会多一个指向派生类的析构函数的函数指针。

提出疑问

1.为什么虚析构后就虚函数里面边多了一个析构函数的地址?
2.当普通指针指向一块内存时,delete该指针是可以释放掉指向的内存的,但在继承多态里就不行了
3.当delete父类指针时,父子类的虚构函数时如何通过delete机制被调用的。
4.上面的监视器截图中发现base指针的类型是Base{Son},我猜测是Base类型的,因为编译器会根据指针类型调用该类的析构函数,但Base{Son}是什么?

建议

想了解更多“为什么”,建议看《汇编语言》。


  1. 在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数,用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。 ↩︎

  2. _vfptr:虚函数表指针,v:virtual,f:function,ptr:pointer。 ↩︎

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值