pybind11绑定含虚函数的C++类出现未定义行为问题求助
解决pybind11绑定带虚函数子类时父类成员访问的垃圾值问题
核心修复方案
将父类vec3的成员绑定方式从def_readwrite改为使用def_property配合lambda getter/setter,避免直接使用成员指针导致的偏移计算错误。
问题根源
当Point类包含虚函数时,其内存布局首地址会被虚表指针(vptr)占据,而vec3作为基类的成员是在vptr之后的内存区域。pybind11的def_readwrite使用的是成员指针,该指针的偏移是相对于vec3实例的起始地址计算的,但通过Point实例访问时,pybind11错误地从Point实例的首地址(即vptr位置)开始应用这个偏移,导致读取到虚表指针的垃圾值。而lambda getter/setter是通过对象引用访问成员,编译器会自动计算Point实例中vec3子对象的正确偏移,从而访问到正确的成员值。
修正后的完整代码
C++绑定代码
#include <pybind11/pybind11.h> namespace py = pybind11; struct vec3 { double t, x, y; vec3(double t, double x, double y) : t(t), x(x), y(y) {} }; class Point : public vec3 { public: Point(double t, double x, double y) : vec3(t, x, y) {} virtual void test() {} }; // 修正trampoline类:非纯虚函数使用PYBIND11_OVERRIDE class PyPoint : public Point { public: using Point::Point; void test() override { PYBIND11_OVERRIDE(void, Point, test, ); } }; PYBIND11_MODULE(relatpy, m) { py::class_<vec3>(m, "vec3") .def(py::init<double, double, double>()) // 用lambda getter/setter替代直接成员指针 .def_property("t", [](const vec3& v) { return v.t; }, [](vec3& v, double val) { v.t = val; }) .def_property("x", [](const vec3& v) { return v.x; }, [](vec3& v, double val) { v.x = val; }) .def_property("y", [](const vec3& v) { return v.y; }, [](vec3& v, double val) { v.y = val; }); py::class_<Point, vec3, PyPoint>(m, "Point") .def(py::init<double, double, double>()) .def("test", &Point::test); }
Python测试代码
from relatpy import * p = Point(1, 2, 3) print(p.t) # 输出1 print(p.x) # 输出2 print(p.y) # 输出3
额外说明
之前的trampoline类使用了PYBIND11_OVERRIDE_PURE,但test函数并非纯虚函数,需改为PYBIND11_OVERRIDE(注意末尾的逗号和空参数,对应无参数的虚函数),不过这不是导致垃圾值的核心原因,核心问题还是成员指针的偏移计算错误。
内容的提问来源于stack exchange,提问作者catmousedog
相关产品推荐
相关产品推荐

