E09 RAII + E10 手动内存管理:从自动释放到手动配对
日期:2026-08-27
学习章节:01 C++ 语言基础
摘要:今天完成 E09 RAII 文件读写与 E10 手动 new/delete 练习,理解了资源自动释放与手动配对的区别,并通过 FileGuard 的异常测试代码验证了栈展开时析构自动执行。
今日进度
- E09-1 到 E09-4:FileGuard、异常安全、禁止拷贝
- E10-1:new/delete 标量
- E10-2:故意不 delete,观察内存泄漏
- E10-3:new[]/delete[] 配对
- E10-4:改用智能指针
一、RAII 与 FileGuard
RAII 的意思是“资源获取即初始化”:构造函数获取资源,析构函数释放资源。无论正常退出还是异常,析构都会执行,资源不会漏释放。
FileGuard 测试代码:
classFileGuard{public:explicitFileGuard(conststd::string&path):File(path){}~FileGuard(){if(File.is_open()){File.close();}}std::ofstream&Stream(){returnFile;}private:std::ofstream File;};异常安全测试:
try{FileGuardfile_guard("test.txt");file_guard.Stream()<<"written via FileGuard"<<std::endl;throwstd::runtime_error("simulate failure while holding file");}catch(conststd::exception&e){std::cerr<<"caught: "<<e.what()<<std::endl;}输出顺序:
FileGuard closed automatically caught: simulate failure while holding file关键点:throw后先发生栈展开,局部对象file_guard在进入catch之前析构,所以“自动关闭”出现在“caught”前面。
二、堆与栈回顾
栈是局部变量的区域,进入函数分配、离开函数自动释放;堆是动态分配区域,生命周期由代码控制。
intvalue=42;// value 在栈上int*p=newint(42);// p 在栈上,p 指向的 int 在堆上三、new/delete 配对规则
new->deletenew[]->delete[]delete arr;对new[]数组是未定义行为。对int数组可能碰巧不崩,因为元素没有析构、底层分配器相似,但换成有析构的类型就会泄漏或崩溃。
int*arr=newint[5];delete[]arr;// 必须 delete[]四、智能指针与 make_unique
std::unique_ptr<int>p=std::make_unique<int>(42);推荐用make_unique,而不是把裸new包进智能指针:
UseResource(std::unique_ptr<A>(newA),GetValue());参数求值顺序不固定,如果GetValue()先抛异常,new A的内存就泄漏了。make_unique内部一次完成分配和包装,没有裸指针悬空窗口。
五、代码片段
intscalar=42;int*pScalar=newint(scalar);deletepScalar;int*arr=newint[5];delete[]arr;std::unique_ptr<int>smart=std::make_unique<int>(42);六、踩坑与收获
delete和delete[]不能混用。- “碰巧没崩”不等于“正确”,未定义行为不能依赖。
- 手动内存管理的核心是“谁分配谁释放”,RAII 和智能指针让这条规则自动成立。
明日计划
- E11 unique_ptr:唯一所有权、移动、容器、工厂函数