卡码笔记-最强八股文
首页
计算机基础
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
简历专栏
代码随想录 (opens new window)
首页
计算机基础
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
简历专栏
代码随想录 (opens new window)
  • 本栏必读

    • 关于本专栏
    • C++学习路线
    • C++面试题系优化
  • 基础与语法

  • 面向对象

  • STL 与容器

  • 内存管理

    • C++内存分区,堆和栈的区别
    • new和melloc的区别是什么?
    • free和delete区别的是什么?
    • placement new的作用
      • 简要回答
      • 详细回答
      • 知识拓展
    • 什么是内存泄漏?什么是野指针?什么是内存越界?如何避免?
    • 内存碎片与内存溢出
    • 如何避免内存碎片
  • C++11 与现代 C++

  • 智能指针

  • 并发与 I/O

# Placement new 的作用

面试官问"placement new 是什么,有什么用",很多人能说出"在指定地址上构造对象",但追问"为什么不能用 delete 释放""内存对齐怎么保证""标准库哪些组件用了 placement new"就答不上来了。这道题的核心是理解:placement new 把"内存分配"和"对象构造"这两步拆开了,让你可以分别控制。

# 简要回答

普通 new 做两件事:分配内存 + 调构造函数。Placement new 只做第二件事——在你给定的内存地址上调用构造函数,不分配任何内存。语法是 new (ptr) Type(args),其中 ptr 是一块已经分配好的内存。对应地,销毁时不能用 delete(它会试图释放这块内存,但这块内存不是 new 分配的),必须手动调析构函数 obj->~Type(),然后用原始方式释放内存。

# 详细回答

和普通 new 的关系

普通 new MyClass(10) 编译器拆成两步:先调 operator new(sizeof(MyClass)) 分配内存,再在这块内存上调构造函数。Placement new 跳过第一步,直接在你提供的地址上调构造函数。底层实现很简单——标准库有一个特殊的 operator new 重载:void* operator new(size_t, void* ptr) noexcept { return ptr; },它什么都不做,直接返回你传入的指针。

为什么不能用 delete 释放

delete obj 做两件事:调析构函数 + 调 operator delete 释放内存。但 placement new 构造的对象,其内存可能来自栈上的 char 数组、内存池、共享内存段——这些都不是堆分配器管理的。如果对这种内存调 operator delete,行为是未定义的(通常直接崩溃)。正确做法:先手动调析构 obj->~MyClass(),再用原始方式释放内存(栈上的自动回收,内存池的还给池,new char[] 分配的用 delete[] 释放)。

内存对齐要求

placement new 要求你提供的内存地址满足目标类型的对齐要求(alignof(Type))。如果用 char 数组做缓冲区,必须加 alignas(Type) 保证对齐,否则在某些架构上会触发硬件异常或性能严重下降。C++17 的 std::aligned_storage 或直接用 alignas 都能解决。

标准库哪些地方用了 placement new

std::vector 扩容时:分配新的更大内存块,然后用 placement new 在新内存上移动构造旧元素。std::optional:内部有一块 aligned_storage,有值时用 placement new 构造,无值时调析构销毁。std::variant:在同一块内存上构造不同类型的对象。所有内存池/对象池的实现都依赖 placement new。

placement new工作原理

# 知识拓展

Q:为什么不能用 memcpy 代替 placement new?

memcpy 只是逐字节拷贝,不调用构造函数。对于有虚函数的类,虚函数表指针不会被正确设置;对于持有资源的类(string、vector 成员),内部指针会指向错误的地址。只有 POD 类型(没有构造函数、没有虚函数)才能用 memcpy,其他类型必须用 placement new。

Q:placement new 会失败吗?

不会。标准的 placement operator new 标记了 noexcept,它只是返回你传入的指针,不做任何可能失败的操作。但构造函数本身可能抛异常——如果构造函数抛了,对象没有被成功构造,你不需要调析构函数,但内存仍然需要按原始方式释放。

Q:能在同一块内存上反复构造不同对象吗?

能,这正是 placement new 的典型用法。先调析构销毁旧对象,再用 placement new 构造新对象。std::variant 切换类型时就是这么做的。但要注意:新对象的大小和对齐要求不能超过这块内存的容量。

Q:placement new 和内存池是什么关系?

内存池的核心思想是"预分配一大块内存,按需切割使用"。切割出来的小块内存上构造对象就靠 placement new,销毁对象就靠手动调析构。这样避免了频繁调 malloc/free 的开销和内存碎片问题。游戏引擎、数据库、高频交易系统都大量使用这种模式。

Last Updated: 5/23/2026, 4:51:07 PM

← free和delete区别的是什么? 什么是内存泄漏?什么是野指针?什么是内存越界?如何避免? →

评论

验证登录状态...

侧边栏 侧边栏
夜间模式 夜间
卡码简历 卡码简历
代码随想录 代码随想录
卡码投递表 卡码投递表🔥
2026实习校招群 2026群
添加客服微信 2026实习校招客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记 支持卡码笔记
鼓励/支持/赞赏Carl 卡码笔记赞赏码
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