1

考虑以下类结构:

class Filter
{
    virtual void filter() = 0;
    virtual ~Filter() { }
};

class FilterChain : public Filter
{
    FilterChain(collection<Filter*> filters)
    {
         // copies "filters" to some internal list
         // (the pointers are copied, not the filters themselves)
    }

    ~FilterChain()
    {
         // What do I do here?
    }

    void filter()
    {
         // execute filters in sequence
    }
};

我在库中公开这个类,所以我无法控制它的使用方式。

我目前有一些关于持有指针的Filter对象所有权的设计问题。FilterChain更具体地说,这里有两种可能的使用场景FilterChain

  • 场景 A:我的库中的一些函数正在构建一个(可能很复杂)过滤器链,根据需要分配内存,并返回一个新分配的FilterChain对象。例如,其中一个函数从一个文件构造一个过滤器链,它可以描述任意复杂的过滤器(包括过滤器链的过滤器链等)。一旦工作完成,函数的用户负责对象销毁。
  • 场景 B:用户可以访问一堆Filter对象,并希望以特定方式将它们组合到过滤器链中。用户构造FilterChain对象供自己使用,然后在完成后销毁它们。当引用它们的对象被销毁时,这些Filter对象不能被销毁。FilterChain

现在,管理对象所有权的两种最简单的方法FilterChain是:

  • FilterChain拥有Filter对象。这意味着 所引用的对象FilterChainFilterChain的析构函数中被销毁。这与场景 B 不兼容。
  • FilterChain拥有这些Filter对象。这意味着FilterChain' 的析构函数什么都不做。现在方案 A 存在一个问题,因为用户必须知道所有Filter涉及的对象的内部结构才能在不遗漏任何一个的情况下将它们全部销毁,因为父对象FilterChain本身不会这样做。那只是糟糕的设计,并要求内存泄漏。

因此,我需要一些更复杂的东西。我的第一个猜测是设计一个带有可设置布尔标志的智能指针,指示智能指针是否拥有该对象。然后,不是采用指向Filter对象的指针集合,而是采用指向对象FilterChain的智能指针Filter集合。当FilterChain' 的析构函数被调用时,它会破坏智能指针。当且仅当设置了指示所有权的布尔标志时,智能指针本身的析构函数将销毁指向的对象(Filter对象) 。

我觉得这个问题在 C++ 中很常见,但是我在网上搜索流行的解决方案或聪明的设计模式并不是很成功。确实,auto_ptr在这里并没有真正的帮助,而且shared_ptr似乎有点矫枉过正。那么,我的解决方案是个好主意吗?

4

4 回答 4

3

这里的智能指针并不过分:显然你有一个设计问题,需要仔细考虑对象的生命周期和所有权。如果您希望能够在运行时重新修补过滤器图中的过滤器,或者能够创建复合FilterChain对象,则尤其如此。

使用shared_ptr将一举消除大部分这些问题,并使您的设计更加简单。我认为这里唯一的潜在问题是您的过滤器是否恰好包含循环。如果您有某种反馈循环,我可以看到这可能会发生。在那种情况下,我建议让所有Filter对象都归一个类所有,然后FilterChain存储指向对象的弱指针Filter

我敢打赌,过滤阶段的执行时间将远远超过取消引用智能指针的额外开销。shared_ptr设计得非常轻巧。

于 2010-07-28T21:18:28.290 回答
2

过滤器是否如此之大以至于您在创建时不能简单地对每个过滤器进行深层复制FilterChain?如果你能做到这一点,那么你所有的问题都会消失:FilterChain总是会自行清理。

如果由于内存问题这不是一个选项,那么使用shared_ptr似乎是最有意义的。调用者必须负责为shared_ptr它关心的每个对象保留 a,然后当它是d时,调用者FilterChain将知道是否删除特定的过滤器。delete

编辑:正如尼尔所说,Filter需要一个虚拟析构函数。

于 2010-07-28T19:22:53.150 回答
0

FilterChain应该有一个单独的DeleteAll()方法,它迭代集合和delete过滤器。它在场景 A中被调用,而在场景 B中不被调用。这确实需要 FilterChain 用户的一些智能,但不再需要记住delete和反对他们曾经做过的事情new。(他们应该能够处理,否则他们应该得到内存泄漏)

于 2010-07-28T19:18:36.557 回答
0

我会选择不拥有 Filter 对象的 FilterChain。然后,在您的库中,当您需要从文件中加载 FilterChain 时,您将拥有另一个 Loader 对象,该对象负责 Filter 对象的生命周期。因此,FilterChain 将始终适用于库加载的链和用户创建的链。

于 2010-07-28T20:52:15.097 回答