这可以通过boost::shared_ptr
Boost.Python 中的一些设置来完成。
boost::shared_ptr
具有接受自定义删除器的构造函数。当shared_ptr
的引用计数达到零时,shared_ptr
将调用客户删除器,将先前托管的指针作为参数传递。这允许使用不同的释放策略,例如“无操作”或调用wxWindow::Destroy()
.
class window {};
void no_op(window*) {};
boost::shared_ptr(new window(), &no_op);
当向 Python 公开一个类时,Boost.Python 允许通过HeldType
. 在这种情况下,HeldType
将是boost::shared_ptr
。这允许在 C++ 和 Python 之间发生正确的引用计数,并允许自定义解除分配策略。
boost::python::class_<window, boost::shared_ptr<window>, ...>("Window", ...);
让这些透明地协同工作的诀窍是:
- 禁止 Boost.Python 创建默认初始化程序 (
__init__
)。
- 显式提供一个
__init__
函数,该函数将调用一个返回shared_ptr
带有自定义删除器的工厂函数。
这是一个模拟window
类,旨在仅通过destroy()
成员函数销毁。
class window
{
...
private:
~window();
public:
void destroy();
};
定义了一个工厂函数,它将window
使用自定义删除器创建一个引用计数对象。destroy()
当引用计数达到零时 ,自定义删除器将在对象上调用。
boost::shared_ptr<window> create_window()
{
return boost::shared_ptr<window>(
new window(),
boost::mem_fn(&window::destroy));
}
最后,使用 Boost.Python 公开window
类,但抑制默认初始化程序,并透明地将其替换为create_window
工厂函数。
boost::python::class_<window, boost::shared_ptr<window>,
boost::noncopyable>("Window", python::no_init)
.def("__init__", python::make_constructor(&create_window));
这是一个完整的例子:
#include <iostream>
#include <boost/mem_fn.hpp>
#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>
/// @brief Mockup window class.
class window
{
public:
window(unsigned int id)
: id_(id)
{
std::cout << "window::window() " << id_ << std::endl;
}
void action() { std::cout << "window::action() " << id_ << std::endl; }
void destroy()
{
std::cout << "window::destroy() " << id_ << std::endl;
delete this;
}
private:
~window() { std::cout << "window::~window() " << id_ << std::endl; }
private:
unsigned int id_;
};
/// @brief Factory function that will create reference counted window
/// objects, that will call window::destroy() when the reference
/// count reaches zero.
boost::shared_ptr<window> create_window(unsigned int id)
{
return boost::shared_ptr<window>(
new window(id),
boost::mem_fn(&window::destroy));
}
BOOST_PYTHON_MODULE(example) {
namespace python = boost::python;
// Expose window, that will be managed by shared_ptr, and transparently
// constructs the window via a factory function to allow for a custom
// deleter.
python::class_<window, boost::shared_ptr<window>,
boost::noncopyable>("Window", python::no_init)
.def("__init__", python::make_constructor(&create_window))
.def("action", &window::action)
;
}
及其用法:
>>> from example import Window
>>> w1 = Window(1)
window::window() 1
>>> w2 = Window(2)
window::window() 2
>>> w3 = Window(3)
window::window() 3
>>> del w2
window::destroy() 2
window::~window() 2
>>> w3 = None
window::destroy() 3
window::~window() 3
>>> w = w1
>>> del w1
>>> w.action()
window::action() 1
>>> w = None
window::destroy() 1
window::~window() 1
请注意 Python 如何仅在 Python 不再引用该实例时才通知 C++ 删除该对象。因此,在这种情况下,Python 不会尝试与已删除的对象进行交互。希望这可以减轻在 C++ 中删除对象时表达的担忧。
如果在某些情况下 C++ 将删除 Python 中仍然处于活动状态的对象,则考虑使用不透明指针来分隔实现类和句柄类。句柄类可以在转发调用之前检查关联的实现实例是否已被删除,从而允许向 Python 抛出异常。