C++中的析构函数

时间:2020-02-23 14:29:56  来源:igfitidea点击:

在本教程中,我们将了解C++中的析构函数的概念,它们的用法以及尝试在我们的代码中实现它们。

C++中的析构函数是什么?

从理论上讲,析构函数是用于破坏或者删除对象的成员函数。
可以是用户定义的。
但是,如果未定义,默认情况下在编译时,编译器会生成一个内联析构函数。

当对象超出范围或者由delete运算符明确销毁时,析构函数将自动调用。

现在让我们来看一下在C++中为名为example的类定义析构函数的语法:

example::~example()
{
	//destructor statements;
}

如您所见,其中析构函数(~example())与它所属的类的名称相同。

注意:在C++中定义析构函数之前,请记住:

  • 析构函数名称和类名称必须相同
  • 析构函数名称前应有波浪号('~')。
    例如。
    ~example(){}
  • 他们不得接受任何参数或者向函数调用返回任何内容
  • 析构函数可以是虚拟的
  • 派生类不继承其基类的析构函数
  • 单个类中只能有一个析构函数。

C++析构函数示例

现在,让我们看一个示例,尝试为演示类定义析构函数。

#include<iostream>
using namespace std;

class demo
{
	public:
		int a=10;
		
		demo() //constructor
		{
			cout<<"Constructor of demo here!"<<endl;
		}
		
		~demo() //destructor
		{
			cout<<"Destructor of demo here!"<<endl;
		}
		
		void print_demo() //function
		{
			cout<<"printed demo"<<endl;
		}
};

int main()
{
	demo demo_obj;  //object of class demo created
	cout<<demo_obj.a<<endl;
	demo_obj.print_demo(); //function called
	return 0;
}  //scope of the object ends

输出:

Constructor of demo here!
10
printed demo
Destructor of demo here!

其中

  • 在演示类内部,我们分别初始化和定义了变量" a = 10"和函数" print_demo()"。
    此外,我们还定义了相应的构造函数–demo()和析构函数–~demo()。

  • 在main()函数内部,我们创建一个对象demo_obj,它属于上面的演示类。
    这导致对构造函数demo()的调用。

  • 然后我们还对其执行了一些操作,例如为对象调用print_demo()函数。

  • 一旦main()函数结束,编译器便为对象demo_obj调用析构函数。
    从上面的输出中可以清楚地看出。

明确调用C++中的析构函数

除了一个对象超出范围时,编译器会调用默认或者用户定义的析构函数这一事实。
C++编程语言还允许用户显式调用对象的析构函数。

让我们尝试为前面的示例代码调用析构函数~demo()
我们只是在main()中修改我们的代码。

#include<iostream>
using namespace std;

class demo
{
	public:
		int a=10;
		
		demo() //constructor
		{
			cout<<"Constructor of demo here!"<<endl;
		}
		
		~demo() //destructor
		{
			cout<<"Destructor of demo here!"<<endl;
		}
		
		void print_demo() //function
		{
			cout<<"printed demo"<<endl;
		}
};

int main()
{
  demo obj;
  obj.print_demo();
  obj.~demo();
  return 0;
}

输出:

Constructor of demo here!
printed demo
Destructor of demo here!
Destructor of demo here!

如您所见,在创建对象时,将调用构造函数demo()

之后,我们显式调用对象obj的析构函数~demo()。
这说明了输出"此处的演示的析构函数!"的第三行。

但是,下一行是编译器在main()函数末尾默认调用析构函数的结果(因为对象范围结束了)。

注意:如果是局部变量(以上一个),则不建议显式调用析构函数,因为析构函数会在创建局部变量的块的最后一个}处再次调用。

C++中的虚拟析构函数

也可以将C++中的析构函数设为虚拟的。
当我们尝试通过指向基类的指针删除派生类的实例时,它们很有用。

现在让我们看一个例子以更好地理解。

虚拟析构函数示例

#include<iostream>
using namespace std;

class Base 
{
	public:
		
		Base()
		{
			cout<<"Base Constructor"<<endl;
		}
 	   virtual ~Base()
		{
			cout<<"Base Destructor"<<endl;
		}
};

class Derived: public Base 
{
	public:
		Derived()
		{
			cout<<"Derived Constructor"<<endl;
		}
		~Derived()
		{
			cout<<"Derived Destructor"<<endl;
		}
};
int main()
{
	Base * b = new Derived;
	delete b;
}

输出:

Base Constructor
Derived Constructor
Derived Destructor
Base Destructor

注意,这一次我们在基本析构函数之前使用了virtual关键字。

从输出中可以明显看出,在main()函数内部," Base * b = new Derived"指令分别导致对构造函数Base()和Derived()的调用。

在删除对象b时,由于这一次虚拟析构函数位于基类内部,因此首先调用派生类的析构函数,然后再调用基类的析构函数。

注意:如果基类的析构函数不是虚拟的,那么只有基类对象会被删除(因为指针是基类的)。
这将导致派生对象的内存泄漏。

为什么要使用析构函数?

一旦析构函数超出范围或者被显式删除,则析构函数将取消分配或者分配给程序中不同对象的内存。
如果不是析构函数,那么用户将必须单独释放分配给不同变量或者对象的内存空间。
这是一项非常繁忙的任务。

内存的这种自动重新分配不仅创建了更加用户友好的工作环境,而且还有助于有效地管理内存。