我有下面的TreeNode
类,它可以存储对其父节点的引用以及指向其所有子节点的指针的向量。
TreeNode
class TreeNode : public std::enable_shared_from_this<TreeNode>
{
private:
int val_;
std::weak_ptr<TreeNode> parent_;
std::vector<std::shared_ptr<TreeNode>> children_;
public:
TreeNode(int val) : val_(val) {}
void addChild(std::shared_ptr<TreeNode> child)
{
child->parent_ = shared_from_this();
children_.push_back(std::move(child));
}
const int& getVal() const
{
return val_;
}
std::weak_ptr<TreeNode> getParent()
{
return parent_;
}
};
parent_
作为weak_ptr
存储,因此父级及其子级之间没有循环依赖项/内存泄漏。
主要
int main(int argc, char** argv) {
std::shared_ptr<TreeNode> parent = std::make_shared<TreeNode>(1);
std::shared_ptr<TreeNode> child = std::make_shared<TreeNode>(2);
parent->addChild(child);
// child->getParent()->getVal(); not possible
return 0;
}
使用此类时,我想访问任何节点的父节点的值(否则,为什么要存储父节点),但是由于parent_
是weak_ptr
,因此我无法调用任何成员函数都可以使用。
我知道周围有一些“骇客”,例如使用lock
从shared_ptr
中提取weak_ptr
,但是我觉得这使API变得笨拙。我想知道建议使用C ++ 11返回weak_ptr
的方式如何仍然可以使用。
编辑:
从API的角度来看,我本不应该将lock
称为“ hack”,而不是优雅。
例如,如果我想访问parent_
,则必须做类似的事情
主要
if (!child->getParent()->expired())
{
std::cout << child->getParent()->lock()->getVal() << std::endl;
}
这似乎很冗长,所以我想知道是否可以利用某些语言功能来避免做所有这些事情。
答案 0 :(得分:1)
无法在lock
上调用weak_ptr
,weak_ptr
的全部意义在于,它不会计入指向它所指向的对象的引用计数仍然允许使用控制块检查对象是否有效,然后获取shared_ptr
。
对我来说,parent_
不应是weak_ptr
,而应是原始指针。使用原始指针不是一个坏习惯,也不是“不现代”,它们不应该拥有任何内存。这样,您就可以检查它是否为nullptr
,以检查节点是否具有父级。
您在lock
上使用weak_ptr
的示例也是错误的。 shared_ptr
和weak_ptr
是由许多线程同时创建和销毁的,因此它们是大多数用例所在的地方。
if (!child->getParent()->expired())
{
std::cout << child->getParent()->lock()->getVal() << std::endl;
}
在您的示例中,您首先使用expired
成员函数检查指针是否仍然有效,如果是,则使用lock
获得一个shared_ptr
,它将有助于对象引用计数。如果有多个线程在使用该指针,则在您的expired
之后和lock
调用之前,您的引用计数可能为0,并且可以从内存中删除对象。然后,您将从nullptr shared_ptr
中获得一个lock
,并在取消引用时获得未定义的行为。
如果您继续使用weak_ptr
,请这样做。
if(auto parent_ptr = child->getParent()->lock())
{
// parent_ptr is shared_ptr that is not null here
}