36
struct InkPen
{
  void Write()
  {
    this->WriteImplementation();
  }

  void WriteImplementation()
  {
    std::cout << "Writing using a inkpen" << std::endl;
  }

};

struct BoldPen
{
  void Write()
  {
    std::cout << "Writing using a boldpen" << std::endl;
  }
};

template<class PenType>
class Writer : public PenType
{
public:
  void StartWriting()
  {
    PenType::Write();
  }
};

int main()
{
  Writer<InkPen> writer;
  writer.StartWriting();
  Writer<BoldPen> writer1;
  writer1.StartWriting();
  return 0;
}

上記のコードは、学習ポリシー ベースの設計の一環として作成しました。上記のコードについていくつか質問があります

1 - この実装は正しいように見えますか? つまり、それは本当にポリシーベースの設計のように見えますか?

2 - あらゆる種類のペンをライターに接続できるようになりました。しかし、既定のコンストラクターがない (パラメーター化されたコンストラクターのみ) ペンを取得した場合はどうすればよいでしょうか? この状況をどのように処理しますか?

template<class PenType>
class Writer : public PenType
{
public:
  void StartWriting()
  {
    PenType::Write();
  }
};

3 - 上記のコードが次のように使用される場合

Writer<InkPen> writer;

コンパイラはPenTypeInkPenに置き換えると思います。はいの場合、基本クラス名 ( PenType::Write() ) を前に付ける代わりに、 StartWriting( )からWrite()だけを呼び出すことができないのはなぜですか?

4 - ポリシー ベースの設計では、意味的に無効なクラスから派生させる必要があると思います。上記のコードでは、ライターがペンを使用するという理由だけで、ライターがペンから派生しています。しかし、作家がペンであると言うのは意味的に無効です。これに対処するための他のより良い方法はありますか、それともここで何かが欠けていますか?

何かご意見は?

4

4 に答える 4

29

クラスを実装する方法は次のとおりです。

template<class PenType>
class Writer
{
public:
  Writer(const PenType& pen = PenType()) : pen(pen) {}

  void StartWriting()
  {
    pen.Write();
  }

private:
  PenType pen;
};

これにより、ユーザーは特定の Pen オブジェクトをコンストラクターに渡すことができます。これは、デフォルトのコンストラクターがない場合、または使用したくない場合です。次に、PenType オブジェクトを省略できます。 'デフォルトのコンストラクターで作成できることをうれしく思います。C++ 標準ライブラリは、多くのクラスで同じことを行います (たとえば、コンテナー クラスのアロケーターを考えてみてください)。

継承を削除しました。実際には何も追加していないように見えます (そして、問題を引き起こす可能性があります。おそらく、Writer クラスのユーザーに PenType::Write 関数を直接呼び出してほしくないでしょう。代わりにプライベート継承を使用することもできますが、多くの場合、合成はよりシンプルで伝統的なデザイン。

一般に、ポリシー ベースの設計では継承は必要ありません。メンバーとして追加することも同様に機能します。継承を行う場合は、非公開にして、#4 で述べた問題が発生しないようにします。

于 2009-05-16T16:41:39.770 に答える
17

これは、ポリシーベースのスマート ポインター実装の良い例のように見えます: linkAndrei Alexandrescuは、彼の著書の 1 つで、ポリシーベースのスマート ポインターの実装について説明しています。今のあなたの質問について。私はこのようなことについてある程度の経験がありますが、私の言葉を当然のことと考えるには十分ではありません:

広告 1 & 4. ポリシーベースの設計は、継承よりもテンプレートに関するものだと思います。次のように、テンプレート クラスを記述し、テンプレート引数はポリシー クラスです。

template<class FooPolicy, class BarPolicy>
class Baz {
    // implementation goes here
};

次に、クラスでポリシー クラスのメソッドを使用します。

void Baz::someMethod(int someArg) {
    FooPolicy::methodInit();
    // some stuff
    BarPolicy::methodDone();
}

ポリシーは状態を必要としないことが多いため、この例では静的メソッドを使用しています。その場合は、継承ではなく構成によってポリシーの状態を組み込みます。

template<class FooPolicy, class BarPolicy>
class Baz {
  private:
    FooPolicy::State fooState; // might require 'typename' keyword, I didn't
                               // actually tried this in any compiler
    // rest of the Baz class
};

広告 2. テンプレートの特殊化を作成できます。メイン クラスとそのポリシーの特定の組み合わせに対して、任意のメソッドまたはコンストラクターの特別なバージョンを作成できます。

template <>
Baz<SomeConcreteFooPolicy, SomeConcreteBazPolicy>::Baz(someArgument)
   : fooState(someArgument)
{
    // stuff here
}

少しでもお役に立てれば幸いです。

マイク

于 2009-05-16T16:22:39.067 に答える
6

このスレッドが古いことは承知していますが、最初の投稿には重大な欠陥があり、このスレッドは Google の上位の結果の 1 つです。

publicポリシーベースの設計に継承を使用しないでください。これは、「has-a」/「uses-a」ではなく「is-a」となります。privateしたがって、継承を使用する必要があります。

于 2013-06-17T19:13:26.700 に答える
5

1 - この実装は正しいように見えますか? つまり、本当にポリシーベースのデザインのように見えますか?

ポリシー クラスは、動作を組み合わせてさまざまな組み合わせを生成することから有用性を引き出します。このようなテンプレート パラメーターが 1 つしかない場合、それはあまりポリシー クラスではありません。

2 - あらゆる種類のペンをライターに接続できるようになりました。しかし、既定のコンストラクターがない (パラメーター化されたコンストラクターのみ) ペンを取得した場合はどうすればよいでしょうか? この状況をどのように処理しますか?

繰り返しますが、これはポリシー クラスの奇妙な例です。ただし、質問に直接答えるには、PenType を受け入れるコンストラクターを提供できます。おそらく、PenType からの継承を避け、代わりにメンバーとして保存する必要があります (ポリシー クラスとそのポリシーを緊密に結合する必要はありません)。

コンパイラは PenType を InkPen に置き換えると思います。はいの場合、基本クラス名 (PenType::Write()) を前に付ける代わりに、StartWriting() から Write() だけを呼び出すことができないのはなぜですか?

クラス テンプレートから継承する場合は、this->member または BaseClass::member を指定する必要があります。

4 - ポリシー ベースの設計では、意味的に無効なクラスから派生させる必要があると思います。上記のコードでは、ライターがペンを使用するという理由だけで、ライターがペンから派生しています。しかし、作家がペンであると言うのは意味的に無効です。これに対処するための他のより良い方法はありますか、それともここで何かが欠けていますか?

上記のように PenType をメンバーとして保存します。継承の密結合関係を回避するため、常に継承よりも構成を優先してください。

于 2010-06-29T02:32:29.890 に答える