C++中有的类的析构函数为什么要定义成虚函数?
source link: https://blog.popkx.com/why-some-deconstruct-functions-are-defined-as-virtual/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
C++中有的类的析构函数为什么要定义成虚函数?
在阅读C++项目(caffe)源码时,发现不少基类不仅把常规的成员函数定义成虚函数(virtual),也会把析构函数定义为虚函数,结合前面几节的介绍,稍稍思考下,这样做的确是有原因的,本文将结合C++代码实例尝试探讨下。
随便写一段C++代码作为实例,在这个例子中,我们先不把析构函数定义为虚函数:
class Base {
public:
Base () {
cout << "Base construct\n";
}
~Base() {
cout << "Base deconstruct\n";
}
virtual void foo() {
cout << "Base::foo\n";
}
char *buf;
};
class Child: public Base {
public:
Child() {
cout << "Child construct\n";
buf = new char[16];
}
~Child() {
delete[] buf;
cout << "Child deconstruct, delete buf\n";
}
void foo() {
buf[0] = 3;
cout << "Child::foo\n";
}
};
这段代码的逻辑很简单,无非就是定义了两个类:类 Base 的成员函数 foo() 为虚函数,构造函数和析构函数都是常规函数,此外它还有个 public 的成员变量 buf。类 Child 则公开继承了 Base,因此它可以直接使用 Base::buf——在构造函数中 new
了一段内存,并且在析构函数 delete
掉它。
Child c;
c.foo();
我们直接使用 Child 实例化一个对象 c,调用 c.foo(),此时得到如下输出:
Base construct
Child construct
Child::foo
Child deconstruct, delete buf
Base deconstruct
一切尽在预料中。
不安全的问题
虽说对象 c 调用 foo() 的输出完全符合预计,但像上面那样定义类仍然是非常危险的做法。在这一节我们曾讨论过,父类指针可以调用派生类的重写函数,因此下面这两行C++代码也是合法的,请看:
Base *pb = new Child();
pb->foo();
delete pb;
编译这段C++代码完全没有问题,运行也不会报错,输出如下:
Base construct
Child construct
Child::foo
Base deconstruct
可是,从输出信息能够看出,派生类 Child 的析构函数没有被调用,对于本例而言,new
出来的 buf 没有对应的 delete
,势必会造成内存泄漏。
要解决所谓的“不安全问题”,其实很简单,按照题目说的做——将基类的析构函数也定义为虚函数就可以了,请看修改后的C++代码:
class Base {
public:
Base () {
cout << "Base construct\n";
}
virtual ~Base() {
cout << "Base deconstruct\n";
}
...
也即尽在基类 Base 的析构函数前加上 virtual 关键字,其他的所有代码都无需改动。现在再执行下面的这几行C++代码:
Base *pb = new Child();
pb->foo();
delete pb;
输出如下:
Base construct
Child construct
Child::foo
Child deconstruct, delete buf
Base deconstruct
显然,此时派生类 Child 的析构函数也会被调用了,内存泄漏的问题倍解决了。
C++ 中的 virtual 关键字是非常好用,也是C++程序员必须掌握的关键字,其实,“不安全问题”出现的原因也是简单的:我们在静态类型与动态绑定一节中提到过,基本上只有涉及到 virtual 函数时,才会发生动态绑定,此时通过对象指针(pb)调用的函数由它指向的类(Child)决定,所以此时派生类 Child 的析构函数会被调用。如果基类 Base 的析构函数不是虚函数,那么对象指针(pb)调用的函数由其静态类型(Base)决定,也即调用的其实只是基类 Base 的析构函数而已。
Recommend
-
38
重要说明 本文中提到的构造函数,在很多书中有其他的说法,如构造器,构造方法,初始化,初始函数等 本文中提到的析构函数,在很多书中有其他的说法,如反构造器,析构方法,反初始化,反初始函...
-
47
-
8
为什么shared_ptr和unique_ptr会发生不同的析构行为? - 知乎登录一下,更多精彩内容等你发现贡献精彩回答,参与评论互动查看全部 6 个回答...
-
4
构造、析构期间被调虚函数发生的惨案 2020.05.30 Netcan 编...
-
8
V2EX › Python Python 类的析构函数的一些疑问,析构到底是和类绑定一起释放还是类释放以后调用的函数。
-
7
V2EX › 程序员 js 的类有没有析构函数,如何进行资源释放?? James369 · 1 天前...
-
2
dotnet 在析构函数调用 ThreadLocal 也许会抛出对方已释放 我在不自量力做一个数组池,就是为了减少使用 System.Buffers.dll 程序集,然而在数组池里面,所用的 ThreadLocal 类型,在我对象析构函数进行归还数组时,抛出了无法访问已释放对象...
-
5
C++类的析构函数什么情况下要写?不写析构函数好像也没事啊,系统都会自动生成,用delete()方法也能释放掉用new方法创建的对象内存,那么在什么情况下才要写析构函数呢?
-
4
C++——构造函数和析构函数 精选 原创 萌新的日常 2022-12-21 08:06:44...
-
6
2023-05-26:golang关于垃圾回收和析构函数的选择题,多数人会选错。 ...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK