如何向要求void*参数的函数传递智能指针?
如何用智能指针向接受void*的库函数传递回调数据
假设我们有一个库函数register_cb,它的定义如下:
void register_cb(void* data, ...) { }
我们需要向它传递非栈存储的数据,避免栈对象超出作用域失效的问题,同时用智能指针管理内存防止泄漏。先看几种错误的尝试:
错误示例1:传递栈对象
void my_processor_fn() { Foo foo; register_cb(&foo); // 无效:foo会在函数结束时销毁,库后续调用回调会访问悬空指针 }
错误示例2:传递裸堆指针(有内存泄漏风险)
void my_processor_fn() { Foo* foo = new Foo(); register_cb(foo); // 有效,但如果忘记手动调用delete会造成内存泄漏 }
错误示例3:传递栈上智能指针的地址
void my_processor_fn() { std::unique_ptr<Foo> foo(new Foo()); register_cb(&foo); // 无效:unique_ptr出作用域会自动销毁内部对象,同样导致悬空指针 }
正确解决方案
方案1:用std::unique_ptr在回调中接管所有权(单次回调场景)
如果库只会调用一次回调,且调用后不再使用该指针,可以在注册时传递裸堆指针,然后在回调里用std::unique_ptr接管,自动释放内存:
// 假设register_cb支持传入回调函数(根据库实际接口调整) void register_cb(void* data, void (*callback)(void*)); void my_callback(void* data) { // 用unique_ptr接管裸指针,函数结束时自动销毁对象 std::unique_ptr<Foo> foo(static_cast<Foo*>(data)); // 在这里处理foo对象 } void my_processor_fn() { Foo* foo = new Foo(); register_cb(foo, my_callback); }
方案2:用std::shared_ptr延长对象生命周期(多次回调场景)
如果库会多次调用回调,或者无法确定回调的调用时机,需要保证对象在所有回调完成前不被销毁,可以用std::shared_ptr,并将其存储在生命周期足够长的地方(比如全局容器、类成员变量):
// 全局容器,用于保存shared_ptr副本,维持对象引用计数 std::vector<std::shared_ptr<Foo>> callback_data_store; void my_callback(void* data) { Foo* foo = static_cast<Foo*>(data); // 处理foo对象,此时shared_ptr仍在容器中,对象不会被销毁 } void my_processor_fn() { auto foo = std::make_shared<Foo>(); callback_data_store.push_back(foo); // 保存副本,延长对象生命周期 register_cb(foo.get(), my_callback); // 传递原始指针 } // 当不再需要该回调时,从容器中移除对应的shared_ptr void unregister_foo(Foo* target) { auto it = std::find_if(callback_data_store.begin(), callback_data_store.end(), [target](const std::shared_ptr<Foo>& ptr) { return ptr.get() == target; }); if (it != callback_data_store.end()) { callback_data_store.erase(it); // 移除后如果引用计数降为0,对象会自动销毁 } }
方案3:传递std::shared_ptr的堆存储副本(无需全局容器)
如果不想用全局存储,可以把std::shared_ptr本身分配在堆上,传递其地址给register_cb,在回调中释放堆上的shared_ptr并获取对象:
void my_callback(void* data) { auto shared_foo_ptr = static_cast<std::shared_ptr<Foo>*>(data); std::shared_ptr<Foo> foo = *shared_foo_ptr; delete shared_foo_ptr; // 释放堆上的shared_ptr对象 // 处理foo对象,此时foo的引用计数至少为1,可安全使用 } void my_processor_fn() { auto shared_foo = std::make_shared<Foo>(); // 把shared_ptr的副本分配在堆上 auto* ptr_to_shared = new std::shared_ptr<Foo>(shared_foo); register_cb(ptr_to_shared, my_callback); }
关键注意事项
- 绝对不要传递栈上智能指针的地址给
register_cb,否则智能指针出作用域会销毁内部对象,导致悬空指针。 - 必须确保智能指针管理的对象生命周期完全覆盖库使用该指针的周期,否则会出现非法内存访问问题。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

