C++开发中级


965 浏览 5 years, 11 months

3.2 父类的析构函数

版权声明: 转载请注明出处 http://www.codingsoho.com/

父类的析构函数

由于析构函数没有参数,因此可以自动调用父类的析构函数。析构函数的调用顺序刚好与构造函数相反:
(1) 调用类的析构函数。
(2) 销毁类的数据成员,与创建的顺序相反。
(3) 如果有父类,调用父类的析构函数。

也可以递归使用这些规则。链上最底层的成员总是被第一个销毁。下面的代码在前面的示例中加入了析构函数。所有的析构函数都声明为virtual,这一点非常重要,将在本示例之后进行讨论。执行时代码将输出“123321"。

class Something
{
    public:
        Something(){ cout << "2"; }
        virtual ~Something(){ cout << "2"; }
};

class Parent
{
    public:
        Parent(){ cout << "1"; }
        virtual ~Parent(){ cout << "1"; }
};

class Child : public Parent
{
    public:
        Child() { cout << "3"; }
        virtual ~Child() { cout << "3"; }
    protected:
        Something mDataMember;        
};

int main()
{
    Child myChild;
    return 0;
}

注意所有析构函数都是virtual。根据经验,所有析构函数都应该声明为virtual。如果前面的析构函数没有声明为virtual,代码也可以继续运行。然而,如果代码使用delete删除一个实际指向子类的超类指针,析构函数调用链将被破坏。例如,下面的代码与前面示例类似,但析构函数不是virtual。当使用指向Parent的指针访问Child对象并删除对象时,问题就发生了。

class Something
{
    public:
        Something(){ cout << "2"; }
        ~Something(){ cout << "2"; } // should be virtual, but will work
};

class Parent
{
    public:
        Parent(){ cout << "1"; }
        ~Parent(){ cout << "1"; } // BUG, Make this virtual
};

class Child : public Parent
{
    public:
        Child() { cout << "3"; }
        ~Child() { cout << "3"; } // should be virtual, but will work
    protected:
        Something mDataMember;        
};

int main()
{
    Parent* ptr = new Child();    
    delete ptr;
    return 0;
}

代码的输出简短的令人吃惊,是“1231"。当删除Ptr变量时,只调用了Parent的析构函数,因为析构函数没有声明为virtual。结果是Child的析构函数没有被调用,并且其数据成员的析构函数也没有被调用。

从技术角度看,您可以将Parent的析构函数声明为Virtual,从而纠正上面的问题。子类将自动“虚化”。然而,我们建议显式地将所有析构函数声明为virtual,这样就不必再担心这个问题。

提示: 将所有析构函数声明为virtual!编译器生成的默认析构函数不是virtual,因此应该定义自己的虚析构函数,至少在父类中应该这么做。