動的 (ランタイム) ポリモーフィズムについては、Non-Virtual-Interface (NVI) イディオムを使用することをお勧めします。このパターンは、インターフェイスを非仮想およびパブリックに、デストラクタを仮想およびパブリックに、実装を純粋な仮想およびプライベートに保持します。
class DynamicInterface
{
public:
// non-virtual interface
void fun() { do_fun(); } // equivalent to "this->do_fun()"
// enable deletion of a Derived* through a Base*
virtual ~DynamicInterface() = default;
private:
// pure virtual implementation
virtual void do_fun() = 0;
};
class DynamicImplementation
:
public DynamicInterface
{
private:
virtual void do_fun() { /* implementation here */ }
};
動的ポリモーフィズムの優れた点は、インターフェイス基本クラスへのポインターまたは参照が必要な派生クラスを実行時に渡すことができることです。this
ランタイム システムは、ポインターを静的基本型から動的派生型に自動的にダウンキャストし、対応する実装を呼び出します (通常は、仮想関数へのポインターを含むテーブルを介して発生します)。
静的 (コンパイル時のポリモーフィズム) の場合は、Curiously Recurring Template Pattern (CRTP) を使用することをお勧めします。動的ポリモーフィズムのベースから派生への自動ダウンキャストを で行う必要があるため、これはかなり複雑static_cast
です。この静的キャストは、各静的インターフェイスの派生元であるヘルパー クラスで定義できます。
template<typename Derived>
class enable_down_cast
{
private:
typedef enable_down_cast Base;
public:
Derived const* self() const
{
// casting "down" the inheritance hierarchy
return static_cast<Derived const*>(this);
}
Derived* self()
{
return static_cast<Derived*>(this);
}
protected:
// disable deletion of Derived* through Base*
// enable deletion of Base* through Derived*
~enable_down_cast() = default; // C++11 only, use ~enable_down_cast() {} in C++98
};
次に、次のように静的インターフェースを定義します。
template<typename Impl>
class StaticInterface
:
// enable static polymorphism
public enable_down_cast< Impl >
{
private:
// dependent name now in scope
using enable_down_cast< Impl >::self;
public:
// interface
void fun() { self()->do_fun(); }
protected:
// disable deletion of Derived* through Base*
// enable deletion of Base* through Derived*
~StaticInterface() = default; // C++11 only, use ~IFooInterface() {} in C++98/03
};
そして最後に、それ自体をパラメーターとしてインターフェースから派生する実装を作成します
class StaticImplementation
:
public StaticInterface< StaticImplementation >
{
private:
// implementation
friend class StaticInterface< StaticImplementation > ;
void do_fun() { /* your implementation here */ }
};
これにより、同じインターフェイスの複数の実装を使用できますが、コンパイル時にどの実装を呼び出しているかを知る必要があります。
では、いつどのフォームを使用するのでしょうか? どちらの形式でも、共通のインターフェイスを再利用し、インターフェイス クラス内に事前/事後条件テストを挿入できます。動的ポリモーフィズムの利点は、ランタイムの柔軟性があることですが、その代わりに仮想関数呼び出し (通常はインライン化の機会がほとんどない関数ポインターを介した呼び出し) が必要になります。静的多項式はその鏡です。仮想関数呼び出しのオーバーヘッドはありませんが、欠点は、より多くの定型コードが必要であり、コンパイル時に何を呼び出しているかを知る必要があることです。基本的に効率と柔軟性のトレードオフです。
注:コンパイル時の多項式では、テンプレート パラメーターも使用できます。CRTP イディオムによる静的インターフェースと通常のテンプレート パラメーターの違いは、CRTP タイプのインターフェースは (メンバー関数に基づいて) 明示的であり、テンプレート インターフェースは (有効な式に基づいて) 暗黙的であることです。