std::shared_ptr 转换不会导致内存泄漏,只要使用 std::static_pointer_cast 复用原控制块(含删除器、引用计数等);错误做法如 std::shared_ptr(ptr.get()) 或 static_cast 会丢弃控制块,引发未定义行为。

std::shared_ptr 转 std::shared_ptr 不会内存泄漏
只要原始 std::shared_ptr<t></t> 的析构函数能正常调用(即 T 的析构是平凡或已定义的),转成 std::shared_ptr<void></void> 后,对象仍会被正确销毁。关键不在类型擦除,而在控制块是否保存了正确的删除器。
转换时必须显式指定删除器,否则可能崩溃或未定义行为
默认构造的 std::shared_ptr<void></void> 没有绑定删除逻辑;直接用 static_cast 或隐式转换会丢失原始删除器,导致 delete 作用于 void* —— 这是未定义行为,常见表现是析构函数不执行、内存未释放、或程序崩溃。
- ✅ 正确做法:用
std::shared_ptr<void>(ptr, ptr.get_deleter())</void>或std::static_pointer_cast<void>(ptr)</void> - ⚠️ 错误写法:
std::shared_ptr<void>(ptr.get())</void>—— 丢弃控制块,只剩裸指针 - ⚠️ 错误写法:
static_cast<:shared_ptr>>(ptr)</:shared_ptr>—— C++ 不允许这样 castshared_ptr
std::static_pointer_cast 是最安全的转换方式
它复用原 shared_ptr 的控制块,包括自定义删除器、引用计数、分配器等全部状态,只是把存储的指针类型从 T* 改为 void*。适用于需要泛型持有、跨模块传递、或与 C 接口桥接的场景。
- 支持自定义删除器(比如用
free分配的内存) - 保持线程安全的引用计数操作
- 不增加额外开销,底层控制块零拷贝
- 不能反向转回
std::shared_ptr<t></t>(类型信息已擦除),除非你存了原始类型指针或用dynamic_pointer_cast(仅限多态)
析构时机只取决于引用计数,和 void 转换无关
std::shared_ptr<void></void> 和 std::shared_ptr<t></t> 共享同一控制块,只要还有任一实例存活,对象就不会析构。哪怕所有变量都是 void 类型,只要引用计数没归零,析构器就还在那儿等着。
立即学习“C++免费学习笔记(深入)”;
- 常见误判点:看到
void*就以为“类型丢了,没法析构”——实际析构逻辑全在控制块里 - 注意:如果原始
shared_ptr是用默认删除器 +new T构造的,那static_pointer_cast<void></void>后仍会调用T::~T(),不是operator delete(void*) - 真正危险的是绕过控制块自己 new + 手动管理,或误用
reset(new T)配合void指针
.get() 后另起一个 shared_ptr<void></void>)都会切断析构链。别碰裸指针,别手写删除器逻辑,别信“void 就是不管了”。










