Skip to content

第7章 智能指针集成

智能指针是现代 C++ 的重要特性,pybind11 提供了完整的智能指针支持。本章将介绍如何绑定使用智能指针的类,以及如何管理对象的生命周期。

unique_ptr.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
// 不可复制的类(只能使用 unique_ptr)
class Resource {
public:
Resource(int id) : m_id(id) {
py::print(py::str("Resource {} created").format(m_id));
}
~Resource() {
py::print(py::str("Resource {} destroyed").format(m_id));
}
int id() const { return m_id; }
void process() { py::print(py::str("Processing resource {}").format(m_id)); }
private:
int m_id;
};
PYBIND11_MODULE(unique_ptr, m) {
// 使用 PYBIND11_DECLARE_HOLDER_TYPE 声明 unique_ptr 持有者类型
PYBIND11_DECLARE_HOLDER_TYPE(Resource, std::unique_ptr<Resource>);
py::class_<Resource, std::unique_ptr<Resource>>(m, "Resource")
.def(py::init<int>(), py::arg("id"), "创建 Resource")
.def("id", &Resource::id)
.def("process", &Resource::process);
// 工厂函数返回 unique_ptr
m.def("create_resource", [](int id) {
return std::unique_ptr<Resource>(new Resource(id));
}, py::arg("id"), "创建并返回 Resource 的 unique_ptr");
}
import unique_ptr as up
res = up.create_resource(42)
print(res.id()) # 42
res.process() # Processing resource 42
del res
// 声明 holder 类型
PYBIND11_DECLARE_HOLDER_TYPE(T, std::unique_ptr<T>);
// 绑定时指定 holder 类型
py::class_<Resource, std::unique_ptr<Resource>>(m, "Resource")
步骤说明
PYBIND11_DECLARE_HOLDER_TYPE通知 pybind11 关于这个 holder 类型
std::unique_ptr<Resource>作为第二个模板参数传递

关键点: unique_ptr 表示唯一所有权,当 Python 侧的引用计数归零时,对象会被自动删除。

