std::make_shared 返回 std::shared_ptr,非裸指针;它在堆上一次性分配对象与控制块,更高效安全;不支持自定义删除器、allocator 或重载的 operator new。

std::make_shared 本质是创建 shared_ptr,不是裸指针
很多人搜“make_shared 创建指针”,其实是想拿一个能自动管理内存的智能指针,但误以为它返回的是 int* 这类裸指针。实际上 std::make_shared 返回的是 std::shared_ptr<t></t> —— 它内部持有裸指针,但你**不该、也不能直接拿到那个裸指针来手动管理**。
正确理解:它帮你一次性在堆上分配对象内存 + 控制块(control block),比先 new T 再构造 shared_ptr 更高效、更安全。
基本用法:传入构造参数,不 new,不裸指针
直接把要构造的对象的参数传给 make_shared,它会转发给 T 的构造函数:
auto ptr = std::make_shared<:string>("hello");
auto vec = std::make_shared<:vector>>(5, 42); // 构造含5个42的vector
auto p = std::make_shared<myclass>(1, "test", true); // 调用 MyClass(int, const char*, bool)
</myclass></:vector></:string>
常见错误:
- 写成
std::make_shared<myclass>(new MyClass(...))</myclass>→ 编译失败,make_shared不接受裸指针 - 试图用
std::make_shared<int>(5)</int>得到指向 int 的指针,然后去取地址或转型 → 完全偏离设计意图
什么时候不能用 make_shared?绕不开裸指针或自定义删除器时
make_shared 要求对象必须支持默认可访问的构造函数(且不能是 explicit 构造函数),并且无法指定自定义删除器(deleter)或分配器(allocator)。以下情况必须用 shared_ptr 的普通构造函数:
- 类只有 private/protected 构造函数,且没给
make_shared友元权限 - 需要绑定自定义释放逻辑,比如用
fclose关闭 FILE*:std::shared_ptr<file>(fopen(...), [](FILE* f){ fclose(f); })</file> - 对象必须用特定 allocator 分配(
make_shared固定使用std::allocator)
这时你得显式 new 或调用其他工厂函数,再包进 shared_ptr,但代价是两次内存分配(对象 + 控制块)。
别误用 get() 拿裸指针去做生命周期管理
ptr.get() 确实返回 T*,但它只是临时借用,**不转移所有权,也不延长生命周期**。常见陷阱:
- 把
ptr.get()存进容器或传给长期存活的模块,指望它“一直有效” → 错。一旦原始shared_ptr被销毁,裸指针立刻悬空 - 对
ptr.get()调用delete→ 重复释放,UB - 用
get()结果初始化另一个shared_ptr(如shared_ptr<t>(ptr.get())</t>)→ 两个shared_ptr管理同一块内存,析构时 double-free
真正需要裸指针的场景极少,比如调 C API;用完立刻丢掉,绝不保存、绝不重包。
最常被忽略的一点:make_shared 分配的内存布局是对象和控制块连续的,这带来性能优势,但也意味着——如果你的类重载了 operator new,make_shared **不会调用它**(它调用的是 std::allocator<t>::allocate</t>)。这点在调试内存行为或 hook 分配时容易踩坑。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











