web-dev-qa-db-ja.com

工場、ベクター、スマートポインター-デザインの質問

したがって、私のビジネスコードにはいくつかのオブジェクトが必要です。
必要なオブジェクトの数がわからず、正確な型もわかりません(多態性が関係しているため)。
私にとって、それはファクトリーパターンを採用する正当な理由に聞こえます。

私のコードは次のようになります:

std::vector<AbstractBaseClass*> objectList;
Factory f;
objectList = f.create("path/to/config.txt");

ファクトリのcreate-methodのプロトタイプは次のようになります。

std::vector<AbstractBaseClass*> Factory ::create(std::string configFile)

朗報、その働き!
ファクトリは設定を読み取り、作成するオブジェクトの数とそれらが持つ具象タイプを決定します。

よく検索しましたが、単一のオブジェクトではなくコンテナを返すファクトリの例は見つかりませんでした。したがって、私の質問:これは良いスタイルですか?

このようにして、解析/作成プロセス全体がビジネスロジックから隠されるので、私はそう思います。ロジックは、オブジェクトの束を持つコンテナがあることを知っているだけです。しかし、あなたは他の意見を持っていますか?
ご注意ください:このプロジェクトはすべて、良いOOP習慣を学ぶことに関するものです。

わかりました、このアプローチは大丈夫だと想像してください。
私は学びました、生のポインタは悪です。 (OK、それらは定義ごとに悪ではありませんが、私はそれらを避けようとします)。
そこで、いくつかのスマートポインタに移動します。このマシンでboostC++11が不足しています。auto_ptrで始めています。

OK、新しいアプローチ:

std::vector< auto_ptr<AbstractBaseClass> > objectList;
Factory f;
objectList = f.create("path/to/config.txt");

そして工場:

std::vector< auto_ptr <AbstractBaseClass> > Factory ::create(std::string configFile)

これは悪に見えます。
現時点では、クレイジーなSTLコンパイラエラーが発生しているため、コンパイルされません。
しかし、それがコンパイルされることを想像してください。
これは良いですか?

私はそのような構造物を見たことがない-スマートポインタのコンテナを返すファクトリ。そして、私はその専門家ではないので、あなたはこれについてどう思うか尋ねたいと思います。

関連するメモとして、どのスマートポインターを使用する必要がありますか?
オブジェクトの所有者はビジネスロジックだけなので、unique_ptrと思います。ただし、unique_ptrコンテナーを返すことができるかどうかはわかりません。 shared_ptrは実装が簡単だと思います。

5
lugge86

C++を主にOOP言語として使用する場合、何らかの形でポインタを処理する必要があります。ほとんどすべてのポインタの問題に対する答えは_std::unique_ptr_です。ポリモーフィズムを使用できるようにしながら、かなり価値に近いセマンティクス。その唯一のオーバーヘッドは構文の乱雑です。これは、

  • 生のポインタ。ポインタには固有の所有権セマンティクスがないため。参照のような借用のセマンティクスとコピーのような所有権移転の両方にポインターが使用されるのを見てきました。それはドキュメントにあるかもしれませんが、それは確かにソースコードや型システムでエンコードされていません。
  • c ++ 11 _std::shared_ptr_これには参照カウントのための追加のオーバーヘッドがあるため。
  • セマンティクスが混乱しているため、非推奨の_std::auto_ptr_です。コピーは移動のように動作します。これには、移動したポインターをソースから消去できるように、コピー元がconstでないことも必要です。 (また、_std::vector_のコピー代入演算子はconst参照を期待するため、_vector<auto_ptr<T>>_はコピーできません。)
  • 型を正しく実装するのは難しいため、手書きのPimplラッパー。正しい実装とは言えない実装によるメモリリークとセグメンテーション違反が十分にあります(ヒント:nullポインターを実装として使用できないようにする)。実際、Pimplsはプライベート_unique_ptr_フィールドの観点から実装することをお勧めします。これにより、必要なすべてのリソース管理が処理されます。

