純粋な仮想メソッドを別の純粋な仮想メソッドでオーバーライドすることは理にかなっていますか?次のオプションのいずれかを他のオプションよりも優先する機能的な違いやコードスタイルの理由はありますか?
class Interface {
public:
virtual int method() = 0;
};
class Abstract : public Interface {
public:
int method() override = 0;
};
class Implementation : public Abstract {
public:
int method() override { return 42; }
};
対:
class Interface {
public:
virtual int method() = 0;
};
class Abstract : public Interface {};
class Implementation : public Abstract {
public:
int method() override { return 42; }
};
どちらのコードでも同じ効果が得られます。クラスAbstract
は抽象的であり、インスタンス化することはできません。
ただし、2つの形式には意味上の違いがあります。
Abstract
が抽象であることを明確に思い出させます(名前が十分に自己分離されない場合に備えて ;-))。それはそれを思い出させるだけでなく、メソッドが純粋な仮想であることを確実にすることによってそれを確実にします。Abstract
がInterface
から正確にすべてを継承することを意味します。基本クラスがそうである場合にのみ抽象です。これは、コードの将来の進化に影響を与えます。たとえば、ある日気が変わって、インターフェースにmethod()
のデフォルト実装を持たせたい場合:
Absract
は抽象のままであり、メソッドのデフォルト実装を継承しません。Abstact
が引き続き継承し、Interface
とまったく同じように動作することを保証します。個人的には、2番目の形式の方が直感的であり、関心事の分離を確実にすることがわかります。しかし、最初のフォームが意味をなすことができる状況がいくつかあると想像できます。
メソッドの純粋な仕様はオーバーライドを強制しますが、メソッドの実装を提供することを妨げるものではありません。以下はまれですが、時に役立つテクニックです。
class Interface
{
virtual void method() = 0;
};
class Abstract : public Interface
{
virtual void method() = 0;
}
inline void Abstract::method()
{
do something interesting here;
}
class Concrete : public Abstract
{
virtual void method();
}
inline void Concrete::method()
{
// let Abstract::method() do it's thing first
Abstract::method();
now do something else interesting here;
}
これは、いくつかの共通の機能を必要とするが、クラス固有の動作を追加する必要があるAbstractから派生したクラスがいくつかある場合に役立つことがあります。 [その動作を提供することを余儀なくされる必要があります。]