侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

智能指针的所有权转移与自定义删除器

2025-12-11 / 0 评论 / 4 阅读

题目

智能指针的所有权转移与自定义删除器

信息

  • 类型:问答
  • 难度:⭐⭐

考点

智能指针的所有权语义,自定义删除器的使用,资源管理

快速回答

关键要点:

  • std::unique_ptr 通过移动语义实现独占所有权的转移
  • 自定义删除器用于管理非标准资源(如文件句柄、网络连接)
  • 删除器类型影响智能指针的拷贝语义和大小
  • 推荐使用 std::move 显式转移所有权
  • 避免在删除器中抛出异常(可能导致未定义行为)
## 解析

1. 所有权转移原理

std::unique_ptr 采用独占所有权模型:

  • 通过禁用拷贝构造函数/赋值运算符保证独占性
  • 移动操作(移动构造/移动赋值)转移所有权,源指针置空
  • 所有权转移后,原指针不再持有资源
// 所有权转移示例
std::unique_ptr<int> ptr1(new int(42));
std::unique_ptr<int> ptr2 = std::move(ptr1); // 转移所有权

assert(ptr1 == nullptr);
assert(*ptr2 == 42);

2. 自定义删除器

用于管理非 new 分配的资源:

  • 必须实现函数调用运算符 operator()(T*)
  • 删除器类型影响智能指针类型(需作为模板参数)
  • 无状态删除器(如函数指针)不增加智能指针大小
  • 有状态删除器(如lambda捕获)会使智能指针变大
// 文件句柄管理示例
struct FileDeleter {
    void operator()(FILE* fp) const {
        if (fp) fclose(fp);
        std::cout << "File closed\n";
    }
};

// 使用自定义删除器
std::unique_ptr<FILE, FileDeleter> filePtr(fopen("test.txt", "r"));

// Lambda表达式作为删除器
auto socketDeleter = [](Socket* s) {
    s->shutdown();
    delete s;
};
std::unique_ptr<Socket, decltype(socketDeleter)> sockPtr(new Socket(), socketDeleter);

3. 最佳实践

  • 优先使用 std::make_unique(C++14+)创建实例
  • 显式使用 std::move 转移所有权,提高代码可读性
  • 简单资源使用无捕获lambda,复杂场景使用函数对象
  • 删除器应声明为 noexcept,避免抛出异常

4. 常见错误

  • 错误:尝试拷贝 unique_ptr
    std::unique_ptr<int> a(new int(10));
    std::unique_ptr<int> b = a; // 编译错误!
  • 错误:删除器签名不匹配
    // 错误:删除器需要接受 FILE* 而非 void*
    std::unique_ptr<FILE, void(*)(void*)> p(fopen(...), [](void*){...});
  • 错误:在删除器中访问已被释放的成员变量

5. 扩展知识

  • std::shared_ptr 删除器不改变类型,通过控制块存储
  • 自定义删除器可用于实现:
    • 资源池(延迟释放)
    • 日志跟踪
    • 特殊清理逻辑
  • C++17 支持数组特化:std::unique_ptr<T[]>