**如何在C++中实现自定义智能指针的拷贝与移动语义?**

在现代 C++ 开发中,智能指针(std::unique_ptrstd::shared_ptr)已经成为了管理动态内存的标准工具。然而,某些特殊场景下我们可能需要自己实现一个自定义的智能指针,例如实现一个对某种资源(文件句柄、网络连接等)具有更细粒度控制的指针。下面我们从设计思路、关键成员函数实现以及拷贝/移动语义的细节来完整阐述如何实现一个简单但功能完整的自定义智能指针。


1. 设计目标与基本框架

1.1 目标

  • 资源管理:负责释放持有的资源,防止泄漏。
  • RAII:资源在对象生命周期内保持唯一拥有权。
  • 拷贝:不可拷贝(类似 unique_ptr),但可以实现自定义拷贝构造来支持浅拷贝或引用计数。
  • 移动:支持移动构造和移动赋值,以便对象可以在容器中移动而不拷贝资源。
  • 线程安全:仅在需要时才引入原子计数器。

1.2 基本类定义

template<typename T, typename Deleter = std::default_delete<T>>
class CustomUniquePtr {
public:
    // 构造
    explicit CustomUniquePtr(T* ptr = nullptr) noexcept;
    explicit CustomUniquePtr(std::unique_ptr<T, Deleter>&& other) noexcept; // 移动构造

    // 析构
    ~CustomUniquePtr();

    // 禁止拷贝
    CustomUniquePtr(const CustomUniquePtr&) = delete;
    CustomUniquePtr& operator=(const CustomUniquePtr&) = delete;

    // 移动
    CustomUniquePtr(CustomUniquePtr&& other) noexcept;
    CustomUniquePtr& operator=(CustomUniquePtr&& other) noexcept;

    // 访问
    T* get() const noexcept;
    T& operator*() const noexcept;
    T* operator->() const noexcept;

    // 释放资源
    void reset(T* ptr = nullptr) noexcept;
    T* release() noexcept;

private:
    T* ptr_;
    Deleter deleter_;
};

2. 成员函数实现

2.1 构造与析构

template<typename T, typename Deleter>
CustomUniquePtr<T, Deleter>::CustomUniquePtr(T* ptr) noexcept
    : ptr_(ptr), deleter_() {}

template<typename T, typename Deleter>
CustomUniquePtr<T, Deleter>::~CustomUniquePtr() {
    if (ptr_) deleter_(ptr_);
}

2.2 移动构造与移动赋值

template<typename T, typename Deleter>
CustomUniquePtr<T, Deleter>::CustomUniquePtr(CustomUniquePtr&& other) noexcept
    : ptr_(other.ptr_), deleter_(std::move(other.deleter_)) {
    other.ptr_ = nullptr;
}

template<typename T, typename Deleter>
CustomUniquePtr<T, Deleter>&
CustomUniquePtr<T, Deleter>::operator=(CustomUniquePtr&& other) noexcept {
    if (this != &other) {
        reset();
        ptr_ = other.ptr_;
        deleter_ = std::move(other.deleter_);
        other.ptr_ = nullptr;
    }
    return *this;
}

2.3 资源管理函数

template<typename T, typename Deleter>
void CustomUniquePtr<T, Deleter>::reset(T* ptr) noexcept {
    if (ptr_ != ptr) {
        if (ptr_) deleter_(ptr_);
        ptr_ = ptr;
    }
}

template<typename T, typename Deleter>
T* CustomUniquePtr<T, Deleter>::release() noexcept {
    T* tmp = ptr_;
    ptr_ = nullptr;
    return tmp;
}

2.4 访问操作符

template<typename T, typename Deleter>
T* CustomUniquePtr<T, Deleter>::get() const noexcept { return ptr_; }

template<typename T, typename Deleter>
T& CustomUniquePtr<T, Deleter>::operator*() const noexcept { return *ptr_; }

template<typename T, typename Deleter>
T* CustomUniquePtr<T, Deleter>::operator->() const noexcept { return ptr_; }

3. 支持引用计数(可选)

如果想让 CustomUniquePtr 在拷贝时实现共享(类似 std::shared_ptr),可以把内部指针改为指向结构体:

struct ControlBlock {
    T* ptr;
    std::atomic <size_t> ref_count;
    Deleter deleter;
    ControlBlock(T* p, Deleter d)
        : ptr(p), ref_count(1), deleter(d) {}
};

template<typename T, typename Deleter>
class CustomSharedPtr {
public:
    explicit CustomSharedPtr(T* ptr = nullptr) : ctrl_(nullptr) {
        if (ptr) ctrl_ = new ControlBlock(ptr, Deleter{});
    }
    // 拷贝构造
    CustomSharedPtr(const CustomSharedPtr& other) noexcept
        : ctrl_(other.ctrl_) {
        if (ctrl_) ++ctrl_->ref_count;
    }
    // ...
private:
    ControlBlock* ctrl_;
};

此时要实现析构时递减计数,并在计数为 0 时释放资源。


4. 使用示例

int main() {
    CustomUniquePtr <int> p(new int(42));
    std::cout << *p << '\n'; // 输出 42

    // 移动
    CustomUniquePtr <int> q(std::move(p));
    if (!p.get()) std::cout << "p 为空\n";

    // reset
    q.reset(new int(100));
    std::cout << *q << '\n';

    // release
    int* raw = q.release();
    std::cout << *raw << '\n';
    delete raw; // 手动释放

    return 0;
}

5. 小结

  • RAII 原则:自定义智能指针通过构造/析构管理资源,天然满足 RAII。
  • 移动语义:实现移动构造和移动赋值,让对象在容器中移动成本低。
  • 不可拷贝:默认不可拷贝,保持资源唯一性;如需共享,可在内部引入引用计数。
  • 可扩展性:可以在 Deleter 模板参数中传入自定义删除器,例如文件句柄、网络套接字等。

通过上述实现,你可以快速构建符合自己需求的智能指针,并在 C++ 项目中享受 RAII 与现代语义带来的安全性与便利。

发表评论