手撕代码:实现自己的unique_ptr

目录

1.引言

2.设计自定义的unique_ptr类

3.实现UniquePtr类

3.1.移动构造函数和移动赋值运算符

3.2.reset函数

3.3.release函数

3.4.解引用操作符和箭头操作符

3.5.析构函数

4.使用自定义的UniquePtr类

5.UniquePtr的进一步优化和扩展

5.1.支持自定义删除器

5.2.支持数组类型的UniquePtr

5.3.支持类型别名

6.总结


1.引言

        在C++11及其后续版本中,std::unique_ptr 是一种智能指针,它负责自动管理动态分配的内存资源,确保在 unique_ptr 生命周期结束时自动删除所指向的对象,从而防止内存泄漏。本文将指导你从零开始实现一个简单的 unique_ptr 类,以深入理解其内部机制。

        unique_ptr 是一种独占所有权的智能指针,即同一时间只能有一个 unique_ptr 指向某个对象。当 unique_ptr 被销毁时(例如超出作用域或被重置),它所指向的对象也会被自动删除。这种特性使得 unique_ptr 非常适用于管理在堆上动态分配的单个对象。

2.设计自定义的unique_ptr类

template<typename T>
class UniquePtr {
public:
    // 构造函数
    explicit UniquePtr(T* ptr = nullptr) : ptr_(ptr) {}

    // 禁止拷贝构造和拷贝赋值
    UniquePtr(const UniquePtr&) = delete;
    UniquePtr& operator=(const UniquePtr&) = delete;

    // 允许移动构造和移动赋值
    UniquePtr(UniquePtr&& other) noexcept;
    UniquePtr& operator=(UniquePtr&& other) noexcept;

    // 重置指针
    void reset(T* ptr = nullptr);

    // 获取原始指针
    T* get() const { return ptr_; }

    // 释放指针
    void release();

    // 解引用操作符
    T& operator*() const;
    T* operator->() const;

    // 检查指针是否为空
    explicit operator bool() const { return ptr_ != nullptr; }

    // 析构函数
    ~UniquePtr();

private:
    T* ptr_;
};

3.实现UniquePtr类

接下来,我们将根据上面的设计逐一实现 UniquePtr 类的成员函数。

3.1.移动构造函数和移动赋值运算符

由于 UniquePtr 独占资源,因此我们需要禁用拷贝构造和拷贝赋值,而只允许移动构造和移动赋值。

template<typename T>
UniquePtr<T>::UniquePtr(UniquePtr&& other) noexcept : ptr_(other.ptr_) {
    other.ptr_ = nullptr; // 将源对象的指针设为nullptr,以确保资源的独占性
}

template<typename T>
UniquePtr<T>& UniquePtr<T>::operator=(UniquePtr&& other) noexcept {
    if (this != &other) { // 防止自赋值
        delete ptr_; // 删除当前对象所指向的资源
        ptr_ = other.ptr_; // 接管源对象的资源
        other.ptr_ = nullptr; // 将源对象的指针设为nullptr
    }
    return *this;
}

3.2.reset函数

reset 函数用于重置 UniquePtr 所指向的对象。如果当前已经持有一个对象,则先删除它,然后接管新传入的对象。

template<typename T>
void UniquePtr<T>::reset(T* ptr) {
    delete ptr_; // 删除当前对象所指向的资源
    ptr_ = ptr; // 接管新资源
}

3.3.release函数

release 函数用于释放 UniquePtr 对资源的所有权,返回原始指针,并将内部指针设为 nullptr。这样做可以手动管理资源,但通常不推荐这样做,因为它破坏了 UniquePtr 的自动管理特性。

template<typename T>
void UniquePtr<T>::release() {
    T* temp = ptr_; // 保存原始指针
    ptr_ = nullptr; // 将内部指针设为nullptr
    return temp; // 返回原始指针
}

3.4.解引用操作符和箭头操作符

这两个操作符使得我们可以像使用原始指针一样使用 UniquePtr

template<typename T>
T& UniquePtr<T>::operator*() const {
    return *ptr_; // 返回指向对象的引用
}

