gpt4 book ai didi

c++ - pybind11 保持对象存活

转载 作者:行者123 更新时间:2023-12-02 09:56:09 25 4
gpt4 key购买 nike

我正在研究 pybind11 中的一个测试文件,并遇到了 keep_alive 的不同用法。 .

py::keep_alive<1, 2>
py::keep_alive<1, 0>
py::keep_alive<0, 1>

有人可以阐明这个测试文件中的这些用法吗?我知道索引0指返回,1this指针。我只能理解py::keep_alive<1, 2> (使用文档)但不包括其在此测试文件中的用法。

class Child {
public:
Child() { py::print("Allocating child."); }
Child(const Child &) = default;
Child(Child &&) = default;
~Child() { py::print("Releasing child."); }
};
py::class_<Child>(m, "Child")
.def(py::init<>());

class Parent {
public:
Parent() { py::print("Allocating parent."); }
~Parent() { py::print("Releasing parent."); }
void addChild(Child *) { }
Child *returnChild() { return new Child(); }
Child *returnNullChild() { return nullptr; }
};
py::class_<Parent>(m, "Parent")
.def(py::init<>())
.def(py::init([](Child *) { return new Parent(); }), py::keep_alive<1, 2>())
.def("addChild", &Parent::addChild)
.def("addChildKeepAlive", &Parent::addChild, py::keep_alive<1, 2>())
.def("returnChild", &Parent::returnChild)
.def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>())
.def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>())
.def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>());

最佳答案

在实际代码中,addChild功能将通过 Parent 来实现对象存储到指针,而不获取所有权(即以后不会在 C++ 端删除它)。什么py::keep_alive<1, 2>确实,是从 Parent 中引用的对象到 Child对象传递到 addChild ,从而限制 Child 的生命周期到 Parent 的那个.

所以,如果写:

p = Parent()
p.addChild(Child())

然后没有 keep_alive ,那个临时的Child对象将在下一行超出范围(引用计数减至零)。相反,使用 keep_alive<1, 2> ,发生以下情况(伪代码):

p = Parent()
c = Child()
p.__keep_alive = c
p.addChild(c)
del c

现在当 p超出范围,其数据被清理,包括。 __keep_alive引用,此时 c也被清理干净。意思是,p和“临时” child c同时超出范围而不是更早。

编辑:对于 keep_alive<0, 1> ,隐式的生命周期 this与返回值相关。在测试中,它仅用于验证该策略是否可以与 None 返回一起使用,但在访问临时的内部数据项时很常见,通常会处理长语句上的中间临时数据,如下所示:

c = getCopyOfData().at(0).getField('f')

问题在于,在 C++ 中,临时变量的生命周期直到语句结束为止,因此上述情况在音译代码中很常见。但在 Python 中,它以引用计数变为 0 结束。也就是说,getCopyOfData() 的结果。调用at(0)后就会消失完成,离开getField()指向已删除的内存。相反,使用 keep_alive<0, 1> ,这将是(伪代码):

d = getCopyOfData()
at0 = d.at(0)
at0.__keep_alive = d
del d
c = at0.getField('f')
c.__keep_alive = at0
del at0

现在复制的数据容器 d在对访问字段的引用超出范围之前,不会超出范围。

对于keep_alive<1, 0> ,返回值的生命周期与隐式 this 相关。 。如果所有权传递给调用者,而隐式 this 则这很有用。保留一个指针,实际上将内存管理从 C++ 推迟到 Python。请记住,在 pybind11 中,对象标识被保留,因此任何对 returnChildKeepAlive 的调用返回相同的指针将产生相同的 Python 对象,而不是新的对象。所以在这种情况下(伪代码):

c = p.returnChildKeepAlive()    # with c now owning the C++ object
p.__keep_alive = c

如果引用c首先超出范围,p仍然会保持它的事件状态,以免被悬空指针卡住。如果p首先超出范围,c不会因为它接管所有权而受到影响(即C++端不会被删除)。如果 returnChildKeepAlive()第二次调用时,它将返回对未完成的 c 的引用,不是一个新的代理,因此不会影响整体生命周期管理。

关于c++ - pybind11 保持对象存活,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59993357/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com