为什么我们需要在C++中使用纯虚拟析构函数?

Mar*_*ark 151 c++ destructor pure-virtual

我理解虚拟析构函数的必要性.但为什么我们需要纯虚拟析构函数?在其中一篇C++文章中,作者提到我们在创建类抽象时使用纯虚析构函数.

但是我们可以通过将任何成员函数设置为纯虚拟来使类抽象化.

所以我的问题是

  1. 我们什么时候才能真正使析构函数变为虚拟?任何人都能给出一个很好的实时例子吗?

  2. 当我们创建抽象类时,将析构函数设置为纯虚拟是一种很好的做法吗?如果是..那为什么?

Mot*_*tti 115

  1. 允许纯虚拟析构函数的真正原因可能是禁止它们意味着在语言中添加另一条规则,并且不需要这条规则,因为允许纯虚拟析构函数不会产生任何不良影响.

  2. 不,简单的老虚拟就足够了.

如果使用其虚拟方法的默认实现创建一个对象,并希望在不强制任何人覆盖任何特定方法的情况下使其成为抽象,则可以使析构函数为纯虚拟.我没有看到太多的意义,但它是可能的.

请注意,由于编译器将为派生类生成隐式析构函数,如果类的作者不这样做,则任何派生类都不是抽象的.因此,在基类中使用纯虚析构函数不会对派生类产生任何影响.它只会使基类抽象(感谢@kappa的评论).

也可以假设每个派生类可能需要具有特定的清理代码并使用纯虚拟析构函数作为提示来编写一个,但这似乎是人为的(并且是非强制的).

注意:析构函数是唯一的方法,即使它纯虚拟的,必须有一个实现来实例化派生类(是的纯虚函数可以有实现).

struct foo {
    virtual void bar() = 0;
};

void foo::bar() { /* default implementation */ }

class foof : public foo {
    void bar() { foo::bar(); } // have to explicitly call default implementation.
};
Run Code Online (Sandbox Code Playgroud)

  • @GMan,你错了,纯粹的虚拟意味着派生类必须覆盖这个方法,这与实现是正交的.如果你想亲眼看看,请查看我的代码并注释掉`foof :: bar`. (77认同)
  • @GMan:C++ FAQ lite说:"请注意,可以为纯虚函数提供一个定义,但这通常会让初学者感到困惑,最好避免以后再使用." http://www.parashift.com/c++-faq-lite/abcs.html#faq-22.4维基百科(正确性的堡垒)也同样说.我相信ISO/IEC标准使用了类似的术语(不幸的是我的副本目前处于工作状态)...我同意这是令人困惑的,当我提供一个定义时,我通常不会在没有澄清的情况下使用该术语,尤其是围绕新的程序员...... (15认同)
  • "是的纯虚函数可以实现"然后它不是纯虚拟的. (12认同)
  • @Motti:这里有趣并提供更多的混淆是纯虚拟析构函数不需要在派生(和实例化)类中明确地重写.在这种情况下使用隐式定义:) (8认同)
  • 如果你想使一个类抽象化,那么让所有构造函数都受到保护会不会更简单? (2认同)

Bra*_*den 32

抽象类所需要的只是至少一个纯虚函数.任何功能都可以; 但是当它发生时,析构函数是任何类都会有的 - 所以它总是作为候选者存在.此外,使析构函数纯虚拟(而不仅仅是虚拟)除了使类抽象之外没有任何行为副作用.因此,许多样式指南建议始终使用纯虚拟目标来指示一个类是抽象的 - 如果没有其他原因,它提供一致的位置,有人阅读代码可以查看该类是否是抽象的.

  • @Surfing:因为派生类的析构函数隐式调用其基类的析构函数,即使该析构函数是纯虚拟的.因此,如果没有实现,那么未定义的行为将会发生. (3认同)
  • 但仍然为什么要提供纯虚拟析构函数的实现。可能会出现什么问题,我将析构函数设为纯虚拟的,并且不提供其实现。我假设只声明了​​基类指针,因此永远不会调用抽象类的析构函数。 (2认同)

lea*_*der 18

