条款 14:在资源管理类中小心 Copying 行为
核心思想
- RAII(Resource Acquisition Is Initialization)对象的复制行为决定了其管理资源的复制行为。
- 在设计 RAII 类时,需仔细考虑是否需要支持复制操作以及如何实现复制。
RAII 对象的复制策略
-
抑制 Copying
- 禁止 RAII 对象被复制,从而避免资源的重复释放。
- 通常通过将复制构造函数和复制赋值操作符声明为
delete
。
示例:禁止复制
class ResourceGuard { public: ResourceGuard(const ResourceGuard&) = delete; ResourceGuard& operator=(const ResourceGuard&) = delete; };
-
引用计数
- 多个 RAII 对象共享同一个资源,通过引用计数控制资源的生命周期。
- 当引用计数降为 0 时,释放资源。
- 常见实现:
std::shared_ptr
。
示例:引用计数
class SharedResource { private: std::shared_ptr<Resource> resource; public: SharedResource(Resource* res) : resource(res) {} };
-
深拷贝
- 复制 RAII 对象时创建资源的新副本,两个对象互相独立。
- 通常适用于需要多个独立资源实例的场景。
示例:深拷贝
class DeepCopyResource { private: Resource* resource; public: DeepCopyResource(const DeepCopyResource& other) : resource(new Resource(*other.resource)) {} DeepCopyResource& operator=(const DeepCopyResource& other) { if (this != &other) { delete resource; resource = new Resource(*other.resource); } return *this; } };
-
转移所有权
- 使用移动语义(C++11 引入的
std::move
)将资源的所有权从一个 RAII 对象转移到另一个。 - 通常适用于
std::unique_ptr
等场景。
示例:转移所有权
class UniqueResource { private: std::unique_ptr<Resource> resource; public: UniqueResource(std::unique_ptr<Resource> res) : resource(std::move(res)) {} };
- 使用移动语义(C++11 引入的
设计建议
-
明确 Copying 行为
- 根据资源的特性决定 RAII 对象是否需要支持复制以及如何实现复制。
- 默认禁止复制,除非明确需要。
-
优先使用标准智能指针
- 对于大多数场景,
std::shared_ptr
和std::unique_ptr
是管理资源的首选。
- 对于大多数场景,
-
小心处理动态分配的资源
- 确保复制行为不会引发资源泄漏或未定义行为。
总结
- RAII 对象的复制行为与其管理的资源密切相关。
- 常见策略包括抑制复制、引用计数、深拷贝和转移所有权。
- 设计 RAII 类时应根据实际需求选择合适的复制策略,避免资源管理问题。