C++ 虚拟析构函数规则

C++ 虚拟析构函数规则,c++,memory,C++,Memory,我对何时以及为什么使用虚拟析构函数有点困惑 class Q { private: int i; int *pi; int *p; public: Q(int k); print(); ~Q() {delete pi; delete p;} }; class DQ : public Q { private: int *pd; public: DQ(); ~DQ(); }; 我明白,如果我主要分

我对何时以及为什么使用虚拟析构函数有点困惑

class Q { 
private: 
    int i; 
    int *pi; 
    int *p; 
public: 
    Q(int k); 
    print(); 
    ~Q() {delete pi; delete p;} 
}; 
class DQ : public Q 
{
private: 
    int *pd; 
public: 
    DQ(); 
    ~DQ(); 
};
我明白,如果我主要分配

Q *p = new DQ();
如果删除p,则只调用Q的析构函数

但在每一种情况下会发生什么:

  • 只有~Q()是虚拟的,~DQ()不是
  • 只有~DQ()是虚拟的,~Q()不是
  • ~Q()是虚拟的,~DQ()是虚拟的
  • 1.)如果
    ~Q()
    是虚拟的,
    ~DQ()
    是虚拟的,无论您是否编写
    虚拟的
    ,因此两个析构函数都将被调用

    2.)调用
    删除p是未定义的行为(可能只调用
    Q
    的析构函数,但可能会崩溃或执行任意数量的操作,因为它是未定义的)。虚拟性向下传播,而不是向上传播

    3.)与#1的情况相同,只是您明确地编写了
    virtual
    (顺便说一句,这是一个很好的实践)。两个析构函数都将被调用

    我对何时以及为什么使用虚拟析构函数有点困惑

    class Q { 
    private: 
        int i; 
        int *pi; 
        int *p; 
    public: 
        Q(int k); 
        print(); 
        ~Q() {delete pi; delete p;} 
    }; 
    class DQ : public Q 
    {
    private: 
        int *pd; 
    public: 
        DQ(); 
        ~DQ(); 
    };
    
    为了以多态方式删除对象,需要在基类中使用虚拟析构函数;也就是说,通过指向基类型的指针删除派生类型的对象

    我理解如果我主要分配一个
    Q*p=new DQ()
    并删除p,则只调用Q的析构函数

    不,你有未定义的行为。它可能会调用Q的析构函数,也可能会向你的老板发送一封侮辱性的电子邮件——第一封更可能,但原则上任何事情都可能发生

    只有~Q()是虚拟的,~DQ()不是

    那是不可能的。如果函数在类中是虚拟的,那么它在任何派生类中也是虚拟的

    只有~DQ()是虚拟的,~Q()不是

    然后,不允许通过指向
    Q
    的指针删除类型为
    DQ
    的对象;如果你尝试,你会得到未定义的行为。但是,如果您从
    DQ
    派生另一个类,那么您可以通过指向
    DQ
    (但不是
    Q
    )的指针删除它们,这要归功于它的虚拟析构函数

    ~Q()是虚拟的,~DQ()是虚拟的

    然后一切正常,您可以通过基类指针安全地删除对象。

    1.~DQ()和~Q()都是按照~DQ(),~Q()的顺序调用的。 2.只调用~Q()。
    3.同时调用~DQ()和~Q()。

    virtual~Q()
    意味着所有派生的析构函数都是虚拟的。所以(1)是没有意义的。我不能在不审查标准的情况下对(2)进行评论。(3)见(1)。一般来说,通过非多态指针触发基类销毁是非常困难的(或者上次我检查pre-C++11时是这样,我无法想象这会发生变化)。这里有一些提示:如果通过指向基类的指针删除派生类型的对象,则行为是未定义的。不需要调用
    Q
    的析构函数。是否有任何原因导致否决票?我很肯定我的回答是正确的,但如果我错了,请告诉我。这对我来说也是个谜。此答案看起来正确且完整。对于2,由于基本析构函数不是虚拟的,因此这将仅调用派生的析构函数增益:行为未定义。这意味着没有要求。如果您想报告您所看到的特定编译器所做的事情,请务必这样做,但要将其标记为特定于实现的。