纯虚拟函数

我们还可以通过将 = 0 附加到声明来指定 virtual 函数是纯虚拟 (抽象)。具有一个或多个纯虚函数的类被认为是抽象的,无法实例化; 只能为所有纯虚函数定义或继承定义的派生类进行实例化。

struct Abstract {
    virtual void f() = 0;
};

struct Concrete {
    void f() override {}
};

Abstract a; // Error.
Concrete c; // Good.

即使将函数指定为纯虚函数,也可以为其指定默认实现。尽管如此,该函数仍将被视为抽象,派生类必须在实例化之前对其进行定义。在这种情况下,甚至允许派生类的函数版本调用基类的版本。

struct DefaultAbstract {
    virtual void f() = 0;
};
void DefaultAbstract::f() {}

struct WhyWouldWeDoThis : DefaultAbstract {
    void f() override { DefaultAbstract::f(); }
};

我们可能想要这样做有几个原因:

  • 如果我们想要创建一个本身不能实例化的类,但不阻止其派生类被实例化,我们可以将析构函数声明为纯虚拟。作为析构函数,如果我们希望能够释放实例,则必须始终定义它。而作为析构函数是最有可能已经虚拟防止多态性在使用过程中的内存泄漏 ,我们将不会因从另一个声明功能 virtual 创下了不必要的性能。这在制作接口时很有用。

      struct Interface {
          virtual ~Interface() = 0;
      };
      Interface::~Interface() = default;
    
      struct Implementation : Interface {};
      // ~Implementation() is automatically defined by the compiler if not explicitly
      //  specified, meeting the "must be defined before instantiation" requirement.
    
  • 如果纯虚函数的大部分或全部实现都包含重复代码,则可以将该代码移动到基类版本,从而使代码更易于维护。

      class SharedBase {
          State my_state;
          std::unique_ptr<Helper> my_helper;
          // ...
    
        public:
          virtual void config(const Context& cont) = 0;
          // ...
      };
      /* virtual */ void SharedBase::config(const Context& cont) {
          my_helper = new Helper(my_state, cont.relevant_field);
          do_this();
          and_that();
      }
    
      class OneImplementation : public SharedBase {
          int i;
          // ...
    
        public:
          void config(const Context& cont) override;
          // ...
      };
      void OneImplementation::config(const Context& cont) /* override */ {
          my_state = { cont.some_field, cont.another_field, i };
          SharedBase::config(cont);
          my_unique_setup();
      };
    
      // And so on, for other classes derived from SharedBase.