首页 > 编程知识 正文

举例说明虚析构函数的作用,析构函数 虚函数

时间:2023-05-04 23:06:57 阅读:27199 作者:4843

一、动态绑定(多态性) )

(一)动态绑定(多态性)的实现条件

1 .从指针调用函数

2 .虚函数

3 .必须用广播(即基类指针)指向子类中的对象

(二)动态绑定实现过程

1 .首先,介绍类对象的内存区域

如果此类具有虚函数,则内存空间将包含虚指针。 虚拟指针指向保存虚拟函数地址的内存空间。

2 .向上转换时的A* a=new B (),具体发生了什么?

1 )关于存储器,首先明确了存在一个存储器区域,即new B ) )的情况下产生。 a也指向这个空间。 因此,在这种情况下,为a的虚表即new B ) )的虚表。

2 )函数调用权。 首先,由于b类是a类的子类,因此b类不仅继承了a类所有成员变量和成员函数的调用权,而且还包含许多:

a .添加一些成员变量

b .添加一些成员函数(虚函数或非虚函数) ) )。

c.a类中虚函数的实现

由于上转换,目标A* a无法访问新添加的A、B内容。

3 .动态绑定的效果

假设a类有虚函数virtual void play (; 然后,b类重载该函数。 中的a-play ) ),则编译器将play ) )确定为虚函数,并将其移动到虚表中以查找其地址,因此最终调用b类重载的play ) )函数。

这个过程就是动态绑定(多态性)。

如果调用非虚函数,则不会在虚表中搜索。

二、基类析构函数必须是虚函数

why? 参考:基类析构函数为虚函数的原因

首先看代码:

A* a=new B (; 电传a; 编译器将delete操作分解为两部分。

1 )调用a类析构函数

2 )操作删除(a )调用系统的free函数,释放内存。

注意: A* a指向的内存空间是new B ()打开的块。 因此,在步骤2中,new B ) )回收打开的空间。

如果b类没有指针成员变量,则不需要在b析构函数中手动释放,因此不调用b析构函数也没有内存泄漏问题。

但是,如果b类具有指针成员变量,则必须在b析构函数中手动释放。 此时,如果不调用b的析构函数,将发生内存泄漏。

声明一:所有的析构函数,在编译器看来都是同一个函数名字,比如: destructor。参考:这里

声明二:调用子类的析构函数,编译器会在末尾自动调用基类的析构函数。

分两种情况讨论:

(一) a类析构函数不虚

delete操作查找析构函数。 此时,如果a的析构函数是非虚的话,就不寻找虚的表,而是直接调用~A (函数,之后的free函数也只是释放a类所占有的存储区域。 添加b类成员变量的空间不会释放,这会导致内存泄漏。

(二) a类析构函数为虚

delete操作查找析构函数。 此时,如果a的析构函数为虚,则在虚的表中查找。 能找到吗?

从声明中可以看出,我们看到的ab2个类的析构函数名称不同,但在编译器中是相同的。 所以,可以顺利地找到虚表中的析构函数(b类的)。

从声明2中可以看到,A B的两个析构函数都将被调用。

随后的free函数释放b类占用的内存空间。 这样,内存空间的释放就完成了。

结论:

1 .动态绑定必须用指针实现(引用也是指针,其基础用指针实现)参考:这里

2 .升级时,不会添加内存的第二个空间。 *a指向的内存空间是new B ()打开的块。

3 .基类析构函数必须是虚函数,否则可能导致内存泄漏。 (此处的内存泄漏意味着派生类b的成员变量new的空间未释放,而不是由A* a=new B ()分配的空间)

其他重要结论:

1 .构造函数、析构函数和复制构造函数不继承。 参考:派生类的复制构造函数怎么写?

2 .复制构造函数的参数必须是引用格式。 否则就会变成无限循环。

3. virtual和override只出现在函数声明中。 基类函数前面必须加virtual。 派生类函数可以不带有virtual。 如果希望派生类必须重载某个虚函数,并且担心忘记了自己重载的实现,请在函数声明的末尾加上override。 如果派生类中没有重载实现此函数,则会报告错误。

其他问题:

1 .为什么非伪析构函数会导致内存泄漏?

2 .基类虚拟析构函数只需要声明吗? 还需要实现吗? 除了声明,这样编译还会报告错误。 派生类的析构函数还需要声明实现吗? 默认可以吗? 是的,可以。

3 .系统的默认析构函数什么时候调用? 只是声明,还会调用默认析构函数吗? 不。 只要你声明,即使是空的也必须定义。 否则编译错误。

4. big 3,如果我自己定义,会不会产生默认值? 是的。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。