純虛擬函式

我們還可以通過將 = 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.