pybind11: método de clase de C++ devuelve un puntero que falló en tiempo de ejecución
Intenté un ejemplo simple de pybind11, como este:
“`c++
// cabecera
class Test
{
private:
int* m_a;
public:
Test(int a);
~Test();
int* getA();
};
```c++
// cpp
Test::Test(int a){ m_a = new int(a); }
Test::~Test(){ delete m_a; }
int* Test::getA(){ return m_a; }
“`c++
// enlace
namespace py = pybind11;
PYBIND11_MODULE(bindtestlib, m)
{
py::class_<test>(m, "Test")
.def(py::init<int>())
// como dice la guía oficial, usa la política de referencia si devuelve un puntero desnudo
.def("getA", &Test::getA, py::return_value_policy::reference);
}
<pre><code><br />luego importa este módulo en el script de python
“`python
import bindtestlib as bt
t = bt.Test(2)
a = t.getA()
print(a)
ejecutar este script imprimirá el valor de a
correctamente, pero encontrará un error antes de salir
luego se queda atascado sin salir
He intentado dos formas:
1. eliminar el delete m_a
en el destructor, luego el script funciona bien, pero creo que esta no es una forma correcta, puede causar pérdida de memoria.
2. depurar pybind11, encontré que antes de que ocurra la excepción, se detiene en un lugar diferente.
Me tiene confundido. No puedo entender qué error he cometido.
davy.ai
El error que se encuentra antes de salir del script es una falla de segmentación (SIGSEGV), lo cual indica que el programa está intentando acceder a una memoria a la cual no tiene permiso. Después de examinar el código que proporcionaste, parece que el problema está en la función
getA()
de la claseTest
.La función
getA()
devuelve un puntero sin formato a la variable miembrom_a
, la cual es asignada en el montón usandonew
en el constructor. Cuando el objetoTest
es destruido, el destructor llama adelete
enm_a
para desasignar la memoria. Sin embargo, cuando la funcióngetA()
devuelve el puntero sin formato, no se garantiza que el objetoTest
todavía esté vivo y que la memoriam_a
sea válida.Para solucionar este problema, puedes cambiar la política de retorno
return_value_policy
acopy
en lugar dereference
. Esto creará una copia de la memoria dem_a
antes de devolverla a Python, de modo que el objeto pueda ser eliminado de forma segura sin afectar al objeto Python.Aquí está el código de vinculación actualizado:
“`c++
namespace py = pybind11;
PYBIND11_MODULE(bindtestlib, m)(m, “Test”)())
{
py::class_
.def(py::init
.def(“getA”, &Test::getA, py::return_value_policy::copy);
}
“`
Con este cambio, el script de Python ya no debería encontrar el error de falla de segmentación.