template<typename T>
T* UniquePtr<T>::operator->() const {
    return ptr_; // 返回原始指针
}

3.5.析构函数

析构函数负责在 UniquePtr 生命周期结束时自动删除所指向的对象。

template<typename T>
UniquePtr<T>::~UniquePtr() {
    delete ptr_; // 删除所指向的对象
}

4.使用自定义的UniquePtr类

现在我们可以使用自定义的 UniquePtr 类来管理动态分配的内存资源了。以下是一个简单的示例:

#include <iostream>
#include "UniquePtr.h" // 假设UniquePtr类定义在UniquePtr.h中

struct MyClass {
    MyClass(int value) : value_(value) {}
    ~MyClass() { std::cout << "Destroying MyClass with value " << value_ << std::endl; }
    int value_;
};

int main() {
    UniquePtr<MyClass> ptr1(new MyClass(10)); // 使用UniquePtr管理MyClass对象
    std::cout << "ptr1 points to " << ptr1->value_ << std::endl; // 使用箭头操作符访问成员变量
    (*ptr1).value_ = 20; // 使用解引用操作符修改成员变量值,这里仅为示例,通常更推荐使用箭头操作符来访问成员变量和成员函数。
    std::cout << "ptr1 points to " << ptr1->value_ << std::endl; // 输出修改后的值以验证修改成功。
    return 0; // 当main函数返回时,ptr1将被销毁,并自动删除所指向的MyClass对象。
} // 在此处输出“Destroying MyClass with value 20”以验证对象已被正确删除。

5.UniquePtr的进一步优化和扩展

5.1.支持自定义删除器

标准的std::unique_ptr允许用户提供一个自定义的删除器,这是一个可调用对象,用于在unique_ptr析构时删除所管理的对象。我们可以在自定义的UniquePtr中也加入这一特性。

template<typename T, typename Deleter = std::default_delete<T>>
class UniquePtr {
    // ... 其他成员保持不变 ...

private:
    Deleter deleter_;
    T* ptr_;
};

// 在构造函数中初始化删除器
template<typename T, typename Deleter>
UniquePtr<T, Deleter>::UniquePtr(T* ptr, Deleter deleter) : deleter_(deleter), ptr_(ptr) {}

// 在析构函数中使用删除器
template<typename T, typename Deleter>
UniquePtr<T, Deleter>::~UniquePtr() {
    deleter_(ptr_);
}

5.2.支持数组类型的UniquePtr

std::unique_ptr有一个模板特化版本,用于管理动态分配的数组。我们的自定义UniquePtr也可以添加对数组的支持。

// 特化版本用于支持数组
template<typename T>
class UniquePtr<T[]> {
public:
    explicit UniquePtr(T* ptr = nullptr) : ptr_(ptr) {}
    // ... 其他必要的成员函数,类似于UniquePtr<T>,但要使用delete[]来释放内存 ...
private:
    T* ptr_;
};

5.3.支持类型别名

为了方便使用,可以提供类型别名,类似于std::unique_ptr

template<typename T, typename Deleter = std::default_delete<T>>
using UniquePtrPtr = UniquePtr<T, Deleter>;

template<typename T>
using UniquePtrArray = UniquePtr<T[]>;

6.总结

        通过实现自定义的UniquePtr,我们不仅学习了智能指针的内部机制,还掌握了如何管理动态分配的内存资源,以及如何设计可重用和可扩展的C++代码。当然,实际生产中的智能指针实现会更加复杂,需要考虑更多的边界情况和性能优化。

        现在,我们的UniquePtr类已经具备了基本的智能指针功能,能够自动管理内存,并且支持移动语义和自定义删除器。这个实现虽然简单,但足以展示智能指针的核心思想和工作原理。

        在实际项目中,建议使用标准库提供的std::unique_ptr,因为它已经过高度优化和测试,可以确保在各种情况下的正确性和性能。然而,通过自己实现一个简单的版本,我们可以更深入地理解其背后的原理和设计考虑。

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值