最可靠方式是将operator new和operator delete声明为=delete——编译期彻底禁止所有堆分配形式,包括new、new[]、placement new及nothrow版本,且必须成对禁用new与new[]、delete与delete[],否则引发未定义行为或链接错误。

直接禁止类在堆上分配内存,最可靠的方式是 operator new 和 operator delete 声明为 = delete —— 这比私有化或空实现更彻底,编译期就报错,不给运行时绕过的机会。
为什么用 = delete 而不是私有化 operator new
私有化 operator new 看似能阻止 new MyClass,但存在漏洞:友元函数、成员函数内部仍可调用;而且 new[]、placement new 不受影响。而 = delete 是语言级禁止,所有形式的堆分配(包括 new、new[]、自定义 nothrow 版本)都会被拦截。
-
void* operator new(https://www.php.cn/link/be04b1ed09ed7d60b621315071c300cd) = delete;拦截常规堆分配 -
void* operator new[](https://www.php.cn/link/be04b1ed09ed7d60b621315071c300cd) = delete;拦截数组分配 -
void operator delete(void*) = delete;和void operator delete[](void*) = delete;配套禁用释放,避免链接错误
常见错误:只禁 new 忘了 new[]
如果只删了 operator new,代码 new MyClass[5] 仍能通过编译——因为 new[] 调用的是独立的重载函数。一旦运行,会触发未定义行为(如调用全局 operator new[]),结果不可控。必须成对禁用。
- 漏掉
operator new[]→ 编译不报错,但对象构造可能异常(如析构函数未被调用) - 漏掉
operator delete[]→delete[]会链接失败,或退回到全局版本,导致内存泄漏或崩溃
注意栈分配不受影响,但需警惕隐式堆分配场景
禁用堆分配后,MyClass obj;、std::vector<myclass></myclass>(元素本身在栈/堆?)这类写法看似安全,但要注意:
-
std::vector<myclass></myclass>的元素存储在堆上(vector内部动态分配),这和MyClass自身能否堆分配无关,不影响 - 但如果
MyClass成员含std::string、std::shared_ptr等,它们内部仍会堆分配——你禁的是类实例本身的内存,不是其成员的 - 继承体系中,基类禁用了
operator new,派生类默认也会继承该禁用;但若派生类显式声明了自己的operator new,则会覆盖基类限制
完整示例:安全禁用堆分配
class StackOnly {
public:
StackOnly() = default;
~StackOnly() = default;
<p>private:
void<em> operator new(https://www.php.cn/link/be04b1ed09ed7d60b621315071c300cd) = delete;
void</em> operator new<a href="https://www.php.cn/link/be04b1ed09ed7d60b621315071c300cd"></a> = delete;
void operator delete(void<em>) = delete;
void operator delete[](void</em>) = delete;
};</p><p>// 正确:编译通过
StackOnly s;</p><p>// 错误:全部编译失败
// StackOnly<em> p = new StackOnly;
// StackOnly</em> arr = new StackOnly[3];
// delete p;
// delete[] arr;
</p>
真正容易被忽略的是 new[] 和 delete[] 的配套禁用,以及派生类可能无意中绕过限制——只要它自己实现了 operator new,哪怕只是转发,也等于解除了约束。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











