CPP 智能指针

CPP 智能指针

  • Created: 2024-06-30T20:43+08:00
  • Published: 2024-11-16T23:17+08:00
  • Categories: C-CPP

智能指针的作用

智能指针最初使的作用就是离开作用域调用析构函数。
因为 malloc 出来的东西只能通过指针持有,栈上的对象在离开作用域后会自动调用析构函数,但是裸指针会不调用持有对象的析构函数。

智能指针的知识要点有:

  1. unique_ptr 要禁用 copy constructor, copy assignment operator
  2. shared_ptr 需要实现 big five,在 move constructor 和 move assignment operator 中要将右值重置
  3. 通过 weak_ptr 防止循环引用,原理是引用计数块中及统计 shared_count,也统计 weak_count,
    shared_count=0 时候释放资源,当 weak_count=0 时候释放引用计数块
  4. std::make_shared 实现有 std::forward,要会写
  5. std::make_shared 会把引用计数块和资源块放到同一块内存区域中以加快访问速度
  6. 要小心 shared_ptr{T* obj},可能导致两个 shared_ptr 管理同一份资源,会 double free

unique_ptr

从中可以看到,unique_ptr 禁用了拷贝构造和赋值运算符,仅仅实现了移动构造和移动赋值构造,这也就使得它是独占式的。

C++内存管理——unique_ptr - 知乎
C++ 智能指针详解(一)——unique_ptr - 知乎

AutoPtr4(const AutoPtr4& ptr4) = delete; // disable copying

AutoPtr4(AutoPtr4&& ptr4) noexcept // move constructor
    : ptr(ptr4)
{
    ptr4.ptr = nullptr;
}

AutoPtr4& operator=(const AutoPtr4& ptr4) = delete; // disable copy assignment

AutoPtr4& operator=(AutoPtr4&& ptr4) noexcept // move assignment
{
    if(this == &ptr4)
    {
        return *this;
    }

    delete this->ptr;
    this->ptr = ptr4.ptr;
    ptr4.ptr = nullptr;
    return *this;
}

shared_ptr

C++ 智能指针详解(二)——shared_ptr 与 weak_ptr - 知乎

shared-ptr-memory

shared_ptr 的引用计数通过 _Rep 指向的控制块控制,相关的操作在基类 _Ref_count_base 中已经实现,主要就是:
当 _Uses 为 0 时,调用 _Destroy() 释放原生对象内存;
当 _Weaks 为 0 时,调用 _Delete_this() 释放控制块自己的内存
(注意:只有当弱引用计数为 0 时,控制块的内存才会被所释放,如果有 weak_ptr 弱引用指针存在,控制块内存不会释放,这一点后面介绍),其余还有引用计数的增加和减少等函数。

  1. shared_ptr 内存结构 / 共享指针实现原理 / 引用计数开辟的空间是一个什么结构
  2. 如何破除循环引用以及原理 / 为什么不会增加引用计数 / 弱指针如何实现的:内存模型完全一致,只有类型和 api 的区别
  3. 不要用裸指针构造 shared_ptr / T 继承 enable_share_from_this 解决指针构造 shared_ptr
  4. 引用计数如何实现线程安全:使用了 atomic,参考:
    1. atomic 实现原理 - 知乎
    2. atomic 的底层实现 - 王的博客 - 博客园
    3. 锁 bus,exchange 原子操作,LR/SC,CAS

禁用的函数合运算符有哪些,他们的运算符重载具体如何实现的。

拷贝构造函数和赋值运算符分别在什么场景下使用。让你实现对应的重载具体代码要包含哪些内容

手写简单的 shared_ptr

参考 ./shared_ptr.cpp

#include <atomic>
#include <iostream>
#include <utility>
using std::atomic;

struct RefCnt
{
    atomic<size_t> shared_cnt{0};
    atomic<size_t> weak_cnt{0};

    void add_shared_cnt() { shared_cnt.fetch_add(1); }
    void sub_shared_cnt() { shared_cnt.fetch_sub(1); }
};

template <typename T>
struct shared_ptr
{
    T *obj_{nullptr};
    RefCnt *ref_cnt_{nullptr};

    shared_ptr(T *obj) : obj_(obj)
    {
        this->ref_cnt_ = new RefCnt();
        this->ref_cnt_->add_shared_cnt();
    }

    shared_ptr(T *obj, RefCnt *ref_cnt) : obj_(obj), ref_cnt_(ref_cnt) {}

    void try_release()
    {
        if (this->ref_cnt_->shared_cnt == 0)
        {
            delete this->obj_;
            delete this->ref_cnt_;
        }
    }

    ~shared_ptr()
    {
        std::cout << "~shared_ptr()" << std::endl;
        if (this->ref_cnt_)
        {
            this->ref_cnt_->sub_shared_cnt();
            try_release();
        }
    }

    T *operator->() const
    {
        return this->obj_;
    }

    // copy constructor
    shared_ptr(const shared_ptr &other) : obj_(other.obj_), ref_cnt_(other.ref_cnt_)
    {
        this->ref_cnt_->add_shared_cnt();
    }

    // copy assignment operator
    shared_ptr &operator=(const shared_ptr &other)
    {
        this->obj_ = other.obj_;
        this->ref_cnt_ = other.ref_cnt_;
        this->ref_cnt_->add_shared_cnt();
        return *this;
    }
    // move constructor
    shared_ptr(shared_ptr &&other)
    {
        std::cout << "shared_ptr(shared_ptr &&other)" << std::endl;
        this->obj_ = other.obj_;
        this->ref_cnt_ = other.ref_cnt_;
        other.obj_ = nullptr;
        other.ref_cnt_ = nullptr;
    }

    // move assign operator
    shared_ptr &operator=(shared_ptr &&other)
    {
        this->obj_ = other.obj_;
        this->ref_cnt_ = other.ref_cnt_;
        other.obj_ = nullptr;
        other.ref_cnt_ = nullptr;
        return *this;
    }
};

template <typename T, typename... Args>
shared_ptr<T> make_shared(Args &&...args)
{

    auto obj = new T(std::forward<Args>(args)...);
    auto ref_cnt = new RefCnt();
    ref_cnt->add_shared_cnt();
    return shared_ptr(obj, ref_cnt);
}
struct Point
{
    float x_{0}, y_{0};
    Point(float x, float y) : x_(x), y_(y) {}
    ~Point()
    {
        std::cout << "Point::~Point(), x = " << x_ << ", y = " << y_ << std::endl;
    }

    void print_self() const
    {
        std::cout << "Point: (" << this->x_ << ", " << this->y_ << ")" << std::endl;
    }
};

int main()
{

    auto p = make_shared<Point>(1, 2);
    p->print_self();
    auto p_copy(p);
    p_copy->print_self();

    auto p_copy_assignment = p;
    p_copy_assignment->print_self();

    auto p_move(std::move(p));
    p_move->print_self();

    auto p_move_assignment = std::move(p_copy);
    p_move_assignment->print_self();
}
posted @ 2024-11-16 23:20  dutrmp19  阅读(6)  评论(0编辑  收藏  举报