我的程序中遇到了设计问题。 我必须管理Nodes对象,它是根ChainDescriptor的一部分。
基本上它看起来如下:
class ChainDescriptor
{
public:
~ChainDescriptor()
{
//delete the nodes in nodes...
}
void addNode(Node *);
Node * getNode();
const std::list<Node *>& getNodes() const;
std::list<Node *> m_nodes;
};
class Node
{
public:
Node(Node *parent);
void addChild(Node *node);
Node * getChild(const std::string& nodeName);
private:
Node * m_parent;
std::list<Node*> m_childs;
};
ChainDescriptor类拥有所有节点并负责删除它们。 但是现在需要在另一个程序中使用这些类,一个具有撤消/重做功能的GUI,以及“所有权”的问题。 在深入修改现有代码之前,我正在考虑不同的解决方案:
shared_ptr
和相应的list<shared_ptr<...> >
weak_ptr
和相应的list<weak_ptr<...> >
在上面的例子中,我真的不知道在哪里正确使用shared_ptr
和weak_ptr
。
有什么建议吗?
答案 0 :(得分:3)
shared_ptr
拥有智能指针,weak_ptr
正在引用智能指针。
因此,在您的情况下,我认为ChainDescriptor
应使用shared_ptr
(它拥有节点)而Node
应使用weak_ptr
m_parent
(仅限{}引用它)和shared_ptr
m_childs
(它拥有它们)。
答案 1 :(得分:3)
您可以shared_ptr
使用m_childs
,weak_ptr
使用m_parent
。
但是,保留指向父Node
的原始指针并且根本不使用任何弱指针仍然是合理的。这背后的保护机制是永久存在非null父级的不变量。
另一个选项是仅在shared_ptr
中使用ChainDescriptor
并保留Node
中的所有原始指针。这种方法避免了弱指针,并且具有干净的所有权策略(父节点拥有自己的子节点)。
弱指针将帮助您自动管理内存,但其背后是模糊的所有权逻辑和性能损失。
答案 2 :(得分:1)
通常的实现是每个节点都有对其子节点的强引用(即使它们保持活动状态),并且每个子节点都有一个弱引用返回父节点。
这样做的原因是避免循环引用。如果只使用强引用,那么你会遇到父引用计数永远不会降为零的情况(因为子引用),并且子引用计数永远不会降为零(因为父引用有引用)。
我认为你的ChainDescriptor
类可以在这里使用强引用。
答案 3 :(得分:1)
尝试用某种智能替换原始指针
指针一般不起作用。智能指针有
不同的语义比弱指针,通常,这些
特殊语义需要在更高的位置考虑
水平。这里“最干净”的解决方案是添加对副本的支持
在ChainDescriptor
中,实施深层复制。 (我想
在这里你可以克隆Node
,并且所有Node
都是
始终由ChainDescriptor
拥有。)另外,对于撤消,您可以
反正需要一份深刻的副本;你不想修改
活动实例,用于修改为撤消保存的数据。
话虽如此,你的节点似乎被用来形成一棵树。在
在这种情况下,std::shared_ptr
将起作用,只要1)所有Node
始终由ChainDescriptor
或父母“拥有”
Node
,2)结构确实是森林,或者至少
DAG的集合(当然,您不会进行更改
在任何已保存的实例中)。如果结构是这样的
可能会出现循环,然后您无法使用shared_ptr
水平。您可以抽象出节点列表和
树成一个单独的实现类,并具有
ChainDescriptor
保留shared_ptr
。
(FWIW:我使用了一个引用计数指针来表示节点 我多年前写过的一个解析树,以及不同的实例 可以共享子树。但我是从它设计的 开始使用引用计数指针。而且因为如何 树被建造,我保证可能没有 周期。)