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