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 copyingAutoPtr4(AutoPtr4&& ptr4) noexcept // move constructor: ptr(ptr4)
{ptr4.ptr = nullptr;
}AutoPtr4& operator=(const AutoPtr4& ptr4) = delete; // disable copy assignmentAutoPtr4& 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
参考 ./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 constructorshared_ptr(const shared_ptr &other) : obj_(other.obj_), ref_cnt_(other.ref_cnt_){this->ref_cnt_->add_shared_cnt();}// copy assignment operatorshared_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 constructorshared_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 operatorshared_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();
}