Pybind11中使用keep_alive确保std::string与Properties生命周期的方式是否正确?
问题分析与结论
这个实现不能正确保证std::string& s在Properties对象的生命周期内保持存活,最终会导致prop.p成为悬垂指针,具体原因如下:
- 当Python端传入字符串时,pybind11会创建一个临时的
std::string实例来匹配setter的std::string&参数,这个临时对象的生命周期仅局限于setter函数的调用过程。 prop.p = s.c_str();让p指向了这个临时std::string内部的字符数组,一旦setter执行完毕,临时std::string被销毁,p就变成了无效的悬垂指针。- 代码中使用的
py::keep_alive<1,2>(),作用是让Python端的Properties对象持有Python端原字符串对象的引用,防止Python回收它,但它无法延长临时std::string的生命周期,也无法让prop.p指向Python字符串对象的内部内存——因为s是Python字符串的拷贝,而非直接引用其内部数据。
正确实现思路
如果要安全实现这个属性,有两种常见方案:
- 修改
Properties结构体,存储std::string而非const char*,直接赋值字符串内容,配合keep_alive保证原Python字符串存活:
struct Properties { std::string p; }; .def_property( "some_attribute", [](Properties &prop) { return prop.p; }, [](Properties &prop, const py::str &s) { prop.p = s.cast<std::string>(); }, py::keep_alive<1,2>() );
- 若必须保留
const char*,则直接使用py::str作为setter参数,获取Python字符串的内部C指针,并通过keep_alive让Properties对象持有该py::str的引用,确保内存不被回收:
struct Properties { const char* p; }; .def_property( "some_attribute", [](Properties &prop) { return std::string(prop.p); }, [](Properties &prop, const py::str &s) { prop.p = PyUnicode_AsUTF8(s.ptr()); // 直接获取Python字符串的内部C指针 }, py::keep_alive<1,2>() );
内容的提问来源于stack exchange,提问作者Kobi
相关产品推荐
相关产品推荐

