飞道的博客

虚析构和纯虚析构

430人阅读  评论(0)

导言

本文将介绍为什么要将多态性质基类析构函数声明为虚函数,以及什么是纯虚析构函数

virtual 析构函数

资源泄漏出现

有时我们希望借助父类指针释放子类对象::

class Person
{
   
public:
	Person();
    ~Person();
    ...
};

class Student : public Person
{
    ... };

// 我们用基类指针指向派生类对象并释放
Person* ptr = new Student;
...
delete ptr;

C++明确指出,当派生类对象经由一个基类指针删除,而该基类的析构函数并不是虚函数,其结果是未定义的。

  • 实际执行时通常发生的是对象的派生成分没被销毁
  • 也就是说声明于Student类内的成员变量没被销毁
  • 然而其基类成分会被销毁,于是造成了诡异的资源泄漏

解决

解决这个问题的方法很简单,只需要将基类的析构函数声明为虚函数即可。这样delete基类指针时,会销毁整个对象,包括所有的派生成分。

虚函数:即被virtual修饰的类成员函数称为虚函数。

class Person
{
   
public:
	Person();
    virtual ~Person();
    ...
};

class Student : public Person
{
    ... };

Person* ptr = new Student;
...
delete ptr;

此时,当调用析构函数时,首先会找到对象内的虚表指针,在虚表指针所指向的虚表中找到适当的函数指针并调用。

总结:

  1. 带多态性质的基类应该声明一个virtual 析构函数。如果基类带有任何virtual 函数,它就应该拥有一个virtual 析构函数
  2. 类的设计目的如果不是作为基类使用,或不是为了具备多态性,就不该声明为virtual 析构函数

pure virtual析构函数

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类,抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。

常规纯虚函数

因为包含纯虚函数的类不能实例化出对象,所以纯虚函数一般没有函数体。

  • 没有对象无法调用该纯虚函数,因此实现没有价值,通常不实现

或许你会想到用类类型的指针来调用,下面就来看一下这种方法:

class Animal
{
   
public:
    virtual void Run() = 0;
    void Fly();
};

void Animal::Run() {
    cout << "Run()" << endl; }
void Animal::Fly() {
    cout << "Fly()" << endl; }

Animal* ptr = nullptr;

ptr->Fly(); // 程序正常屏幕打印Fly()
ptr->Run();	// 程序崩溃屏幕不打印

为什么会出现上面的情况?普通函数和纯虚函数有什么不同吗?

这是因为虚函数的调用需要通过虚表指针,没有对象也就没有虚表指针,故上述代码会崩溃。

纯虚析构

有时我们需要一个抽象类,但又没有纯虚函数,该怎么办呢?由于抽象类总是被当作基类来使用,而基类又应该有个virtual 析构函数,并且由于纯虚函数会产生抽象类,因此我们可以:把希望成为抽象的那个类声明一个pure virtual 析构函数

class Animal
{
   
public:
    virtual ~Animal() = 0;
};

// 必须为这个pure virtual函数提供一份定义,否则连接器会报错
Animal::~Animal() {
   }

析构函数的运行逻辑是,最深层派生的呢个类其析构函数最先被调用,然后是其每一个基类的析构函数被调用。编译器会在Animal的派生类的析构函数中创建一个对~Animal的调用动作,所以必须为这个函数提供一份定义。否则,连接器会报错。


转载:https://blog.csdn.net/qq_40080842/article/details/125467491
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场