ただし、Pimplをブリッジパターンとして使用すると、値ベースのAPIを使用しながら、ポインタのようなセマンティクスで多相クラス階層を設計できます。特に、_vector<BaseClass>_のより便利な表記として_vector<unique_ptr<BaseClassIf>>_を使用できます。

_std::unique_ptr_の「問題」は、C++ 11が必要であることです。このポインタ型の核となる考え方は、コピーはできないが、移動はできるということです。したがって、所有権は常に明確に定義されています。値によってオブジェクトを返す場合、C++ 11では移動のセマンティクスによって値によって_unique_ptr_を返すことができるC++ 11を除いて、オブジェクトはコピーセマンティクスの対象になります(実際のコピーが最適化されていない場合でも)。

一般に、スマートポインターのコンテナーを使用することは完全に問題なく、他の方法よりも優れています。あなたの場合、_auto_ptr_に関してC++ 03の制限されたセマンティクスのために失敗します。 C++ 11(現在のすべてのメインストリームコンパイラでサポートされている)にアップグレードできない場合、2つの現実的な選択肢があります。生のポインタを使用するか、カスタムPimplでポインタをラップします。努力が正当である場合、Pimplを使用します。それほど複雑なコードではありませんが、必要なすべての操作を転送するように注意する必要があります。

_class BaseClassIf {
public:
  virtual ~BaseClassIf() {}
  virtual void someOperation() = 0;
  virtual BaseClassIf* copy() = 0; // { return new T(*this); }
}

class BaseClass {
  BaseClassIf* impl;
  void assertInvariant() { if (!impl) throw ...; }
public:
  BaseClass(BaseClassIf* impl) : impl(impl) { assertInvariant(); }

  ~BaseClass() { if (impl) delete impl; impl = 0; }

  BaseClass(const BaseClass& o) : impl(o.impl->copy()) { assertInvariant(); }

  void someOperation() { impl->someOperation(); }

private:
  BaseClass& operator=(const BaseClass&);
  // Cannot be reasonably overloaded as a value-based copy,
  // e.g. "*impl = *rhs.impl" since the exact types are unknown.
  // Cannot be overloaded as a reference copy,
  // e.g. "impl = rhs.impl" since that violates pointer ownership.
};
_

BaseClassラッパーでは正確なタイプが不明であるため、インターフェイスはコピーコンストラクターにアクセスするための準備を行う必要があることに注意してください。これはたまに役立つテクニックです(たとえば、テンプレートパラメーターを非表示にするための「型消去」として)。しかし、ここでは、ポリモーフィズムを使用することからの迷惑なものです。また、インターフェースに共通のvirtual BaseClassIf::operator=(const BaseClassIf&)メソッドを含めない限り、アダプターのコピー割り当て演算子を定義できないことに注意してください。ただし、ほとんどのオブジェクト階層は、共通のベースから有用なコピーを実行できません。

9
amon

最初の質問:「ファクトリがメソッドをコンテナに戻すのは良いスタイルですか?」:

IMHO std::vector<AbstractBaseClass*>のようなタイプには特別なものはありません。少なくとも、原則として、他のタイプのようなユーザー定義タイプです。次のようなtypedefエイリアスを作成できます

 typedef std::vector<AbstractBaseClass*> MyAbstractBaseClassContainer;

それがより良い抽象化を提供する場合、または新しいクラス内にラップする場合。後者では、コンストラクターをプライベートにする機会が与えられるため、静的クラスメンバーファクトリメソッドとは異なる方法でその型のオブジェクトを作成することを禁止します。しかし、実際には「良い」工場では必ずしも必要ではありません。std::vector<AbstractBaseClass*>がニーズに合っている場合は、先に進んでください。

some_smart_ptr<AbstractBaseClass>よりもAbstractBaseClass*を優先する必要がある場合、特にvectorのコンテキストでは、最初の質問から完全に独立した質問です。 here または here 、または@amonの回答など、すでにその質問に対する良い回答があったため、私はこれに回答しようとはしていません。より良い。

5
Doc Brown