C++中的虚函数和继承

Virtual function and inheritance in C++

下面的代码段工作正常:

#include <iostream> 
#include <string>

using namespace std;

class HelpInterface {
public:
    void getHelp();
};

class Applicaiton : public HelpInterface {
public:
    void getHelp() {
        cout << "General help";
    }
};

int main(void) {
    Applicaiton applicaiton;
    applicaiton.getHelp();
}

在 HelpInterface class 中将 getHelp 函数设为虚函数,我将收到链接器错误:

class HelpInterface {
public:
    virtual void getHelp();
};

如果我对 getHelp 进行空实现,如下所示,事情将再次起作用:

class HelpInterface {
public:
    virtual void getHelp() {};
};

谁能帮我理解为什么 virtual 会引发链接器错误,除非我在基础 class 中有 getHelp 的实现,以及为什么没有实现的非虚函数也能正常工作?在这个例子中,基本函数永远不会被调用。

这是链接器错误的VS2013截图:

如果您希望基 class 方法是虚拟的并且不提供任何实现,您必须像这样将其设置为零:

class HelpInterface {
public:
    virtual void getHelp() = 0;
};

这称为纯虚方法。它具有使您的 class 抽象并强制其所有派生的 classes 提供该方法的实现的效果。因此,请注意,您将无法再创建基 class 的实例,因为它是抽象的。

当一个方法在基础 class 中但不是虚拟的时,如果您没有在 [=] 上显式调用该方法,链接器实际上不会引用该方法的实现21=] 到基类型(在派生的实例 class 上)或基类型的实例。

虚函数可以通过多种不同的方式实现,并且是特定于实现的。最常见的一种是使用虚拟 table。 (也称为虚方法 table、虚函数 table、虚调用 table、调度 table、vtable 或 vftable) , 我很确定你的编译器 (VS2013) 使用这种实现虚函数的方法。

虚拟 table 是虚拟成员函数的 table 函数指针。 class 实例将包含指向 class 所属的 table 的指针。

当您将函数设为虚拟时,链接器会尝试将其放入该类型的虚拟 table 中。不管你是否调用它或实例化一个基础class(也是一个特定于实现的细节)。

正如 qexyn 已经回答的那样,要解决这个问题,您可以通过在虚函数声明之后添加 = 0 来将方法声明为纯虚函数。这告诉链接器在 class 的虚函数 table 中放置一个空指针。您还可以将虚函数声明为纯虚函数,并提供实现。这会强制派生的 classes 实现该功能,但允许它们选择使用默认方法。

原因是您的基本 class 定义使用间接获取 Application 的实际函数。

这通常是用函数指针实现的,但在任何情况下都可能有派生的 class 不会覆盖基础 class 实现。

尽管this isn't usually reproducible that is simply because member functions are implicitly declared inline and when the implementation is visible an optimizing compiler will do exactly that; inline the function. The proof is in the optimization.

您想要的是确保每个派生的 class 来自您的基础实现 getHelp()。这是一个常见的习语,是一个核心语言特性。你想要一个 "pure virtual function"。这将使你的基础 class 成为 "abstract base class",这实际上是面向对象的 jibber-jabber 中 "interface" 的同义词。

在 C++ 中执行此操作的方法是在所有成员函数限定符之后使用特殊的成员函数特定语法(我在这里使用尾随 return 类型,因为我认为它们很漂亮):

class base{
    public:
        virtual auto func() -> void = 0;
};

这指定 base 是一个带有纯虚函数 func() 的抽象基 class,从它派生的所有 classes 都将实现。

在你的情况下你会写:

class HelperInterface{
    public:
        virtual void getHelp() = 0; // probably want const qualifier
};

并且 Application 保持原样。

你每天都能学到东西,是吗?