博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++学习之虚析构函数
阅读量:4685 次
发布时间:2019-06-09

本文共 2344 字,大约阅读时间需要 7 分钟。

什么样的情况下才需要虚析构函数?

类需要控制自己的对象执行一系列操作时发生什么样的行为,这些操作包括:创建(对象)、拷贝、移动、赋值和销毁。在继承体系中,如果一个类(基类或其派生的类)没有定义拷贝控制操作,则编译器将自动的为其合成一个。即为合成的拷贝控制

基类拷贝控制中,由于继承关系导致的最大影响就是:基类通常应该定义一个‘虚析构函数’。用以动态的分配继承体系中的对象。

如:类A,B,C,D有如下继承关系(代码1):

1
2
3
4
class
A;
class
B:
public
A;
class
C:
public
B;
class
D:
public
C;

其中:类A定义如下(代码2):

1
2
3
4
5
class
A {
public
:
    
//其他函数
    
virtual ~A()=
default
;
//用于动态绑定的析构函数

};

当我们delete一个A* item 类型的指针时,该指针可能是指向A的,也可能指向的是B,C,D中的一个,编译器在delete时必须弄清楚到底应该执行A,B,C,D中哪一个类的析构函数。此时需要编译器进行动态绑定(即只有运行时才能知道到底item 指向的是那个类)。当在基类A中定义的析构函数为虚析构函数时,无论A的派生类(B,C,D)使用的是合成的析构函数还是自己定义的析构函数,它们都是虚析构函数。说人话就是:你老祖姓虚,传到你还是姓虚,你儿子孙子都得姓虚(千万别较真女生~~~),不管这儿孙是你血缘的还是你自己领养的,都得虚!

举个例子(代码3):

1
2
3
4
A *item =
new
A; 
//此时item指向的就是A,静态类型于动态类型一致(这就是你本人)
delete
item; 
//调用A自己的析构函数(自杀了,杀的是你自己)
item =
new
B; 
//静态类型为A,动态类型为B(此时你的血脉传到了你儿子身上,item是你儿子了!)
delete
item;  
//调用B自己的析构函数(你儿子要自杀,此时死的是你儿子,和你无关)

如果基类A的析构函数不是虚的(虚函数),则delete时,如果item指向的不是A,而是B或其他A的派生类,则会产生未定义的行为,未定义的行为通常会导致BUG。

那么问题来了:什么样的情况下才需要虚析构函数呢?是所有类都应该有吗?

通过基类的指针删除派生类的对象时,基类的析构函数应该是虚的。否则其删除效果将无法实现。

简单解释一下,派生类B中所有的属性以操作(Bp)不仅有B自己定义的属性、操作(Bself),还有继承自A的属性、操作(Aself),即Bp=Bself+Aself;

如代码3,当delete一个指向B的item时(其实item的类类型为A),如果A中的析构函数不是虚的,则只会删除Aself部分,因为item的类类型其实是A,只是指向了其派生类对象。但是在A的析构函数里其实并没有Bself部分,那这部分就删不掉了--这就是所谓的内存泄漏!只有A的析构函数是虚的,才能删除的不仅有Aself,还有Bself,即Bp全部被删除了。这才是正确的。

同时,并不是所有类都需要将析构函数定义成虚的。因为编译器在编译时会给类添加一个虚函数表,里面来存放虚函数指针,如果都定义成虚的,这样就会增加类的存储空间。浪费了!不用作基类,也不需要为虚的!不需要通过基类的指针操作派生类的对象时,基类的析构函数应该是虚的。

这里借用一下文章代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class
ClxBase
{
public
:
     
ClxBase() {};
     
virtual ~ClxBase() {};
     
virtual
void
DoSomething() { cout <<
"Do something in class ClxBase!"
<< endl; };
};
class
ClxDerived :
public
ClxBase
{
public
:
     
ClxDerived() {};
     
~ClxDerived() { cout <<
"Output from the destructor of class ClxDerived!"
<< endl; };
     
void
DoSomething() { cout <<
"Do something in class ClxDerived!"
<< endl; };
};
     
代码
ClxBase *pTest =
new
ClxDerived;
pTest->DoSomething();
delete
pTest;

正常情况应该输出:

1
2
Do something
in
class
ClxDerived!
Output from the destructor of
class
ClxDerived!

如果将类ClxBase的析构函数定义为非虚(去掉前面的那个virtual),则输出为:

1
Do something
in
class
ClxDerived!

根本没有调用ClxDerived的析构函数哦~~~

同样,在中,提出了一个这样的问题:

为什么继承一个没有虚析构函数的类是危险的?

这个问题吗其实上面已经解释过了,会导致删不完!内存泄漏问题。当你公有继承创建一个从基类继承的相关类时,指向新类对象中的指针和引用实际上都指向了起源的对象。因为析构函数不是虚函数,所以当你delete一个这样的类时,C++就不会调用析构函数链。

 

转载于:https://www.cnblogs.com/lomper/p/4096118.html

你可能感兴趣的文章
kafka中的消费组
查看>>
python--注释
查看>>
前端资源链接 ...
查看>>
yum install ntp 报错:Error: Package: ntp-4.2.6p5-25.el7.centos.2.x86_64 (base)
查看>>
leetcode-Single Number-136
查看>>
CF715C Digit Tree
查看>>
二分法练习1
查看>>
QT 制作串口调试小助手----(小白篇)
查看>>
前端MVC实践之hellorocket——by张舒彤
查看>>
OptimalSolution(2)--二叉树问题(3)Path路径问题
查看>>
IPC 之 Messenger 的使用
查看>>
macos 下usb键盘问题.
查看>>
SQL函数学习(十六):STUFF()函数
查看>>
Apache Hadoop 和Hadoop生态圈
查看>>
Ctrl+Enter 选中文本提交
查看>>
android WIFI
查看>>
常用的匹配正则表达式和实例
查看>>
小组成员及其git链接
查看>>
SQL case when else
查看>>
MVc Identity登陆锁定
查看>>