为了使讨论清楚,我将以非常一般的方式描述问题,即我既不会提供真实类的名称,也不会描述域/上下文(但是,如果它转向,我可能会紧急)。
想象一下班级A
。让这个类有2个不可变的字段,例如x
和y
(请注意,这些可能是大对象,即效率低下复印)。另外,让这些x
和y
为主要字段,即只有它们用于==
/ !=
运算符的实现以及哈希计算功能。
由于A
在<{1}}和x
方面属于不可变,因此我们的想法是让多个y
实例(比如{{ 1}}和A
)a1
a2
(即a1.x == a2.x
)隐式拥有对这些a1.y == a2.y
的共享访问权限a1 == a2
,因此没有不必要的重复。
此外,现在假设x
中有另一个字段:y
,次要和可变,并用作排序行为调整A
。根据设计,希望在z
的相等实例之间共享此字段。因此,如果我调用A
,此更改也会影响A
,因为他们对a1.setZ(...)
的访问权限是共享。
因此,我们最终得到一个具有纯值语义的类a2
,但是在相同的实例中共享其成员隐式。 AFAIK这种模式称为Flyweight或别名。
在我们提出问题之前还有一个细节。项目中的大多数类都是使用 Pimpl 成语:
实现的z
和类A
不是排除。这就是为什么提出的实施上述方案的想法如下:
private:
class Private;
Private* p;
而非原始用户
Pimpl 成语; A
; A::Private
的构造函数中检查是否共享
指针到合适的A::Private
已存在于集合中
(当然,使用A
和A::Private
),如果是,则只需设置x
它,否则创建y
的新实例并存储
此集合中的共享指针,同样将p
设置为它; A::Private
的析构函数应该从集合中删除共享指针到p
。这看起来像是最简单直观的实现。但问题是,由于此全局集将共享指针保存到A::Private
,这意味着即使相应的this
的所有实例都被销毁,引用计数器也将保留在A::Private
上,即它永远不会到达A
,因此永远不会释放内存。
我认为如果某些共享指针提供一种方法来设置引用计数器的下限,那将是一件好事。在这种情况下,例如,我只需将其设置为1
,这意味着当它达到0
时,它会释放内存。不幸的是,我还没有在流行的库(Boost,Qt,Poco等)中找到任何这种行为的实现。当然,我可以为我的问题做手动引用计数,但这感觉不对,闻起来像重新发明轮子。
可能还有其他方法可以解决这个问题。期待您的建议。
注意:我想立即拦截任何建议将问题转换为指针语义,我很清楚。我完全需要上述方案的解决方案。
答案 0 :(得分:2)
如果我正确理解了你的设计问题,那么我会让全局集包含弱,非拥有指针(例如weak_ptr<>
),它们能够检查它们是否是悬空,但他们不会增加参考数量。
std::vector<std::weak_ptr<Private>> _objects;
因此,当所有拥有对象的共享指针被销毁时,该对象也将被销毁**。
现在你的全局集将留下悬空weak_ptr<>
,但好处是你可以检查该指针是否指向一个活着的对象(使用lock
()成员函数获取可能为空的shared_ptr<>
。如果没有,则不会取消引用它:
// 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
类型的新对象并向其返回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<>
替换为boost::shared_ptr<>
,将std::weak_ptr<>
替换为boost::weak_ptr<>
,并将lambdas替换为lambdas使用正确定义的仿函数。
希望这有帮助。
答案 1 :(得分:0)
您检查了Boost.Flyweight吗?