Effective C++ 条款14:在资源管理类中小心copying行为
RAII(Resource Acquisition is Initialization)意思是资源取得的时间是初始化时机。怎么理解呢,比如说我们new出了一块动态内存,就应该立刻放入智能指针中;申请了一个数据库链接,就应该立刻放入数据库管理对象中。因为我们需要这些资源管理对象帮助我们自动释放这些资源(通过析构函数)。
所以说,资源获取了以后就要立马初始化一个资源管理对象来管理这些资源。这个就叫做RAII
下面我们谈谈资源管理类的复制问题,一般有下面4中处理方式,一个个看。
禁止复制
假设我们现在有一个互斥器Mutex,用来保证多线程安全。对互斥器加锁,则互斥器所关联的临界资源就无法被其他线程访问。当使用结束以后需要解锁,释放互斥器。
这里我们设计了一个Lock类用来管理互斥器这个资源。在它的构造函数中对互斥器加锁,析构函数中解锁。
设想一下我们要复制Lock对象,会发生什么?一个Lock对象管理Mutex,现在我们试图复制Lock,就表明我们又要用另一个Lock对象管理同一个Mutex,这显然不合理。一个互斥器仅需要也仅能够被一个Lock对象管理,因为不可以对同一个Mutex加两次锁。所以这里我们需要拒绝复制行为。
对底层资源使用引用计数
现在让我们回到智能指针上面来。假设我们new了一块动态内存,并用一个智能指针来保存,在智能指针的析构函数中释放这块内存。现在我们需要另一个智能指针也指向这块内存,这是非常常见的事情。一个对象当然可以被多个指针引用,所以这里我们无法拒绝禁止复制这一操作。
但如果不禁止的话,一个智能指针生命期结束了就会调用析构函数释放内存。而另一个指针指向的就是一块被释放的内存。这也是非常严重的错误。
我们可以引入引用计数法。也就是直到最后一个智能指针被销毁,我们才释放这块内存资源。
复制底层资源
这就属于深拷贝了,我们不仅复制了一个资源管理对象,底层的资源也复制了一份。这两个资源管理对象和底层资源就是互不干扰的,各自负责管理各自的资源。
转移底部资源的拥有权
相当于移交管理权。之前的那个资源管理对象就不负责管理这块资源了。unique_ptr就属于这种,一个unique_ptr对象在被赋值后,原始的对象会将指针置为空。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通