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();
}