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,使得(在我们的组织中不太可能在未来 20 年内)过渡到 unique_ptr 更加困难。我应该相信我的感觉吗?什么情况下自由方法作为抽象工厂接口的一部分是合理的?

4

2 回答 2

7

您朋友提出的解决方案(实际上也是您原来的解决方案)的一个问题是它有一个不必要的重要协议。一旦获得Widgetvia makeWidget,您需要记住释放它(直接释放,或通过调用工厂的某些方法)。众所周知,这很脆弱——它要么会很快崩溃(导致Widget泄漏),要么会使客户端代码真正复杂化。

如果你看一下它的接口std::shared_ptr::shared_ptr(...),你可以看到它可以带一个自定义的删除器对象

因此,也许您可​​以(或等效的)智能指针typedef到底是什么:Widget

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

Widget如果您稍后决定在释放a 时工厂需要执行某些操作,您可以将其更改typedef为使用自定义删除器的操作,并且此自定义删除器可以通知工厂正在删除对象。

这样做的主要优点是它消除了记住Widget从用户释放 a 的责任。

于 2016-02-10T13:29:19.463 回答
0

我想说这里没有一般的经验法则。这一切都取决于您Widget的实施细节。

如果正确销毁任何子类的实例所需的一切Widget都可以在其普通析构函数中处理,那么freeWidget在您的因子中声明显式 () 没有任何用处。它不会增加任何价值。

另一方面,如果需要在析构函数中完成某些无法处理的事情,无论出于何种原因,那么显然,您需要一个显式的方法来销毁您的小部件。

也许此时不需要任何这种特殊处理,但您预见到将来需要它。在这种情况下,声明一个显式的 () 方法是有意义的freeWidget,以避免以后重写一堆代码。

如果您决定使用freeWidget() 方法,您可能会考虑做的一件事是将所有子类的析构函数设为私有(很可能带有一些合适的friend声明,因此某些东西实际上可以销毁这些东西),以强制执行此策略。

您可能想要显式freeWidget() 的一个示例是异常。从析构函数中抛出异常是......敏感的。这是允许的,但它带有某些......限制。因此,如果销毁您的小部件可能会引发异常,那么在这种情况下,使用freeWidget() 将允许您更好地控制引发异常,并在这样做之前正确清理被破坏的小部件。

于 2016-02-10T13:31:00.957 回答