CPP 智能指针
CPP 智能指针
- Created: 2024-06-30T20:43+08:00
- Published: 2024-11-16T23:17+08:00
- Categories: C-CPP
智能指针的作用
智能指针最初使的作用就是离开作用域调用析构函数。
因为 malloc 出来的东西只能通过指针持有,栈上的对象在离开作用域后会自动调用析构函数,但是裸指针会不调用持有对象的析构函数。
智能指针的知识要点有:
- unique_ptr 要禁用 copy constructor, copy assignment operator
- shared_ptr 需要实现 big five,在 move constructor 和 move assignment operator 中要将右值重置
- 通过 weak_ptr 防止循环引用,原理是引用计数块中及统计 shared_count,也统计 weak_count,
当shared_count=0
时候释放资源,当weak_count=0
时候释放引用计数块 std::make_shared
实现有std::forward
,要会写std::make_shared
会把引用计数块和资源块放到同一块内存区域中以加快访问速度- 要小心
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 的引用计数通过 _Rep 指向的控制块控制,相关的操作在基类 _Ref_count_base 中已经实现,主要就是:
当 _Uses 为 0 时,调用 _Destroy() 释放原生对象内存;
当 _Weaks 为 0 时,调用 _Delete_this() 释放控制块自己的内存
(注意:只有当弱引用计数为 0 时,控制块的内存才会被所释放,如果有 weak_ptr 弱引用指针存在,控制块内存不会释放,这一点后面介绍),其余还有引用计数的增加和减少等函数。
- shared_ptr 内存结构 / 共享指针实现原理 / 引用计数开辟的空间是一个什么结构
- 如何破除循环引用以及原理 / 为什么不会增加引用计数 / 弱指针如何实现的:内存模型完全一致,只有类型和 api 的区别
- 不要用裸指针构造 shared_ptr / T 继承 enable_share_from_this 解决指针构造 shared_ptr
- 引用计数如何实现线程安全:使用了
atomic
,参考:- atomic 实现原理 - 知乎
- atomic 的底层实现 - 王的博客 - 博客园
- 锁 bus,exchange 原子操作,LR/SC,CAS
禁用的函数合运算符有哪些,他们的运算符重载具体如何实现的。
拷贝构造函数和赋值运算符分别在什么场景下使用。让你实现对应的重载具体代码要包含哪些内容
手写简单的 shared_ptr
#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(); }
如果您有任何关于文章的建议,欢迎评论或在 GitHub 提 PR
作者:dutrmp19
本文为作者原创,转载请在 文章开头 注明出处:https://www.cnblogs.com/dutrmp19/p/18550109
遵循 CC 4.0 BY-SA 版权协议
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本