通常のクラスを奇妙な繰り返しパターンに変換する

少し前に、私は最近c ++を学び始め、手続き型プログラミングに慣れたプログラマーにアプローチされました。 びっくりしました。



仮想メソッドの仕組みを思い出しましょう。 クラスに1つ以上の仮想メソッドが含まれる場合、そのようなクラスのコンパイラは仮想メソッドのテーブルを作成し、クラス自体に仮想テーブルポインターを追加します。 コンパイラーは、クラスコンストラクターでコードを生成して、仮想テーブルポインターを初期化します。 呼び出された仮想メソッドは、作成されたテーブルからメソッドアドレスを選択することにより、プログラム実行段階で選択されます。



合計では、次の追加費用があります。



1)クラス内の追加のポインター(仮想メソッドのテーブルへのポインター)。

2)クラスコンストラクターの追加コード(仮想テーブルポインターを初期化するため)。

3)仮想メソッドへの各呼び出しの追加コード(仮想メソッドのテーブルへのポインターを参照し、仮想メソッドの目的のアドレスをテーブルで検索します)。



幸いなことに、コンパイラは仮想化などの最適化をサポートしています。 その本質は、コンパイラが呼び出されるオブジェクトのタイプを正確に認識し、仮想メソッドテーブルが使用されない場合、仮想メソッドが直接呼び出されることです。 このような最適化はかなり前に登場しました。 たとえば、gcc-バージョン4.7以降、clang'aバージョン3.8以降(-fstrict-vtable-pointersフラグが表示されます)。



しかし、それでも、一般に仮想関数なしでポリモーフィズムを使用することは可能ですか? 回答:はい、できます。 いわゆる「奇妙な繰り返しパターン」(CRiously Recurring Template PatternまたはCRTP)が助けになります。 確かに、これは静的なポリモーフィズムです。 通常のダイナミックとは異なります。



仮想メソッドを持つクラスをテンプレートを持つクラスに変換する例を見てみましょう。



class IA { public: virtual void helloFunction() = 0; }; class B : public IA { public: void helloFunction(){ std::cout<< "Hello from B"; } };
      
      





になります:



 template <typename T> class IA { public: void helloFunction(){ static_cast<T*>(this)->helloFunction(); } }; class B : public IA<B> { public: void helloFunction(){ std::cout<< "Hello from B"; } };
      
      





申し立て:



 template <typename T> void sayHello(IA<T>* object) { object->helloFunction(); }
      
      





IAクラスは、生成されたクラスをテンプレートとして受け入れ、これへのポインターを生成されたクラスにキャストします。 static_castはコンパイルレベルのキャストチェックを実行するため、パフォーマンスには影響しません。 クラスBはクラスIAから派生し、クラスIAはクラスBによってテンプレート化されます。



追加コスト-クラス内の追加ポインター、クラスのコンストラクター内の追加コード、仮想メソッドの呼び出しごとの追加コード。最初の場合と同様に、ありません。 コンパイラが仮想化の最適化をサポートしていない場合、そのようなコードはより高速に動作し、メモリを消費しません。



ご清聴ありがとうございました。



誰かがこのメモが役立つと思うことを願っています。



All Articles