为了使讨论清楚,我将以非常笼统的方式描述问题,即我既不会提供真实类的名称,也不会描述域/上下文(但是,如果事实证明是紧迫的)。
想象课A
。让这个类有 2 个不可变字段,例如x
和y
(请注意,这些可能是潜在的大对象,即复制效率低下)。此外,让这些x
和y
成为主要字段,即仅在==
/!=
运算符以及哈希计算功能的实现中使用它们。
由于在and方面A
是不可变的,因此我们的想法是让具有(ie )的(say and ) 的多个实例隐式地共享对那些and的访问权限,这样就不会出现不必要的重复。x
y
A
a1
a2
a1.x == a2.x
a1.y == a2.y
a1 == a2
x
y
此外,现在想象在A
:中有另一个字段z
,它是次要的和可变的,并用作A
. 通过设计,希望使该字段在too的相等实例之间共享。因此,如果我调用此更改也会影响,因为他们的访问权限是共享的。A
a1.setZ(...)
a2
z
结果,我们最终得到了一个A
具有纯值语义的类,但在相等的实例中隐式地共享其成员。AFAIK 这种模式称为Flyweight或aliasing。
在我们转向这个问题之前,还有一个细节。项目中的大多数类都是使用Pimpl idiom 实现的:
private:
class Private;
Private* p;
阶级A
不是排斥。这就是为什么提出的实施上述方案的想法如下。
- 在Pimpl成语中使用共享指针而
A::Private
不是原始 指针; - 拥有一组指向;的全局共享指针
A::Private
- 在构造函数中
A
检查集合中是否已经存在指向合适的共享指针(当然是利用) ,如果是,则简单地设置 它,否则创建新实例并将指向它的共享指针存储 在此集合中,类似地设置它;A::Private
x
y
p
A::Private
p
A::Private
的析构函数应该从集合中移除共享指针。this
这看起来是最直接和直观的实现。然而,问题在于,由于这个全局集合拥有一个指向 的共享指针,A::Private
这意味着即使对应的所有实例A
都被销毁,引用计数器仍将保持在 上1
,即永远不会到达0
,因此永远不会释放内存。
我认为如果一些共享指针能够提供一种方法来设置引用计数器的下限,那将是一件好事。例如,在这种情况下,我只需将其设置为1
这意味着当它到达时1
会释放内存。不幸的是,我没有在流行的库(Boost、Qt、Poco 等)中找到这种行为的任何实现。当然,我可以为我的问题进行手动引用计数,但这感觉不对,而且闻起来像是重新发明轮子。
可能还有其他方法可以解决此问题。期待您的建议。
注意:我想立即拦截任何将问题转换为我很清楚的指针语义的建议。我需要完全针对上述方案的解决方案。