C++中的析构函数
在本教程中,我们将了解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时,由于这一次虚拟析构函数位于基类内部,因此首先调用派生类的析构函数,然后再调用基类的析构函数。
注意:如果基类的析构函数不是虚拟的,那么只有基类对象会被删除(因为指针是基类的)。
这将导致派生对象的内存泄漏。
为什么要使用析构函数?
一旦析构函数超出范围或者被显式删除,则析构函数将取消分配或者分配给程序中不同对象的内存。
如果不是析构函数,那么用户将必须单独释放分配给不同变量或者对象的内存空间。
这是一项非常繁忙的任务。
内存的这种自动重新分配不仅创建了更加用户友好的工作环境,而且还有助于有效地管理内存。

