析构函数是类的一个成员函数,用于释放类的对象在生存期程序为其分配的内存资源。由于析构函数是由程序自动调用的,那么我们就需要正确的分析出析构函数何时被调用。以下是我在学习过程中遇到的几种易产生误区的调用场景:
场景1> 对于一个全局函数,如果函数的形参的数据类型是类的对象,那么在函数作用域结束时,会自动调用析构函数,将形参的内存资源析构掉。同时,全局函数内部定义了一个临时对象tmp,在执行return语句时,程序将调用拷贝构造函数,把临时对象tmp拷贝给一个副本,称副本为匿名对象。故最终return出来的对象是匿名对象而非临时对象tmp。而tmp会被析构。因此,这段代码实际调用了两次析构函数。代码如下:
1 #include "iostream" 2 using namespace std; 3 4 class Test 5 { 6 public: 7 int a; 8 public: 9 Test(int a = 0) 10 { 11 this->a = a; 12 } 13 Test(const Test &obj) 14 { 15 a = obj.a; 16 } 17 ~Test() 18 { 19 cout<<"我是析构函数"<<endl; 20 } 21 }; 22 23 Test MyCopy(Test p) 24 { 25 Test tmp = p; 26 return tmp; //先析构形参p,再析构tmp 27 } 28 29 void main() 30 { 31 Test t1(2); 32 Test t2 = MyCopy(t1); 33 34 cout<<"hello..."<<endl; 35 system("pause"); 36 return ; 37 }
输出结果为:
场景2> 另外,主函数中的32行,返回的匿名对象通过“=”号赋值给t2,此时由于采用的是初始化的方式,因此编译器会直接将匿名对象的名称更改为t2,即直接转化为t2,不会调用析构函数。而下面一种情况则需要调用,请看代码:
Test t2;
t2 = MyCopy(t1);
由于此时代码采用的是赋值的方式,因此编译器会调用拷贝构造函数将匿名对象拷贝给t2,同时匿名对象被析构。这样,析构函数就被调用了三次,程序更改后输出结果如下:
场景3> 当全局函数形参为引用时,不会调用析构函数。代码如下:
Test MyCopy(Test &p) {Test tmp = p;return tmp; }void main() {Test t1(2);Test t2 = MyCopy(t1);cout<<"hello..."<<endl;system("pause");return ; }
由于引用的作用相当于指针,因此传递的只是对象所在的内存空间地址。又函数返回时采用初始化的方式,因此析构函数仅被执行一次。输出结果如下: