Tutorial Study Image

C++ 析构函数


2023年5月26日, Learn eTutorial
740

在本教程中,我们将学习 C++ 析构函数、虚析构函数以及纯虚析构函数。通过本教程,您还将清楚地了解析构函数何时被调用,以及它们的规则和规定。

C++ 中的析构函数

删除对象的成员函数称为析构函数。当对象离开其作用域时,编译器会自动调用析构函数。析构函数是特殊的成员函数,其行为与构造函数完全相反;构造函数初始化对象,而析构函数销毁(或删除)对象。

析构函数是构造函数的逆操作,其定义方式与构造函数类似。析构函数主要用于销毁类对象。在一个类中,它只能定义一次,并且必须与类同名。它与构造函数一样会自动调用。但在它前面会添加一个波浪号(~)。

当类的对象被销毁时,析构函数通常用于释放内存并为对象和类成员执行其他清理工作。当类对象超出其作用域或被有意消除时,就会调用析构函数。


~className{

    // Some code
};
 
注意:C++ 析构函数不允许有参数。此外,析构函数不能被修改。

示例 1


class X {
public:
  // Constructor for class X
  X();
  // Destructor for class X
  ~X();
};
 
  • 析构函数是与类同名的成员函数,并以 ~ (波浪号) 作为前缀。
  • 析构函数没有返回类型,也没有参数。你不能获取它的地址。析构函数不允许使用 const、volatile、const volatile 或 static 声明。析构函数可以是纯虚函数或声明为虚函数。
  • 如果一个类没有用户定义的析构函数,并且需要一个,编译器会隐式声明一个。这个隐式声明的析构函数是其类的公共成员,并且是内联的。

示例 2

编写一个 C++ 程序来理解析构函数的概念


#include<iostream>
using namespace std;

class Test
{
public:
  Test()
 {
   cout<<"\n Constructor executed";
 }

 ~Test()
  {
   cout<<"\n Destructor executed";
  }
};
main()
{
 Test t;
 return 0;
}

 

输出


Constructor executed
Destructor executed

C++ 中析构函数的属性

  • 当一个对象被销毁时,析构函数会自动调用。
  • 它不能是静态的或 const 的。
  • 析构函数没有参数。
  • 它甚至没有 void 返回类型。
  • 具有析构函数的类中的项不可能加入联合。
  • 类的公共部分应该有析构函数声明。
  • 程序员无法访问析构函数的地址。

C++ 中析构函数主要在何时调用?

当对象离开其作用域时,析构函数会自动调用。

C++ 中的析构函数主要在以下情况中被调用:

  1. 函数完成时。
  2. 程序结束时。
  3. 包含局部变量的块结束时。
  4. 无论何时使用 delete 运算符。

析构函数与普通成员函数有何区别?

由于它们不接受任何参数也不返回任何值,析构函数与普通成员函数不同。析构函数名称以波浪号 (~) 开头,并且与它们的类同名。

C++ 中的析构顺序

当一个项从作用域中删除或被删除时,会发生以下一系列事件来完成其销毁:

  • 调用类的析构函数,并执行析构函数体的代码。
  • 非静态成员对象的析构函数调用顺序由它们在类声明中的出现顺序决定。
  • 非静态成员对象的析构函数调用顺序由它们在类声明中的出现顺序决定。
  • 构造或销毁的顺序不受用于成员构造的可选成员初始化列表的影响。
  • 非虚基类析构函数的调用顺序与它们声明时的顺序相反。
  • 虚基类析构函数的调用顺序与它们声明时的顺序相反。

虚析构函数是什么意思?

当使用基类类型的指针删除具有非虚析构函数的派生类对象时,会导致未定义的行为。通过在基类定义中添加虚析构函数可以解决此问题。

基类的析构函数可以是虚函数。每当执行向上转型时,基类的析构函数必须设置为虚函数,以确保程序终止时对象被正确销毁。

静态转换操作用于打印 void 指针的内容。它将指针的 void* 类型更改为指针所存储地址的适当数据类型。

使用 C++ 程序进行没有虚析构函数的向上转型


#include <iostream>
using namespace std;

// declare a class
class base {

   public:
    // Destructor
    ~base() {
        cout << "The Destructor base." << endl;
    }
};
class derived : publicbase {
    public:
    // Destructor
    ~derived() {
        cout << "The Destructor derived." << endl;
    }
}

int main() {

    base* b = new derived; // Upcasting
    delete b;

    return 0;
}

 

输出


The Destructor base

解释

上述示例中的 `delete b` 命令只会调用基类的析构函数,这是不希望的,因为它阻止了派生类对象的销毁,因为它的析构函数从未被调用。这会导致内存泄漏。

使用 C++ 程序进行带虚析构函数的向上转型


#include <iostream>
using namespace std;

// declare a class
class base {

   public:
    virtual ~base() {
        cout << "The Destructor base." << endl;
    }
};
class derived : publicbase {
    public:
    ~derived() {
        cout << "The Destructor derived." << endl;
    }
}

int main() {

    base* b = new derived; // Upcasting
    delete b;

    return 0;
}

 

输出


The Destructor base 
The Destructor derived

解释:当基类中存在虚析构函数时,期望的行为是首先调用派生类的析构函数,然后调用基类的析构函数。

纯虚析构函数是什么意思?

当析构函数的初始值设为 0 时,它被称为虚析构函数,并在基类中声明。纯虚析构函数是另一个选项,类似于虚析构函数。但是,它必须在基类中声明。

虚析构函数和纯虚析构函数的主要区别在于,纯虚析构函数会使其基类成为抽象类,这意味着您将无法创建该类的对象。

派生类不一定需要包含纯虚析构函数。

定义析构函数的规则是什么?

  1. 名称应以波浪号(~)开头,并与类名匹配。
  2. 一个类不能有多个析构函数。
  3. 与可以接受参数的构造函数不同,析构函数不接受任何参数。
  4. 像构造函数一样,它们也没有返回类型。
  5. 如果类没有指定析构函数,编译器会创建一个并将其放入您的代码中。