こんにちは
私はC ++を初めて使用しますが、一般的には自分の喜びのためだけに(そして時々エキゾチックなプラットフォームのために)プログラムします。理論的な本を読んでいません。執筆プロセスでGoogle、Stack Overflow、直感を積極的に使用しています。
これにより質問の一部が削除され、驚きの外観が防止されることを願っています。 :)
それでも、空き時間に次の自転車を(プラスとQtオームで)書いた後、この場所に何らかのメカニズムをねじ込むといいと思いました。 さらに、私が理解しているように、 オブジェクトの抽象ファクトリーと呼ばれるより一般的なデザインパターンがあり、その本質は私がキャッチしませんでした、そしてより単純な抽象化は、オブジェクトが多くのクラス識別子のペアについて知っているということです、そしてそれは作成することができます識別子によるクラスのインスタンス(最も単純な場合、この識別子は文字列ですが、列挙などを使用すると便利です)。 私が望むもの(つまり、最後の1つ)を多少理解したとき、私は自分に合った既製の美しいソリューションを探しに登りましたが、ほとんどすべてのGoogleをレイプしましたが、驚くべきことに、それを見つけませんでした 。
その結果、そのようなナンセンスに許容できない時間を費やして、私は自分のユニークな場所の山から集めて(Googleの残りの部分に到達し)、まだエレガントで小さいようです(本当に小さい...私は全体の投稿の必要性さえ疑います...)バイク、テンプレートクラス、テンプレート関数、特殊化、さらにはC ++ 11の 可変テンプレートや関数など、多くのことを処理する必要があります 。
まさに私が欲しいものを予約しましょう。 最初に、もちろん、私はルーマンキャラクターrtorstenの記事を読みました。 ストリーム に オブジェクトを 配置し ます 。 オブジェクトのファクトリパターンです 。しかし、起こっているすべての意味がまだ伝わっておらず、実装が少し過負荷に見え、コンストラクタに引数を渡す方法がありませんなるほど。 この投稿は、同じことをやろうとしているが、より良い試みだと言うことができます。 さて、そして次に、私が望んだことと達成したこと(それは一致したはずです:)):ベースクラス
Base
とそこからの2つの直接の子孫があると仮定します:
Derived1
と
Derived2
、ファクトリによって作成したいインスタンス。
上記はその記事でカバーされていますが、私も欲しいです
- さまざまなコンストラクター引数を使用してクラスインスタンスを作成します。
- 任意の基本クラスと、任意の数とタイプのコンストラクター引数に対して、単一のファクトリー実装があります。
- ファクトリに追加できるようにクラスを変更する必要はありません。
つまり、次のようなものです。
#include <iostream> #include <string> #include "factory.h" using namespace std; class Animal{ public: Animal(bool isAlive,string name) : isAlive(isAlive),name(name){}; bool isAlive; string name; virtual string voice() const=0; }; class Dog : public Animal{ public: using Animal::Animal; string voice() const{ return this->isAlive? "Woof! I'm "+this->name+"\n": ""; } }; class Cat : public Animal{ public: using Animal::Animal; string voice() const{ return this->isAlive? "Meow, I'm "+this->name+"\n": ""; } }; int main(void){ GenericObjectFactory<string,Animal,bool,string> animalFactory; animalFactory.add<Dog>("man's friend"); animalFactory.add<Cat>("^_^"); Animal *dog1=animalFactory.get("man's friend")(true,"charlie"); Animal *dog2=animalFactory.get("man's friend")(false,"fido"); Animal *cat =animalFactory.get("^_^")(true,"begemoth"); cout << dog1->voice() << dog2->voice() << cat ->voice(); return 0; }
したがって、 animalFactory
ファクトリは、最初に
animalFactory
テンプレートクラスから作成され、そのキーパラメータはキータイプ (この例では文字列ですが、整数値や列挙など他のものを使用すると便利な場合があります)これらの値は、クラスを作成するためにファクトリに追加する必要があるクラスです。 2番目の必須パラメーターとして-これらのクラスの基本クラス(および1つのクラスから継承する必要があります); 残りの2つのパラメーター(任意の数(ゼロを含む))は、ファクトリーによって作成されたクラスのコンストラクターの引数のタイプです。
その後、テンプレートメンバ関数
add
呼び出して、ファクトリにクラスを追加できます。実際には、ファクトリが登録する必要があるクラスを最初の唯一のパラメータとして、最初の唯一の引数として、このクラスの識別子、この場合は文字列(定義済みテンプレートクラスの最初のパラメーター)。
その後、楽しみが始まります。
get
メンバー関数は、識別子文字列を最初で唯一の引数として使用し、この識別子で見つかったクラスのいわゆるインスタンス生成子を返します。 Instantiatorは、(ファクトリーの特殊化中に指定された数と型の)引数のセットを受け取り、目的のクラスのインスタンスを作成してコンストラクターに渡す関数です。 必要なプロキシの一種で、その理由を説明します。 これは、コンストラクター自体が返された方法と非常に似ています。 :)同時に、インスタンシエーターは作成されたオブジェクトへのポインターを返しますが、このポインターは継承された型ではなく、 基本クラスのものです。 必要に応じて、
dynamic_cast
'threadを実行できます。
ところで、このインスタンス生成プログラムをどこかに保存することを妨げるものはありません。
auto catInstantiator=animalFactory.get("^_^");
、必要なときに後でオブジェクトを作成します。 Animal *cat=catInstantiator(true,"delayed cat"); Animal *cat=catInstantiator(false,"dead delayed cat");
:)
しかし、大丈夫、最終的には明らかでした。 また、私がこのメモを書いているのはベテランのプラスではなく、何らかの理由で工場を作りたいと思っている初心者向けです。 :)
ファクトリの実際の実装を見てみましょう。 作成または登録するときのファクトリー内のクラスの有無に関するすべての種類のチェック、および同様のロジックと他のあらゆる種類の便利さを含む、それが機能しないことなく意図的にすべてを切り取りました(または、QtからSTLにこれを移植するには遅すぎました)追加するのは難しくありません。 しかし、一般に、サンプルコードはできるだけシンプルで明白なものである必要があると考えています。もちろん、これらの避けられない悪魔的な詳細はありません。理解しやすく、これがまさに今必要なことです。
それだけです! 象じゃない? 彼の記事で提案されたrtorstenのソリューションを少なくとも3回は超えているように思えます。
- #include <map>
- テンプレート < クラス ID、 クラス Base、 クラス ...引数> クラス GenericObjectFactory {
- プライベート :
- typedef Base * ( * fInstantiator ) (引数... ) ;
- template < class Derived > static Base * instantiator ( Args ... args ) {
- 新しい Derived (引数... )を 返し ます。
- }
- std :: map < ID、fInstantiator >クラス;
- 公開 :
- GenericObjectFactory ( ) { }
- template < class Derived > void add ( ID id ) {
- classes [ id ] = & instantiator < Derived > ;
- }
- fInstantiator get ( ID id ) {
- クラス[ id ]を 返します。
- }
- } ;
ここで何が起こっているのか:
- 1行目:必要なのは、何らかの
map
コンテナです。 この場合、STL-evskyを使用します。 しかし、QMap
は問題なく機能します。 - 3行目:テンプレート定義。 Google 、 Wikipedia 、 Habr (FlexFerrum habraiserに関して )で魔法の3つのポイント(さまざまなテンプレート)について読むことができます。
- 5番目 :タイプ定義
fInstantiator
。 これは、Base
型(テンプレートパラメーター)のオブジェクトへのポインターを返す関数へのポインターであり、Args ...
型の引数を受け入れArgs ...
(テンプレートパラメーターでもありますが、任意の数があります。可変長テンプレート↑を参照)get
メンバー関数が返します(行16 ) - 6番目 :まさにそのような機能、定型的なものであると同時に、私たちが話したまさに
instantiator
。 これは、この関数がファクトリに追加されたDerived
クラスで特殊化できるようにするために必要です。これにより、テンプレートメンバ関数がvoid add<class Derived>
にし、特殊な関数アドレス(このクラスのみのオブジェクトを作成)を取得し、std::map classes
値としてstd::map classes
コンテナをstd::map classes
します。ここで、キーは、タイプID
(ファクトリテンプレートの最初のパラメータ)のvoid add<class Derived>
関数の引数です。instantiator
簡単にするためにstatic
instantiator
宣言static
れていますが、本当に必要な場合は、いわゆるメンバー関数ポインターを使用できます 。 - 7行目では、オブジェクトが実際に作成され、このオブジェクトのコンストラクターには、ファクトリーの特殊
instantiator
中に指定された数と型の引数が渡されます。 もちろん、同じ数と型で、そしてもちろん、これはすべてコンパイル時に既知であり、ファクトリに追加されたクラスのいずれのコンストラクタもこれらの型の引数を受け入れない場合、コンパイラはエラーをスローします。 - 9行目:実行時にファクトリに追加されたクラスが格納されるコンテナ。 工場の中核。 キーは、既に述べたように、テンプレートの最初のパラメーターで指定されたタイプを持ちます-すべてが単純です。 しかし、意味はテンプレート関数への魔法のポインタです。 (
Base
から継承されたクラスによる)特殊化の後、関数は同じシグネチャを持ちます。つまり、常に(任意のテンプレートパラメーターで)既知の等しい数とタイプの引数を取り、常に1つの既知のタイプの値-Baseタイプのオブジェクトへのポインターを返します。 つまり、元の問題は解決されました! :)。 - 14行目:
add
関数は、std::map classes
id
型ID
(クラステンプレートの最初のパラメーター)と静的な特殊instantiator
メンバー関数のアドレスのペアを追加します。 すでに言った。 - 17行目 (数字の練習をしたかった)行:
get
関数はinstantiator
目的のクラスのinstantiator
へのポインターを返します。 オブジェクトが作成されていない時点で、ユーザーは発行されたinstantiator
を使用して自分でオブジェクトを作成しinstantiator
。 これはまさに私が達成したかったものです。
私はC ++を初めて使用することに言及しましたか? :)だから、私はいくつかの場所に嘘をついた可能性が高い(または、たぶんいくつかの場所にいない )...
最後のリストを取得して、
#include "factory.h"
代わりに最初のリストに貼り付け、お気に入りのコンパイラで結果を確認し、 C ++ 11標準のサポートを含めることを忘れないでください。
PS一般的に、最初は工場のバージョンを最初の記事にコメントしたかったのですが、何らかの理由で私は投稿を書きました...とにかく、LANのエラーについて話し合うのは非常にうれしいです... :)
PPSああ! ただ送信したかったのですが、 イデオネがいたことを思い出しました! 例を記入しました: 感心することができます 。 :)