如果要创建抽象基类:

  • 不能被实例化(是的,这是多余的与术语"抽象"!)
  • 需要虚拟析构函数行为(你打算携带指向ABC的指针,而不是指向派生类型的指针,并通过它们删除)
  • 并不需要任何其他虚拟调度其他方法的行为(也许还有没有其他的方法呢?考虑一个简单的受保护的"资源"的容器,它需要一个构造函数/析构函数/分配,但仅此而已)

...通过使析构函数为纯虚拟为其提供定义(方法体),最简单的方法是使类抽象化.

对于我们假设的ABC:

您保证它不能被实例化(即使是类本身内部,这就是私有构造函数可能不够的原因),您获得了析构函数所需的虚拟行为,并且您不必查找并标记另一个方法不需要虚拟调度作为"虚拟".


Lau*_*hel 8

从我读到的答案到你的问题,我无法推断出一个真正使用纯虚析构函数的理由.例如,以下原因并不能说服我:

允许纯虚拟析构函数的真正原因可能是禁止它们意味着在语言中添加另一条规则,并且不需要这条规则,因为允许纯虚拟析构函数不会产生任何不良影响.

在我看来,纯虚拟析构函数可能很有用.例如,假设代码中有两个类myClassA和myClassB,myClassB继承自myClassA.由于Scott Meyers在他的"更有效的C++"一书中提到的原因,第33项"使非叶类抽象化",实际上创建一个myClassA和myClassB继承的抽象类myAbstractClass是更好的做法.这提供了更好的抽象并防止了例如对象副本引起的一些问题.

在抽象过程(创建类myAbstractClass)中,可能没有myClassA或myClassB的方法是一个很好的候选者,因为它是一个纯虚方法(这是myAbstractClass抽象的先决条件).在这种情况下,您可以定义抽象类的析构函数pure virtual.

以下是我自己写的一些代码的具体例子.我有两个类,Numerics/PhysicsParams,它们共享共同的属性.因此,我让他们继承自抽象类IParams.在这种情况下,我绝对没有可以纯粹是虚拟的方法.例如,setParameter方法必须为每个子类具有相同的主体.我唯一的选择是让IParams的析构函数纯粹是虚拟的.

struct IParams
{
    IParams(const ModelConfiguration& aModelConf);
    virtual ~IParams() = 0;

    void setParameter(const N_Configuration::Parameter& aParam);

    std::map<std::string, std::string> m_Parameters;
};

struct NumericsParams : IParams
{
    NumericsParams(const ModelConfiguration& aNumericsConf);
    virtual ~NumericsParams();

    double dt() const;
    double ti() const;
    double tf() const;
};

struct PhysicsParams : IParams
{
    PhysicsParams(const N_Configuration::ModelConfiguration& aPhysicsConf);
    virtual ~PhysicsParams();

    double g()     const; 
    double rho_i() const; 
    double rho_w() const; 
};
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这种用法,但“强制”继承的另一种方法是声明“IParam”的构造函数受到保护,正如其他评论中指出的那样。 (2认同)

Ani*_*753 6

这里我想告诉我们什么时候需要虚析构函数,什么时候需要纯虚析构函数

class Base
{
public:
    Base();
    virtual ~Base() = 0; // Pure virtual, now no one can create the Base Object directly 
};

Base::Base() { cout << "Base Constructor" << endl; }
Base::~Base() { cout << "Base Destructor" << endl; }


class Derived : public Base
{
public:
    Derived();
    ~Derived();
};

Derived::Derived() { cout << "Derived Constructor" << endl; }
Derived::~Derived() {   cout << "Derived Destructor" << endl; }


int _tmain(int argc, _TCHAR* argv[])
{
    Base* pBase = new Derived();
    delete pBase;

    Base* pBase2 = new Base(); // Error 1   error C2259: 'Base' : cannot instantiate abstract class
}
Run Code Online (Sandbox Code Playgroud)
  1. 当您希望没有人能够直接创建 Base 类的对象时,请使用纯虚拟析构函数virtual ~Base() = 0。通常至少需要一个纯虚函数,让我们把 virtual ~Base() = 0, 作为这个函数。

  2. 当你不需要上面的东西时,你只需要派生类对象的安全销毁

    Base* pBase = new Derived(); 删除 pBase;不需要纯虚拟析构函数,只有虚拟析构函数才能完成这项工作。


小智 5

如果您想停止实例化基类而不对已实现和测试的派生类进行任何更改,请在基类中实现纯虚拟析构函数。