手动内存管理:new 和 delete 的基本用法
写 C++ 程序时,最常碰到的问题就是内存泄漏。比如你写了个小型学生管理系统,每次添加学生都用 new 开辟一块内存,但删掉学生后忘了 delete,运行时间一长程序就卡死。这种情况太常见了。
最基本的内存操作就是 new 和 delete。new 用来在堆上分配内存,delete 负责释放。比如:
int* p = new int(10);
delete p;
p = nullptr; // 避免野指针很多人只记得 new,却总把 delete 忘在脑后。更麻烦的是,在函数中途 return 或抛出异常时,delete 很容易被跳过,导致内存没释放。
智能指针:让内存自动回收
C++11 引入的智能指针算是救星。最常见的有 std::unique_ptr 和 std::shared_ptr。它们能自动管理生命周期,只要超出作用域,内存就会被清理。
比如处理一个临时配置对象:
#include <memory>
std::unique_ptr<Config> cfg = std::make_unique<Config>();
cfg->load();
// 函数结束时自动释放,不用手动 deleteunique_ptr 是独占式管理,不能复制,适合一对一场景。而 shared_ptr 用引用计数,多个指针可以共享同一块内存,适合复杂对象共享的情况。
避免循环引用的小技巧
shared_ptr 虽好,但也有坑。两个对象互相持有对方的 shared_ptr,就会形成循环引用,内存永远无法释放。这时候可以用 std::weak_ptr 打破循环。
比如父子节点结构中,父节点用 shared_ptr 指向子节点,子节点用 weak_ptr 指回父节点,这样就不会卡住内存。
RAII 原则:资源即对象
RAII(Resource Acquisition Is Initialization)是 C++ 内存管理的核心思想。意思是资源的获取即初始化,对象构造时申请资源,析构时自动释放。
文件操作就是一个典型例子。你打开一个文件句柄,如果中途出错没关闭,系统资源就被占着。用 RAII 思路,可以把文件封装成类,析构函数里自动 close,不管哪条路径退出都能保证资源回收。
容器比原生数组更安全
很多人习惯用 int* arr = new int[100]; 这种方式创建数组,但容易忘记用 delete[],或者误用 delete 单个释放,导致未定义行为。
换成 std::vector 就省心得多:
std::vector<int> data(100);
data.push_back(42);
// 出作用域自动释放,还能动态扩容vector 内部自己管理内存,你只需要关心逻辑,不用操心分配和释放细节。
定位内存泄漏的小工具
开发过程中,可以用一些工具辅助排查。Windows 上用 Visual Studio 自带的调试器能查内存分配堆栈;Linux 下 valgrind 是神器,跑一遍程序就能告诉你哪里漏了 delete。
也可以在代码里加简单的计数:
static int alloc_count = 0;
void* operator new(size_t size) {
++alloc_count;
return malloc(size);
}
void operator delete(void* ptr) noexcept {
--alloc_count;
free(ptr);
}程序结束前检查 alloc_count 是否归零,能快速发现明显泄漏。