完全可行且是现代C++默认实践:用RAII、值语义和智能指针替代new/delete,禁用堆分配需成对删除所有operator new/delete重载版本。

直接说结论:不用 new 和 delete 是完全可行的,而且是现代 C++ 项目的默认实践。关键不是“少用”,而是用 RAII + 值语义 + 智能指针替代手动生命周期管理。
为什么函数里还写 new/delete 就等于埋雷
常见错误现象:new 分配后被异常跳过、delete 写在 return 之后、delete[] 忘了加方括号、多个分支漏掉释放路径——这些都不是“粗心”,而是语言机制决定的不可靠模式。
根本原因在于:C++ 的 new/delete 不是资源获取/释放的原子操作,它们不绑定对象生命周期,也不参与异常安全保证。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 把所有临时缓冲区(如网络包、序列化数据)换成
std::vector<char></char>或std::string,用.data()和.size()对接 C 接口 - 避免在函数内部动态分配单个对象,改用栈对象或传入已构造好的引用
- 如果必须延迟构造,用
std::optional<t></t>替代T*,它天然支持就地构造和析构
类成员里怎么绕开 new/delete
使用场景:类需要持有某资源(比如一个配置对象、一个连接句柄),但又不想在构造时就强制创建。
参数差异:std::unique_ptr<t></t> 默认空、可移动、不可复制;std::shared_ptr<t></t> 支持共享所有权,但有引用计数开销;std::weak_ptr<t></t> 配合 shared_ptr 解环引用。
性能影响:除非高频创建/销毁,否则 unique_ptr 几乎零开销;shared_ptr 构造/拷贝需原子操作,慎用于 hot path。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 成员变量优先声明为
std::unique_ptr<foo></foo>,构造函数中用std::make_unique<foo>(...)</foo>初始化 - 不要写
ptr.reset(new Foo(...))—— 这会绕过make_unique的异常安全保证 - 若该成员只是可选依赖,且生命周期明确短于宿主类,考虑直接用值语义:
Foo m_foo{...},配合移动构造
如何禁止别人误用 new 创建你的类
错误现象:有人写了 new MyClass,结果你类里有 std::string 成员,导致堆上多一层间接分配,还可能破坏内存池布局。
真正有效的做法是编译期拦截,而不是靠文档或 review 提醒:
- 在类定义内声明:
void* operator new(std::size_t) = delete; - 必须配套声明:
void* operator new[](std::size_t) = delete; - 同时禁用释放:
void operator delete(void*) = delete;和void operator delete[](void*) = delete;
漏掉任一版本,都会导致未定义行为:比如只禁 new 不禁 new[],代码 new MyClass[1] 仍能编译通过,但对象可能没调用构造函数。
全局 new/delete 替换不是银弹
使用场景:想统一监控内存分配、接入自定义内存池、或做 leak 检测。
容易踩的坑:
- 替换
operator new后,所有第三方库、STL 容器(如std::vector内部)也会走你的分配器,必须确保线程安全和异常强保证 - 不能只重载
new忘了new[],否则std::string等内部用数组分配的类型会崩溃 - placement new 不受全局重载影响,它只是“在已有内存上调用构造函数”,别指望它走你的分配逻辑
更轻量的替代方案:对特定类重载 operator new,或者用 std::pmr::polymorphic_allocator 配合内存资源对象,按需切换。
最常被忽略的一点:禁用堆分配后,std::vector<myclass></myclass> 依然合法——因为 vector 自己在堆上分配连续内存来存放 MyClass 实例,这和 MyClass 能否被 new 无关。真正要约束的是“谁拥有这块内存”,而不是“内存物理在哪”。


















