一个好的C++程序,应有一套谨慎的逻辑结构,一套严密的资源管理方法。其中资源管理总是最容易出现纰漏的地方,很多时候我们会忽略一个或多个delete/operator delete,或者我们写了清理代码,却没意识到它可能不会被执行。第一种情况自不必多说,而第二种情况可能就让人有些疑惑了。既然我写了它用来清理不需要的资源,那我必然会显式调用它或者编译器会自动调用它,怎么还能说不会被执行呢?对于这种情况,我能想到的有两种可能:

    1.使用多态性操作时,它被忽略了;

    2.异常影响了调用。

    对于第一点,我们可以先来分析一段代码:

 1 class base
 2 {
 3 private:
 4 protected:
 5 public:
 6     base() { cout << "Base's Constructor is Called!" << endl; }
 7     virtual ~base() { cout << "Base's Destructor is Called!" << endl; }
 8 };
 9 
10 class derived : public base
11 {
12 private:
13 protected:
14 public:
15     derived() : base() { cout << "Derived's Constructor is Called!" << endl; }
16     ~derived() { cout << "Derived's Destructor is Called!" << endl; }
17 };
18 
19 derived* GetDerived()
20 {
21     return new derived();
22 }

    以上是一个基类与一个派生类的声明,及一个返回派生类指针的操作。接下来,我将在主函数里,进行一个常规操作以及一个多态性操作,我们可以观察其运行结果:

1 int main()
2 {
3     base *pb = GetDerived();
4     delete pb;
5     derived *pd = GetDerived();
6     delete pd;
7 
8     return 0;
9 }

    仔细观察运行结果,我们可以发现,第一次,我将派生类指针赋给基类指针(这是可行的),并将基类指针进行删除,由此,我们可以看到基类构造、派生类构造函数的调用,还有基类析构函数的调用,等等!问题出现了,派生类析构函数未得到调用。这里,我先说解决的办法:我们可以在析构函数前加上virtual,使其成为虚析构函数,然后再执行同样的操作,我们可以看到,将派生类指针赋给基类指针使用时派生类析构函数也被调用了。

    之所以会如此,是因为编译器对virtual方法的处理。对于非virtual方法,编译器执行静态联编,也就是说,我使用的是基类指针,它就仅有基类操作;对于virtual方法,编译器使用动态联编,虽然我使用基类指针,但编译器此时会知道,这个指针指向的其实是一个派生类对象,如此多态性就体现了。

    以上的例子仅仅是为了清晰呈现virtual与非virtual的区别,在实际编码中,我们需要的是更严谨的态度,若少了一步,则某些清理工作可能不会得到执行,就将造成资源的浪费,所以我们要做的是善用虚析构函数。对于,上面提过了两种情况,第二种异常情况,将在后续文里提到,本文就不在赘述了。