【C++】多态 ⑤ ( 虚析构函数 | 虚析构函数语法 | 虚析构函数意义 | 父类指针指向子类对象情况下父类和子类使用 virtual 虚析构函数 | 代码示例 )
  K1I6GvxBEuyy 2023年12月02日 24 0



文章目录

  • 一、虚析构函数
  • 1、构造函数不能是虚函数
  • 2、析构函数可以是虚函数
  • 3、虚析构函数语法
  • 4、虚析构函数意义
  • 二、代码示例 - 虚析构函数
  • 1、代码示例 - 没有使用虚析构函数导致子类析构函数无法调用
  • 2、代码示例 - 使用虚析构函数正确示例







一、虚析构函数




1、构造函数不能是虚函数



构造函数 不能定义为 虚函数 , 不能使用 virtual 关键字修饰 ;

如果要创建一个 子类的 实例对象 , 需要 从 该子类的 最上层的 父类开始 , 沿着继承路径 , 逐个调用 构造函数 ;



场景说明 : A 类 是基类 , B 类 继承 A 类 , C 类 继承 B 类 ;

如果要创建 C 类 的 实例对象 , 需要从 最顶层的 父类 A 类 开始 , 先调用 A 的构造函数 , 然后调用 B 的构造函数 , 最后调用 C 的构造函数 ;



参考 【C++】继承 ⑧ ( 继承 + 组合 模式的类对象 构造函数 和 析构函数 调用规则 ) 博客中 , 构造函数 / 析构函数 调用策略 , 在 继承 + 组合 的情况下 , 构造函数 与 析构函数 调用规则如下 :

  • 构造函数 : 父类 -> 成员 -> 自身 ;
  • 首先 , 调用 父类 构造函数 ;
  • 然后 , 调用 成员 构造函数 ; 也就是 成员变量 类型的 构造函数 ;
  • 最后 , 调用 自己 构造函数 ; 自身定义的 构造函数 ;
  • 析构函数 : 自身 -> 成员 -> 父类 ;
  • 首先 , 调用 自己 析构函数 ; 自身定义的 析构函数 ;
  • 然后 , 调用 成员 析构函数 ; 也就是 成员变量 类型的 析构函数 ;
  • 最后 , 调用 父类 析构函数 ;


2、析构函数可以是虚函数



析构函数 可以是 虚函数 ;

虚析构函数 的 主要作用是 , 引导 delete 预算符 释放 动态对象 ;

虚析构函数 Virtual Destructor 是特殊的 析构函数 , 该函数用于在 子类 中覆盖 父类 的 析构函数 ;



场景说明 : A 类 是基类 , B 类 继承 A 类 ;

声明一个 A 类型 的指针变量 , 为其赋值 B 类型对象的地址 , 当需要释放该指针变量时 , 使用 delete 释放 A 类型指针指向的对象 ;

由于 A 类型指针指向的 是一个 B 类型的对象 , 对象的实际类型是 B 类型 , 而且 B 是 A 的子类 , 其成员多余 A , 因此必须调用 B 类型的 析构函数 ;

释放 A 类型的指针 , 需要调用其子类 B 类型对象的 析构函数 , 此时需要将 A 类型 和 B 类型的 析构函数 声明为 虚析构函数 ;



3、虚析构函数语法



虚析构函数 的 语法 是 在 父类 中使用 virtual 关键字 来声明 析构函数 ;

子类中 也要 使用 virtual 虚析构函数 ;

class Parent
{  
public:
	// 虚析构函数  
   	virtual ~Base() {}  
};



4、虚析构函数意义



父类中使用了 虚析构函数 , 在 子类 中 , 必须 覆盖 父类 的虚析构函数 , 并且使用相同的函数签名 ;

如果 子类 没有提供自己的 析构函数 , 则编译器会自动生成一个 析构函数 , 该 析构函数 会首先调用 父类 的 析构函数 , 然后执行 子类 的 析构函数 ;



使用 虚析构函数 的目的是 确保在释放 子类 对象时正确地释放资源和调用析构函数 ;

当使用 父类 指针指向一个 子类 对象时 , 如果要通过 delete 释放该指针指向的对象 ,

如果是正常的析构函数 , 没有 使用 virtual 定义虚析构函数 , 则只会调用 父类 的 析构函数 , 子类的析构函数不会被调用到 ;

虚析构函数 可以确保 首先调用 子类 的 析构函数 , 然后调用 父类 的析构函数 ;

这样可以 避免在 释放 子类对象 时出现 资源泄漏 的情况 ;



需要注意的是 , 只有在 父类 的析构函数是 虚函数 时 , 子类 的析构函数才必须是虚函数 ;

如果 父类 的 析构函数 不是 虚函数 , 则 子类 的 析构函数 可以是 普通的 非虚函数 ;






二、代码示例 - 虚析构函数




1、代码示例 - 没有使用虚析构函数导致子类析构函数无法调用



在下面的代码中 ,

  • 声明 子类指针 指向 子类对象 , 释放 子类指针 时 先调用 子类析构函数 , 再调用父类析构函数 ;
  • 声明 父类指针 指向 子类对象 , 释放 父类指针 时 只调用 子类析构函数 ;


代码示例 :

#include "iostream"
using namespace std;

// 父类
class Parent {
public:
	~Parent()
	{
		cout << "调用父类 Parent 析构函数 " << endl;
	}
};

class Child : public Parent
{
public:
	~Child()
	{
		cout << "调用子类 Child 析构函数 " << endl;
	}
};


int main() {

	// 声明子类指针 指向 子类对象
	Child* child = new Child();
	// 释放时 先调用 子类析构函数 , 再调用父类析构函数
	delete child;

	// 声明父类指针 指向 子类对象
	Parent* parent = new Child();
	// 释放时 只调用 子类析构函数
	delete parent;
	
	
	// 控制台暂停 , 按任意键继续向后执行
	system("pause");

	return 0;
}

执行结果 :

调用子类 Child 析构函数
调用父类 Parent 析构函数
调用父类 Parent 析构函数
请按任意键继续. . .

【C++】多态 ⑤ ( 虚析构函数 | 虚析构函数语法 | 虚析构函数意义 | 父类指针指向子类对象情况下父类和子类使用 virtual 虚析构函数 | 代码示例 )_开发语言



2、代码示例 - 使用虚析构函数正确示例



在下面的代码中 , 将 父类 和 子类 的析构函数 , 都使用 virtual 关键字修饰 ;

  • 声明 子类指针 指向 子类对象 , 释放 子类指针 时 先调用 子类析构函数 , 再调用父类析构函数 ;
  • 声明 父类指针 指向 子类对象 , 释放 父类指针 时 先调用 子类析构函数 , 再调用父类析构函数 ;


代码示例 :

#include "iostream"
using namespace std;

// 父类
class Parent {
public:
	virtual ~Parent()
	{
		cout << "调用父类 Parent 析构函数 " << endl;
	}
};

class Child : public Parent
{
public:
	virtual ~Child()
	{
		cout << "调用子类 Child 析构函数 " << endl;
	}
};


int main() {

	// 声明子类指针 指向 子类对象
	Child* child = new Child();
	// 释放时 先调用 子类析构函数 , 再调用父类析构函数
	delete child;

	// 声明父类指针 指向 子类对象
	Parent* parent = new Child();
	// 释放时 只调用 子类析构函数
	delete parent;
	
	
	// 控制台暂停 , 按任意键继续向后执行
	system("pause");

	return 0;
}

执行结果 :

调用子类 Child 析构函数
调用父类 Parent 析构函数
调用子类 Child 析构函数
调用父类 Parent 析构函数
请按任意键继续. . .

【C++】多态 ⑤ ( 虚析构函数 | 虚析构函数语法 | 虚析构函数意义 | 父类指针指向子类对象情况下父类和子类使用 virtual 虚析构函数 | 代码示例 )_c++_02


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年12月02日 0

暂无评论

推荐阅读
K1I6GvxBEuyy
作者其他文章 更多