shared_ptr.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
class DatabaseConnection {
public:
DatabaseConnection(const std::string& url)
: m_url(url), m_ref_count(0) {
py::print(py::str("DatabaseConnection to {} created").format(m_url));
}
~DatabaseConnection() {
py::print(py::str("DatabaseConnection to {} destroyed").format(m_url));
}
void query(const std::string& sql) {
py::print(py::str("Executing: {}").format(sql));
}
const std::string& url() const { return m_url; }
private:
std::string m_url;
int m_ref_count;
};
PYBIND11_MODULE(shared_ptr, m) {
// 声明 shared_ptr holder 类型
PYBIND11_DECLARE_HOLDER_TYPE(DatabaseConnection, std::shared_ptr<DatabaseConnection>);
py::class_<DatabaseConnection, std::shared_ptr<DatabaseConnection>>(m, "DatabaseConnection")
.def(py::init<const std::string&>(), py::arg("url"))
.def("query", &DatabaseConnection::query, py::arg("sql"))
.def("url", &DatabaseConnection::url);
// 工厂函数返回 shared_ptr
m.def("connect", [](const std::string& url) {
return std::make_shared<DatabaseConnection>(url);
}, py::arg("url"), "创建数据库连接");
}
import shared_ptr as sp
conn1 = sp.connect("postgresql://localhost/mydb")
conn2 = conn1 # 共享引用
print(conn1.url()) # postgresql://localhost/mydb
conn1.query("SELECT * FROM users")
print(conn1 is conn2) # True
del conn1
del conn2
特性unique_ptrshared_ptr
所有权唯一共享
复制不可(可移动)可以(引用计数 +1)
销毁时机唯一所有者销毁时最后一个所有者销毁时
开销低(无引用计数)中等(有引用计数)
线程安全是(移动操作)是(引用计数操作)
weak_ptr.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
class CacheEntry {
public:
CacheEntry(const std::string& key, const std::string& value)
: m_key(key), m_value(value) {}
const std::string& key() const { return m_key; }
const std::string& value() const { return m_value; }
private:
std::string m_key;
std::string m_value;
};
class Cache {
public:
void put(const std::string& key, std::shared_ptr<CacheEntry> entry) {
m_entries[key] = entry;
}
// 返回 weak_ptr
std::weak_ptr<CacheEntry> get(const std::string& key) {
auto it = m_entries.find(key);
if (it != m_entries.end()) {
return std::weak_ptr<CacheEntry>(it->second);
}
return std::weak_ptr<CacheEntry>();
}
bool is_expired(const std::string& key) {
auto wp = get(key);
return wp.expired(); // 检查是否已过期(对象已销毁)
}
private:
std::unordered_map<std::string, std::shared_ptr<CacheEntry>> m_entries;
};
PYBIND11_MODULE(weak_ptr, m) {
PYBIND11_DECLARE_HOLDER_TYPE(CacheEntry, std::shared_ptr<CacheEntry>);
py::class_<CacheEntry, std::shared_ptr<CacheEntry>>(m, "CacheEntry")
.def(py::init<const std::string&, const std::string&>(),
py::arg("key"), py::arg("value"))
.def("key", &CacheEntry::key)
.def("value", &CacheEntry::value);
py::class_<Cache>(m, "Cache")
.def(py::init<>())
.def("put", &Cache::put, py::arg("key"), py::arg("entry"))
.def("get", &Cache::get, py::arg("key"), "返回 weak_ptr")
.def("is_expired", &Cache::is_expired, py::arg("key"), "检查缓存是否过期");
// weak_ptr 不能直接返回给 Python,需要转换为 shared_ptr 或返回 bool
m.def("try_get_value", [](const std::string& key, Cache& cache) -> py::object {
auto wp = cache.get(key);
if (auto sp = wp.lock()) {
return py::make_tuple(true, sp->value());
}
return py::make_tuple(false, py::none());
}, py::arg("key"), py::arg("cache"), "尝试获取缓存值");
}
import weak_ptr as wp
cache = wp.Cache()
entry = wp.CacheEntry("user:1", "Alice")
cache.put("user:1", entry)
cached = cache.get("user:1")
print(cached) # <weak_ptr reference>
print(cache.is_expired("user:1")) # False
valid, value = wp.try_get_value("user:1", cache)
print(valid) # True
print(value) # Alice
del entry
print(cache.is_expired("user:1")) # True
方法说明
wp.expired()检查对象是否已销毁
wp.lock()尝试升级为 shared_ptr(失败返回空)
Python 端不能直接暴露 weak_ptr,需要封装为可检查的形式
factory_functions.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
class Config {
public:
Config() : m_verbose(false), m_port(8080) {}
void set_verbose(bool v) { m_verbose = v; }
void set_port(int p) { m_port = p; }
bool verbose() const { return m_verbose; }
int port() const { return m_port; }
private:
bool m_verbose;
int m_port;
};
class Server {
public:
Server(std::shared_ptr<Config> config) : m_config(config) {}
void start() {
py::print(py::str("Server starting on port {}").format(m_config->port()));
}
std::shared_ptr<Config> config() const { return m_config; }
private:
std::shared_ptr<Config> m_config;
};
PYBIND11_MODULE(factory_functions, m) {
PYBIND11_DECLARE_HOLDER_TYPE(Config, std::shared_ptr<Config>);
PYBIND11_DECLARE_HOLDER_TYPE(Server, std::shared_ptr<Server>);
py::class_<Config, std::shared_ptr<Config>>(m, "Config")
.def(py::init<>())
.def("set_verbose", &Config::set_verbose, py::arg("verbose"))
.def("set_port", &Config::set_port, py::arg("port"))
.def("verbose", &Config::verbose)
.def("port", &Config::port);
py::class_<Server, std::shared_ptr<Server>>(m, "Server")
.def(py::init<std::shared_ptr<Config>>(), py::arg("config"))
.def("start", &Server::start)
.def("config", &Server::config, "获取配置对象的共享引用");
// 工厂函数:创建带配置的 Server
m.def("create_server", [](int port) {
auto config = std::make_shared<Config>();
config->set_port(port);
return std::make_shared<Server>(config);
}, py::arg("port") = 8080, "创建 Server 实例");
}
import factory_functions as ff
server = ff.create_server(9000)
server.start() # Server starting on port 9000
config = server.config()
print(config.port()) # 9000
config2 = config
print(config is config2) # True
config.set_verbose(True)
print(server.config().verbose()) # True
lifetime_management.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
// 场景:Widget 归 Owner 所有,Owner 销毁时 Widget 也销毁
// 但 Python 代码也可能持有 Widget 引用
class Widget {
public:
Widget(const std::string& name) : m_name(name) {
py::print(py::str("Widget '{}' created").format(m_name));
}
~Widget() {
py::print(py::str("Widget '{}' destroyed").format(m_name));
}
const std::string& name() const { return m_name; }
private:
std::string m_name;
};
class Owner {
public:
Owner(const std::string& name) : m_name(name) {
py::print(py::str("Owner '{}' created").format(m_name));
}
~Owner() {
py::print(py::str("Owner '{}' destroyed").format(m_name));
// m_widget 在这里被销毁
}
void set_widget(std::unique_ptr<Widget> w) {
m_widget = std::move(w);
}
Widget* widget() const { return m_widget.get(); }
private:
std::string m_name;
std::unique_ptr<Widget> m_widget;
};
PYBIND11_MODULE(lifetime_management, m) {
PYBIND11_DECLARE_HOLDER_TYPE(Widget, std::unique_ptr<Widget>);
py::class_<Widget, std::unique_ptr<Widget>>(m, "Widget")
.def(py::init<const std::string&>(), py::arg("name"))
.def("name", &Widget::name)
.def("__repr__", [](const Widget& w) {
return py::str("<Widget '{}'>").format(w.name());
});
py::class_<Owner, std::unique_ptr<Owner>>(m, "Owner")
.def(py::init<const std::string&>(), py::arg("name"))
.def("set_widget", &Owner::set_widget, py::arg("widget"), "设置内部 Widget")
.def("widget", &Owner::widget, py::arg("owner"), "获取 Widget 指针(不转移所有权)",
py::return_value_policy::reference_internal)
.def("__repr__", [](const Owner& o) {
return py::str("<Owner '{}'>").format(o.name());
});
}
import lifetime_management as lm
owner = lm.Owner("manager")
print(owner) # <Owner 'manager'>
widget = lm.Widget("button")
owner.set_widget(widget)
w = owner.widget()
print(w) # <Widget 'button'>
del owner
owner2 = lm.Owner("manager2")
widget2 = lm.Widget("slider")
owner2.set_widget(widget2)
del widget2 # 不影响 Owner
del owner2 # Widget 仍然被销毁
策略pybind11 选项说明
C++ 拥有所有权return_value_policy::automatic默认,引用计数管理
共享所有权shared_ptr<T>Python 和 C++ 共享
C++ 拥有,Python 借用return_value_policy::reference_internal短期借用,不延长生命周期
C++ 拥有,Python 借用return_value_policy::reference不修改引用计数
// 返回引用(不转移所有权)
.def("get_widget", &Owner::widget,
py::return_value_policy::reference_internal)
// 返回智能指针(共享所有权)
.def("get_shared_widget", &Owner::shared_widget,
py::return_value_policy::automatic) // 默认
circular_refs.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
// 循环引用示例:
// Parent 持有 Child 引用
// Child 可能持有 Parent 引用(通过 weak_ptr 避免循环)
class Child;
class Parent {
public:
Parent(const std::string& name) : m_name(name) {}
void add_child(std::shared_ptr<Child> child) {
m_children.push_back(child);
// child 可能持有 weak_ptr 指向 this
child->set_parent(py::cast(std::weak_ptr<Parent>(shared_from_this())));
}
const std::string& name() const { return m_name; }
size_t num_children() const { return m_children.size(); }
private:
std::string m_name;
std::vector<std::shared_ptr<Child>> m_children;
};
class Child {
public:
Child(const std::string& name) : m_name(name) {}
void set_parent(std::weak_ptr<Parent> parent) {
m_parent = parent;
}
std::string parent_name() const {
if (auto p = m_parent.lock()) {
return p->name();
}
return "(no parent)";
}
const std::string& name() const { return m_name; }
private:
std::string m_name;
std::weak_ptr<Parent> m_parent; // 使用 weak_ptr 避免循环引用
};
PYBIND11_MODULE(circular_refs, m) {
PYBIND11_DECLARE_HOLDER_TYPE(Parent, std::shared_ptr<Parent>);
PYBIND11_DECLARE_HOLDER_TYPE(Child, std::shared_ptr<Child>);
py::class_<Parent, std::shared_ptr<Parent>>(m, "Parent")
.def(py::init<const std::string&>(), py::arg("name"))
.def("add_child", &Parent::add_child, py::arg("child"))
.def("name", &Parent::name)
.def("num_children", &Parent::num_children);
py::class_<Child, std::shared_ptr<Child>>(m, "Child")
.def(py::init<const std::string&>(), py::arg("name"))
.def("name", &Child::name)
.def("parent_name", &Child::parent_name);
}
import circular_refs as cr
parent = cr.Parent("Dad")
child = cr.Child("Son")
print(parent.name()) # Dad
print(child.name()) # Son
parent.add_child(child)
print(child.parent_name()) # Dad
print(parent.num_children()) # 1
del parent
print(child.parent_name()) # (no parent) - weak_ptr 已过期
print(child.name()) # Son - 对象仍然存在
循环引用(A → B → A):
Parent 持有 shared_ptr<Child> → Child 持有 shared_ptr<Parent> → 内存泄漏
解决方案:
1. Child 使用 weak_ptr<Parent>(推荐)
2. 或者手动 break 循环(Python 端调用 clear())
方法优点缺点
weak_ptr<Parent>自动避免循环需要 lock() 检查
手动清理精确控制需要 Python 端配合
避免双向引用简单限制设计

本章介绍了 pybind11 智能指针集成的核心概念:

主题关键点
unique_ptrPYBIND11_DECLARE_HOLDER_TYPE + std::unique_ptr<T>
shared_ptr共享所有权,引用计数管理
weak_ptr避免循环引用,需要 lock() 检查
工厂函数返回智能指针的工厂函数模式
生命周期策略return_value_policy 控制所有权转移
循环引用使用 weak_ptr 打破循环

最佳实践:

  • 默认使用 unique_ptr(开销最低)
  • 需要共享时使用 shared_ptr
  • 避免循环引用用 weak_ptr
  • 理解 return_value_policy 的四种模式

下一章我们将讨论 pybind11 的高级主题,包括异常处理、性能优化和自定义类型转换。