您现在的位置: 首页 > 网站导航收录 > 百科知识百科知识
C++中有些类的析构函数也被定义为虚函数,这样做有什么用?
函数,子类,代码C++中有些类的析构函数也被定义为虚函数,这样做有什么用?
发布时间:2019-02-08加入收藏来源:互联网点击:
C++中有些类的析构函数也被定义为虚函数,这样做有什么用?
回答于 2019-09-11 08:43:50
回答于 2019-09-11 08:43:50
在阅读C++项目(caffe)源码时,发现不少基类不仅把常规的成员函数定义成虚函数(virtual),也会把析构函数定义为虚函数,稍稍思考下,这样做的确是有原因的,本文将结合C++代码实例尝试探讨下。
常规
随便写一段C++代码作为实例,在这个例子中,我们先不把析构函数定义为虚函数:
这段代码的逻辑很简单,无非就是定义了两个类:类 Base 的成员函数 foo() 为虚函数,构造函数和析构函数都是常规函数,此外它还有个 public 的成员变量 buf。类 Child 则公开继承了 Base,因此它可以直接使用 Base::buf——在构造函数中 new了一段内存,并且在析构函数 delete 掉它。
Child c;
c.foo();
我们直接使用 Child 实例化一个对象 c,调用 c.foo(),此时得到如下输出:
一切尽在预料中。
不安全的问题
虽说对象 c 调用 foo() 的输出完全符合预计,但像上面那样定义类仍然是非常危险的做法。在这一节我们曾讨论过,父类指针可以调用派生类的重写函数,因此下面这两行C++代码也是合法的,请看:
编译这段C++代码完全没有问题,运行也不会报错,输出如下:
Base construct
Child construct
Child::foo
Base deconstruct
可是,从输出信息能够看出,派生类 Child 的析构函数没有被调用,对于本例而言,new 出来的 buf 没有对应的 delete,势必会造成内存泄漏。
解决问题
要解决所谓的“不安全问题”,其实很简单,按照题目说的做——将基类的析构函数也定义为虚函数就可以了,请看修改后的C++代码:
也即尽在基类 Base 的析构函数前加上 virtual 关键字,其他的所有代码都无需改动。现在再执行下面的这几行C++代码:
输出如下:
显然,此时派生类 Child 的析构函数也会被调用了,内存泄漏的问题倍解决了。
小结
C++ 中的 virtual 关键字是非常好用,也是C++程序员必须掌握的关键字,其实,“不安全问题”出现的原因也是简单的:我们在静态类型与动态绑定一节中提到过,基本上只有涉及到 virtual 函数时,才会发生动态绑定,此时通过对象指针(pb)调用的函数由它指向的类(Child)决定,所以此时派生类 Child 的析构函数会被调用。如果基类 Base 的析构函数不是虚函数,那么对象指针(pb)调用的函数由其静态类型(Base)决定,也即调用的其实只是基类 Base 的析构函数而已。
回答于 2019-09-11 08:43:50
面试中经常考察的一个知识点。涉及到类内部资源释放,子类基类的析构函数调用顺序等知识点。
子类基类析构函数调用顺序
当某个类D,派生于某个基类B的时候,释放类D的某个对象的时候,不仅仅会调用D的析构函数,还会自动调用B的析构函数。
当然,调用的顺序是先D后B,而且在执行D的析构函数的时候,D的对象,还是相对完整的一个状态,这时候,虚函数表还在正常工作的,也就是还可以调用虚函数。
子类资源释放
如果说通过B声明一个指针或者引用b,指向一个D的对象。那么通过b来释放资源的时候,也能够做到先调用D的析构函数,再调用B的析构函数的效果。
从而保证D和B两个类中涉及到的资源,都可以正常释放。
总结
所以说,虚析构函数的目的,只要是保证子类和基类的资源可以正常释放。
当然,如果某个类的析构函数,如果不是虚函数,那么在C++里面,就相当于告诉开发者,这个类不能派生子类。否则很容易出现资源未正常释放的问题。
上一篇:美国为什么要打伊朗?
下一篇:返回列表
相关链接 |
||
网友回复(共有 0 条回复) |