pybind11中通过shared_ptr获取Python实现的抽象类实例后,调用纯虚函数未触发Python重写实现的问题
我之前碰到过几乎一模一样的问题,你的代码核心问题出在抽象基类的Python包装类没有正确定义构造函数,导致pybind11没法为Python子类实例创建对应的C++包装对象,最终调用虚函数时无法路由到Python的重写实现。
问题根源分析
你的Script和ScriptFactory都是抽象类,本身没有可直接调用的构造函数。你定义的PyScript和PyScriptFactory用了using Script::Script;和using ScriptFactory::ScriptFactory;来尝试继承构造函数,但抽象类的构造函数是没法被直接继承和调用的——这就导致这两个包装类实际上没有可用的默认构造函数。
当Python返回MyScript实例给C时,pybind11需要创建一个PyScript实例作为底层C对象来关联Python对象,但因为PyScript没有合法的构造函数,这个关联过程失败了。最终C++拿到的shared_ptr<Script>指向的是一个未正确初始化、没有绑定到Python对象的实例,调用init()时自然就触发了纯虚函数的运行时错误。
解决方案
修改C++中的PyScript和PyScriptFactory定义,为它们添加默认构造函数,替换原来无效的构造函数继承语句:
// 修改PyScript定义 struct PyScript : public Script { // 替换 using Script::Script; 为默认构造函数 PyScript() = default; void init() override { PYBIND11_OVERRIDE_PURE( void, Script, init ); } }; // 修改PyScriptFactory定义 struct PyScriptFactory : ScriptFactory { // 替换 using ScriptFactory::ScriptFactory; 为默认构造函数 PyScriptFactory() = default; std::shared_ptr<Script> getScript(const std::string& type) override { PYBIND11_OVERRIDE_PURE( std::shared_ptr<Script>, ScriptFactory, getScript, type ); } };
修改后的效果
添加默认构造函数后,pybind11可以正常创建PyScript和PyScriptFactory的实例,这些实例会正确绑定到Python侧的子类对象(MyScript和MyFactory)。当C++调用script->init()时,会通过PyScript::init()中的PYBIND11_OVERRIDE_PURE宏路由到Python的MyScript.init()方法,就能输出你预期的Hello, world!了。
另外补充一句:你的Python代码里不存在对象提前被垃圾回收的问题——Container持有script的shared_ptr,只要my_cont对象存在,Python的MyScript实例就不会被回收,所以核心问题还是在C++包装类的构造函数上。
内容来源于stack exchange




