NIUCLOUD是一款SaaS管理后台框架多应用插件+云编译。上千名开发者、服务商正在积极拥抱开发者生态。欢迎开发者们免费入驻。一起助力发展! 广告
## 智能指针实现原理 智能指针本质上并不神秘,其实就是 RAII 资源管理功能而已,下面代码就是比较典型的 RAII 资源管理类: ~~~ class shape { public: virtual ~shape() {} }; class circle : public shape { public: ~circle() { puts("~circle"); } } ~~~ ~~~ class shape_wrapper { public: explicit shape_wrapper(shape* ptr = nullptr) : ptr_(ptr) {} ~shape_wrapper() { delete ptr_; } shape* get() const { return ptr_; } private: shape* ptr_; }; ~~~ 这个类可以完成智能指针最基本的功能,对超出作用域的对象进行释放。但该类有以下缺点: 1. 这个类只适用于 shape 类; 2. 该类对象的行为不够像指针; 3. 拷贝该类对象会引发程序异常。 ## auto_ptr 指针实现 ~~~ template <typename T> class Auto_ptr { public: explicit Auto_ptr(T* ptr = nullptr) : ptr_(ptr) {} ~Auto_ptr() { delete ptr_; } T* get() const { return ptr_; } /* * 能用 * 运算符解引用 * 能用 -> 运算符指向对象成员 * 能像指针一样用在布尔表达式里 */ T& operator*() const { return *ptr_; } T* operator->() const { return ptr_; } operator bool() const { return ptr_; } Auto_ptr(Auto_ptr& other) { ptr_ = other.release(); } Auto_ptr& operator=(Auto_ptr& rhs) { Auto_ptr(rhs).swap(*this); return *this; } T* release() { T* ptr = ptr_; ptr_ = nullptr; return ptr; } void swap(Auto_ptr& rhs) { using std::swap; swap(ptr_, rhs.ptr_); } private: T* ptr_; } ~~~ ~~~ Auto_ptr<shape> ptr1(new circle); Auto_ptr<shape> ptr2{ptr1}; // OK Auto_ptr<shape> ptr3; ptr3 = ptr1; // OK ptr3 = std::move(ptr1); // 编译出错 Auto_ptr<shape> ptr4{std::move(ptr3)}; // 编译出错 ~~~ ## unique\_ptr 实现 ~~~ template <typename T> class Unique_ptr { public: explicit Unique_ptr(T* ptr = nullptr) : ptr_(ptr) {} ~Unique_ptr() { delete ptr_; } T* get() const { return ptr_; } /* * 能用 * 运算符解引用 * 能用 -> 运算符指向对象成员 * 能像指针一样用在布尔表达式里 */ T& operator*() const { return *ptr_; } T* operator->() const { return ptr_; } operator bool() const { return ptr_; } Unique_ptr(Unique_ptr&& other) { ptr_ = other.release(); } Unique_ptr& operator=(Unique_ptr rhs) { rhs.swap(*this); return *this; } T* release() { T* ptr = ptr_; ptr_ = nullptr; return ptr; } void swap(Unique_ptr& rhs) { using std::swap; swap(ptr_, rhs.ptr_); } private: T* ptr_; ~~~ ~~~ Unique_ptr<shape> ptr1(new circle); Unique_ptr<shape> ptr2{ptr1}; // 编译出错 Unique_ptr<shape> ptr3; ptr3 = ptr1; // 编译出错 ptr3 = std::move(ptr1); // OK Unique_ptr<shape> ptr4{std::move(ptr3)}; // OK ~~~ ## shared_ptr实现 ~~~ class shared_count { public: shared_count() : count_(1) {} long add_count() { return ++count_; } long reduce_count() { return --count_; } long get_count() const { return count_; } private: long count_; }; template <typename T> class Shared_ptr { public: /* * 构造函数跟之前的主要不同点是会构造一个 shared_count 出来。 * 析构函数在看到 ptr_ 非空时(此时根据代码逻辑,shared_count * 也必然非空),需要对引用数减一,并在引用数降到零时彻底删除 * 对象和共享计数。原理就是这样,不复杂。 */ explicit Shared_ptr(T* ptr = nullptr) : ptr_(ptr) { if (ptr) { shared_count_ = new shared_count(); } } ~Shared_ptr() { if (ptr_ && !shared_count_->reduce_count()) { delete ptr_; delete shared_count_; } } T* get() const { return ptr_; } /* * 能用 * 运算符解引用 * 能用 -> 运算符指向对象成员 * 能像指针一样用在布尔表达式里 */ T& operator*() const { return *ptr_; } T* operator->() const { return ptr_; } operator bool() const { return ptr_; } /* * 除复制指针之外,对于拷贝构造的情况,我们需要在指针非空时 * 把引用数加一,并复制共享计数的指针。对于移动构造的情况, * 我们不需要调整引用数,直接把 other.ptr_ 置为空,认为 * other 不再指向该共享对象即可。 */ Shared_ptr(Shared_ptr& other) { ptr_ = other.ptr_; if (ptr_) { other.shared_count_->add_count(); shared_count_ = other.shared_count_; } } template <typename U> Shared_ptr(Shared_ptr<U>& other) { ptr_ = other.ptr_; if (ptr_) { other.shared_count_->add_count(); shared_count_ = other.shared_count_; } } template <typename U> Shared_ptr(Shared_ptr<U>&& other) { ptr_ = other.ptr_; if (ptr_) { shared_count_ = other.shared_count_; other.shared_count_ = nullptr; } } Shared_ptr& operator=(Shared_ptr rhs) { rhs.swap(*this); return *this; } void swap(Shared_ptr& rhs) { using std::swap; swap(ptr_, rhs.ptr_); swap(shared_count_, rhs.shared_count_); } long use_count() const { return ptr_ ? shared_count_->get_count() : 0; } private: T* ptr_; shared_count* shared_count_; } ~~~ ~~~ Shared_ptr<shape> ptr1(new circle); printf("use count of ptr1 is %ld\n", ptr1.use_count()); Shared_ptr<shape> ptr2; printf("use count of ptr2 is %ld\n", ptr2.use_count()); ptr2 = ptr1; printf("use count of ptr2 is %ld\n", ptr2.use_count()); if (ptr1) { puts("ptr1 not empty"); } ~~~ 结果如下: use count of ptr1 is 1 use count of ptr2 is 0 use count of ptr2 is 2 ptr1 not empty ~circle ## 线程安全的智能指针 用原子对象,我们可以轻而易举地把上面的的 shared\_count 变成线程安全的。 ~~~ class shared_count { public: shared_count() : count_(1) {} long add_count() { count_.fetch_add(1, std::memory_order_relaxed); return get_count(); } long reduce_count() { count_.fetch_sub(1, std::memory_order_relaxed); return get_count(); } long get_count() const { return count_.load(std::memory_order_release); } private: std::atomic_long count_; }; ~~~