2

为了使讨论清楚,我将以非常笼统的方式描述问题,即我既不会提供真实类的名称,也不会描述域/上下文(但是,如果事实证明是紧迫的)。

想象课A。让这个类有 2 个不可变字段,例如xy(请注意,这些可能是潜在的大对象,即复制效率低下)。此外,让这些xy成为主要字段,即仅在==/!=运算符以及哈希计算功能的实现中使用它们。

由于在and方面A不可变的,因此我们的想法是让具有(ie )的(say and ) 的多个实例隐式地共享对那些and的访问权限,这样就不会出现不必要的重复。xyAa1a2a1.x == a2.x a1.y == a2.ya1 == a2xy

此外,现在想象在A:中有另一个字段z,它是次要的和可变的,并用作A. 通过设计,希望使该字段在too的相等实例之间共享。因此,如果我调用此更改也会影响,因为他们的访问权限是共享的。A a1.setZ(...)a2z

结果,我们最终得到了一个A具有纯值语义的类,但在相等的实例中隐式地共享其成员。AFAIK 这种模式称为Flyweightaliasing

在我们转向这个问题之前,还有一个细节。项目中的大多数类都是使用Pimpl idiom 实现的:

private:
  class Private;

  Private* p;

阶级A不是排斥。这就是为什么提出的实施上述方案的想法如下。

  1. 在Pimpl成语中使用共享指针A::Private不是原始 指针;
  2. 拥有一组指向;的全局共享指针A::Private
  3. 在构造函数中A检查集合中是否已经存在指向合适的共享指针(当然是利用) ,如果是,则简单地设置 它,否则创建新实例并将指向它的共享指针存储 在此集合中,类似地设置它;A::PrivatexypA::Privatep
  4. A::Private的析构函数应该从集合中移除共享指针this

这看起来是最直接和直观的实现。然而,问题在于,由于这个全局集合拥有一个指向 的共享指针A::Private这意味着即使对应的所有实例A都被销毁,引用计数器仍将保持在 上1,即永远不会到达0,因此永远不会释放内存。

我认为如果一些共享指针能够提供一种方法来设置引用计数器的下限,那将是一件好事。例如,在这种情况下,我只需将其设置为1这意味着当它到达时1会释放内存。不幸的是,我没有在流行的库(Boost、Qt、Poco 等)中找到这种行为的任何实现。当然,我可以为我的问题进行手动引用计数,但这感觉不对,而且闻起来像是重新发明轮子。

可能还有其他方法可以解决此问题。期待您的建议。

注意:我想立即拦截任何将问题转换为我很清楚的指针语义的建议。我需要完全针对上述方案的解决方案。

4

2 回答 2

2

如果我正确理解您的设计问题是什么,那么我会让全局集包含的、非拥有的指针(例如weak_ptr<>),它们能够检查它们是否悬空,但它们不会增加引用计数。

std::vector<std::weak_ptr<Private>> _objects;

因此,当一个对象的所有拥有的共享指针都被销毁时,该对象也将被销毁**。

现在你的全局集合将留下一个悬空weak_ptr<>,但好的是你可以检查该指针是否指向一个活着的对象(使用lock() 成员函数来获得一个可能的 null shared_ptr<>。如果它没有t,你不会取消引用它:

// A simple, hypothetical loop through the collection of objects
// which does something, but checks whether the pointers are
// dangling before doing that something on a possibly dead object
// that would be Undefined Behavior)
std::for_each(_objects.begin(), _objecs.end(), [] (std::weak_ptr<Private> p) 
{
    std::shared_ptr<Private> sp = p.lock();
    if (sp != nullptr)
    {
        sp->callMember(); // For instance...
    }
});

如果您还想weak_ptr<>在对象被销毁后从集合中删除对应的对象,那么您可以使用自定义删除程序。当对象被销毁时,您的例程将被调用,并将传递指向该对象的指针:此时,在解除分配之前,您可以从集合中擦除相应的元素。

例如,实例化新类型对象并向其A返回 a的函数shared_ptr可能如下所示:

static std::shared_ptr<object> make_A()
{
    std::shared_ptr<Private> sp(
        new Private(),   // Instantiate the object
        [] (Private* p)  // Set up the custom deleter...
        {
            // Remove the corresponding element from the vector...
            _objects.erase(
                // ...so let's find that element!
                std::find_if( 
                    _objects.begin(),
                    _objects.end(),
                    [p] (std::weak_ptr<priv> wp)
                    {
                        // lock() will return a null pointer if wp is dangling
                        std::shared_ptr<priv> sp = wp.lock();

                        // In case wp is not dangling, return true if and only
                        // if it points to the object we're about to delete
                        return ((sp != nullptr) && (sp.get() == p));
                    })
                );
        });
}

在这里,我假设 C++11,你可以在 C++03 中通过用正确定义的函子替换std::shared_ptr<>with boost::shared_ptr<>std::weak_ptr<>withboost::weak_ptr<>和 lambda 轻松地做同样的事情。

希望这可以帮助。

于 2013-03-10T00:03:24.993 回答
0

你检查过 Boost.Flyweight了吗?

于 2013-04-08T14:01:50.717 回答