【问题标题】:Why does it make sense to give definition for a pure virtual function?为什么定义纯虚函数有意义?
【发布时间】:2012-10-06 18:50:57
【问题描述】:

Scott 在《Effective C++, 3rd Edition, pg.》上说。 43 要创建一个抽象类,我们只需要给它一个纯虚析构函数:

class AWOV {                  // AWOV = "Abstract w/o Virtuals"
public:
  virtual ~AWOV() = 0;        // declare pure virtual destructor
};

然后,他接着说有一个转折:我们必须为纯虚析构函数提供一个定义:

AWOV::~AWOW() {}              // definition of pure virtual dtor

我的问题是,通过指定= 0,对于纯虚函数,我们说该函数不能对声明这个纯虚函数的类有任何定义。

为什么在这里为纯虚析构函数提供定义(即使是空的)也可以?

【问题讨论】:

  • 因为基类必须在派生类死亡时调用它。
  • 您可以为任何纯虚函数提供定义。
  • Effective C++中有一个实现纯虚函数的例子。 pp166-167,但您可能必须阅读第 34 条的全部内容才能了解上下文。

标签: c++ class polymorphism virtual-functions


【解决方案1】:

“我们是说函数不能对声明这个纯虚函数的类有任何定义。”

这不是纯虚拟的意思。纯虚拟只意味着包含的类不能被实例化(是抽象的),所以它必须是子类,并且子类必须覆盖该方法。例如,

struct A {
    virtual ~A() = 0;
};

A::~A() {}

struct B : A {};

int main()
{
    A a;  // error
    B b;  // ok
}

这里,B 析构函数是隐式定义的。如果它是另一种纯虚拟方法,则必须显式覆盖它:

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

void A::foo() {}

struct B : A {};

int main()
{
    B b;  // error
}

当基类必须是抽象的但仍提供一些默认行为时,需要为纯虚方法提供定义。

在析构函数的特定情况下,它必须提供,因为it will be called automatically 当子类实例被销毁时。尝试使用没有定义的纯虚析构函数实例化类的子类的程序将不会通过链接器。

【讨论】:

  • 我从 Andrew Koenig 的“Accelerated C++”中得到了这样的印象,特别是第 15.1.2 节:“C++ 语言没有强迫我们为这些操作编造任意定义,而是让我们说会有对给定的虚函数没有定义。"
  • @QiangXu:事实上,这是纯虚拟的主要用例,但从语言/编译器的角度来看,这不是它们的工作方式。此外,您不能省略析构函数的定义,因为它必须在行为良好的程序中调用。
【解决方案2】:

使其成为纯粹的派生(非抽象)类来实现自己的。

提供一个实现允许派生类调用基类行为(默认情况下析构函数会这样做)。

【讨论】:

  • “使它成为纯粹的强制派生类在那里实现自己的。”不是真的。
  • @LuchianGrigore 如果派生类想要被实例化,它会让派生类在继承线的某个地方实现自己的,不是吗?
  • @MooingDuck 在您的代码中,派生类确实实现了自己的析构函数(自动)。
【解决方案3】:

有两种情况。

纯虚析构函数

这种情况是标准专门处理的。

12.4 析构函数 [class.dtor]

9) 析构函数可以声明为virtual (10.3) 或纯virtual (10.4);如果该类的任何对象或任何 派生类在程序中创建,必须定义析构函数。如果一个类有一个基类 虚拟析构函数,它的析构函数(无论是用户声明的还是隐式声明的)都是虚拟的。

析构函数的情况不同,因为所有析构函数在继承层次结构(假设正确删除)中以相反的构造顺序调用,即使不是显式的。所以当对象被删除时会调用基类析构函数——这就是它需要实现的原因。

纯虚方法

它们与析构函数的不同之处在于它们不需要实现,也不需要实现。缺少要求的不同之处在于,当调用 Derived::foo() 时,它不会自动调用 Base::foo()(不是它可以,因为它可以实现或不可以实现)。

为什么要实现纯virtual 方法取决于具体情况。我将纯说明符视为对程序员的提示,而不是与逻辑相关。它告诉你——程序员——你应该实现那个方法。基类是否有实现并不重要。

通过指定 = 0,对于纯虚函数,我们说该函数不能对声明此纯虚函数的类有任何定义。

不是真的。您是说派生的非抽象类必须覆盖该函数。这并不妨碍您自己实现它。

【讨论】:

  • 好的,知道了。纯虚函数不需要实现,但也不禁止实现。对于纯虚拟析构函数,甚至需要实现。我说的对吗?
【解决方案4】:

这对于所有纯虚函数 不是必需的。一点也不。

这样,派生类仍将被强制重写实现,但基类中会有一个默认实现,以防万一需要 调用它。这就是这里的情况——因为你正在处理一个析构函数:当一个派生对象被销毁时,它的析构函数被称为,它的基类析构函数也被调用。这就是为什么您需要实现A::~A

【讨论】:

    【解决方案5】:

    通过将函数设为纯虚函数,我们强制类的用户用派生类中的另一个函数替换该函数。

    基类函数仍然可以用BaseClass::myfunction(...)调用

    现在基类可能想要提供派生类可以使用的一些核心功能,如果它选择这样做的话。

    【讨论】:

      【解决方案6】:

      正确答案: 假设类中有 2 个纯虚函数。现在假设两者都有实现。提供 PVF 实现的原因是,如果 Base 类是通过实现这些 PVF 方法由子类派生的,则 Base 类方法可以充当默认行为(使类成为具体)

      【讨论】:

      • "如果基类是通过实现至少一种方法由子类派生的(使类成为具体的)" - 不,派生类必须实现 both 虚函数.
      猜你喜欢
      • 2011-04-27
      • 2011-05-09
      • 1970-01-01
      • 2018-03-08
      • 2015-08-25
      • 2011-12-25
      • 2012-07-27
      • 2021-12-09
      相关资源
      最近更新 更多