4

次のインターフェイスを検討してください (まだ C++98 を使用しているため、ダム ポインターが使用されます)。

class WidgetMaker {
    virtual Widget* makeWidget() = 0;
};

次の可能性のある実装で

class SpecificWidgetMaker: public WidgetMaker {
    Widget* makeWidget() {
        return new SpecificWidget();
    }
};

Widget は仮想デストラクタを持つ基本クラスであり、SpecificWidget はそれを拡張します。私の同僚は、WidgetMaker インターフェイスに次のメソッドを含める必要があると主張しています。

virtual void freeWidget(Widget* widget);

根拠は、このように makeWidget 実装に標準の新しい割り当てを使用することを強制しないことです。カスタム プール アロケータを使用するか、ウィジェットがステートレスなどの場合に常に同じグローバル インスタンスを返すことができます。

このような設計は一般的に悪い考えだと思います。クライアント コードを複雑にし、KISS と YAGNI に違反し、unique_ptr への移行を (今後 20 年間で私たちの組織ではありそうもないことですが) 困難にします。自分の気持ちを信じるべきですか?抽象ファクトリ インターフェイスの一部としてのフリー メソッドが正当化されるのはどのような場合ですか?

4

2 に答える 2

7

あなたの友人が提案した解決策 (実際には、あなたの元の解決策も) の問題は、それが不必要に重要なプロトコルを持っていることです。Widgetviaを取得したらmakeWidget、(直接、またはファクトリの何らかのメソッドを呼び出して) 割り当てを解除することを忘れないでください。これは脆弱であることが知られています。すぐに壊れるか (Widgetリークの原因となる)、クライアント コードが非常に複雑になります。

のインターフェイスを見ると、カスタムの削除オブジェクトstd::shared_ptr::shared_ptr(...)を使用できることがわかります。

したがって、おそらくスマート ポインターtypedefとは何か (または同等のこと)が可能です。Widget

using WidgetPtr = std::shared_ptr<Widget, ...>

Widgetの割り当てが解除されたときにファクトリが何らかのアクションを実行する必要があると後で判断した場合typedefは、 をカスタム デリータを使用するものに変更できます。このカスタム デリータは、オブジェクトが削除されていることをファクトリに通知できます。

これの主な利点は、ユーザーが a の割り当てを解除することを覚えておくという負担を取り除くことWidgetです。

于 2016-02-10T13:29:19.463 に答える
0

ここで従うべき一般的な経験則はありません。それはすべて、実装の詳細に依存しますWidget

サブクラスのインスタンスを適切に破棄するために実行する必要があるすべてのことをWidget通常のデストラクタで処理できる場合freeWidget、ファクタで明示的な () を宣言しても何の役にも立ちません。付加価値はありません。

一方、なんらかの理由でデストラクタで処理できない何かを行う必要がある場合は、明らかに、ウィジェットを破棄するための明示的なメソッドが必要です。

現時点では、この種の特別な処理は必要ないかもしれませんが、将来的には必要になると予想されます。freeWidgetその場合、後で大量のコードを書き直さないように、明示的な () メソッドを宣言するのが理にかなっています。

() アプローチを採用する場合は、このポリシーを適用するためfreeWidgetに、すべてのサブクラスのデストラクタを非公開にすることを検討してください (おそらく何らかの適切なfriend宣言を使用して、何かが実際にこれらのものを破壊できるようにします)。

明示的な () が必要な理由の 1 つの例は、freeWidget例外です。デストラクタから例外をスローするのは...厄介です。許可されていますが、特定の制限があります。そのため、ウィジェットを破棄すると例外がスローされる可能性がある場合、freeWidget() を使用すると、例外のスローをより詳細に制御でき、その場合は破棄されたウィジェットを正しくクリーンアップしてから実行できます。

于 2016-02-10T13:31:00.957 に答える