将 operator new 设为私有或删除可阻止堆分配,需同时禁用普通、数组及 nothrow 版本,并配套处理 operator delete;但 placement new 仍可用,友元和继承可能绕过限制。

把 operator new 设为私有(或删除)是最直接的方式
只要类的 operator new 不可访问,编译器在遇到 new MyClass 时就会报错。C++ 标准允许你重载类作用域内的 operator new,把它设为 private 即可阻断堆分配路径。
注意:仅禁用非成员(全局)operator new 没用,因为类内重载的版本优先级更高;必须在类内部声明并限制访问权限。
- 推荐写法是同时禁用普通、数组和带
nothrow版本:operator new、operator new[]、operator new(std::nothrow_t const&)等都声明为private - 如果用的是 C++11 及以后,更简洁的做法是用
= delete(但需确保所有重载都被显式删除,否则可能被隐式调用) - 别忘了也处理
operator delete和operator delete[]—— 否则即使构造失败,析构逻辑也可能因不匹配的释放函数出问题
栈上对象仍可正常创建,但要注意拷贝/移动语义
禁用堆分配不影响栈变量、静态变量或作为其他类成员存在,比如 MyClass obj; 或 std::vector<myclass></myclass> 都能照常工作。
不过,如果类内部还禁用了拷贝/移动构造函数,那像 std::vector 这类容器就可能编译失败(因需要移动元素),这不是堆分配限制导致的,而是语义冲突。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 若类设计初衷就是“不可移动”,建议明确删除
MyClass(MyClass&&)和赋值操作符 - 若只是禁止堆分配,但希望支持容器存储,请保留移动语义,并确保移动后对象处于有效但未定义状态(如置空内部指针)
- 注意:某些 STL 实现(如 libstdc++)在扩容时可能先 new 再 placement-new,若只删了
operator new而没删operator new[],仍可能触发错误
友元函数或继承体系下容易绕过限制
私有 operator new 对友元函数无效 —— 如果你写了 friend void make_on_heap(); 并在其中调用 new MyClass,它照样能通过编译。
同样,子类可以重新声明公有的 operator new,从而恢复堆分配能力。所以这种机制本质是“约定型防护”,不是绝对安全的内存围栏。
- 若需更强约束,可配合
final关键字防止继承,再配合私有operator new - 友元带来的绕过风险需结合代码审查控制,不能单靠语法限制
- 某些嵌入式或高安全场景会搭配链接期检查(如自定义
operator new抛异常或 abort),但这已超出编译期阻止范畴
注意 placement new 依然可用,它不调用类的 operator new
new (ptr) MyClass 是 placement new,它跳过内存分配阶段,只调用构造函数。即使你删掉了所有 operator new,这段代码仍合法 —— 它依赖的是你提供的内存地址,而非类自己的分配逻辑。
这意味着:禁止堆分配 ≠ 禁止任意内存上的对象构造。如果你真正想防的是“不在常规堆区构造”,那 placement new 就是个常见漏网之鱼。
- 无法用语言机制完全封禁 placement new,它是 C++ 的底层设施
- 若业务逻辑中真要杜绝,得靠编码规范 + 静态分析工具(如 clang-tidy 检查
new (模式) - 有些团队会在基类里把 placement new 的参数类型故意设为私有(如
void* operator new(size_t, PrivateTag)),让误用立刻报错
实际中最容易忽略的是数组形式和 nothrow 版本的遗漏,以及对 placement new 的误判 —— 它看起来像堆分配,实则完全绕过你的拦截逻辑。

















